diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 153a7de..bfd6d8e 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -14,6 +14,7 @@
  * @see batch_get()
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Timer;
 use Drupal\Core\Batch\Percentage;
 use Drupal\Core\Page\DefaultHtmlPageRenderer;
@@ -44,7 +45,10 @@ function _batch_page(Request $request) {
       return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
     }
   }
-
+  // Restore safe strings from previous batches.
+  if (!empty($batch['safe_strings'])) {
+    SafeMarkup::setMultiple($batch['safe_strings']);
+  }
   // Register database update for the end of processing.
   drupal_register_shutdown_function('_batch_shutdown');
 
@@ -480,6 +484,8 @@ function _batch_finished() {
  */
 function _batch_shutdown() {
   if ($batch = batch_get()) {
+    // Update safe strings.
+    $batch['safe_strings'] = SafeMarkup::getAll();
     \Drupal::service('batch.storage')->update($batch);
   }
 }
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 7f8b179..aa7ae3f 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -6,6 +6,7 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Environment;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\DrupalKernel;
@@ -900,8 +901,27 @@ function watchdog($type, $message, array $variables = array(), $severity = WATCH
  *
  * @return array|null
  *   A multidimensional array with keys corresponding to the set message types.
- *   The indexed array values of each contain the set messages for that type.
- *   Or, if there are no messages set, the function returns NULL.
+ *   The indexed array values of each contain the set messages for that type,
+ *   and each message is an associative array with the following format:
+ *   - safe: Boolean indicating whether the message string has been marked as
+ *     safe. Non-safe strings will be escaped automatically.
+ *   - message: The message string.
+ *   So, the following is an example of the full return array structure:
+ *   @code
+ *     array(
+ *       'status' => array(
+ *         array(
+ *           'safe' => TRUE,
+ *           'message' => 'A <em>safe</em> markup string.',
+ *         ),
+ *         array(
+ *           'safe' => FALSE,
+ *           'message' => "$arbitrary_user_input to escape.",
+ *         ),
+ *       ),
+ *     );
+ *   @endcode
+ *   If there are no messages set, the function returns NULL.
  *
  * @see drupal_get_messages()
  * @see theme_status_messages()
@@ -913,7 +933,10 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
     }
 
     if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
-      $_SESSION['messages'][$type][] = $message;
+      $_SESSION['messages'][$type][] = array(
+        'safe' => SafeMarkup::isSafe($message),
+        'message' => $message,
+      );
     }
 
     // Mark this page as being uncacheable.
@@ -940,17 +963,23 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
  *   intact. Defaults to TRUE.
  *
  * @return array
- *   A multidimensional array with keys corresponding to the set message types.
- *   The indexed array values of each contain the set messages for that type.
  *   The messages returned are limited to the type specified in the $type
- *   parameter. If there are no messages of the specified type, an empty array
- *   is returned.
+ *   parameter, if any. If there are no messages of the specified type, an
+ *   empty array is returned. See drupal_set_message() for the array structure.
  *
  * @see drupal_set_message()
  * @see theme_status_messages()
  */
 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
   if ($messages = drupal_set_message()) {
+    foreach ($messages as $message_type => $message_typed_messages) {
+      foreach ($message_typed_messages as $key => $message) {
+        if ($message['safe']) {
+          $message['message'] = SafeMarkup::set($message['message']);
+        }
+        $messages[$message_type][$key] = $message['message'];
+      }
+    }
     if ($type) {
       if ($clear_queue) {
         unset($_SESSION['messages'][$type]);
diff --git a/core/includes/common.inc b/core/includes/common.inc
index c324243..c84eb90 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -29,6 +29,7 @@
 use Drupal\Core\EventSubscriber\HtmlViewSubscriber;
 use Drupal\Core\Routing\GeneratorNotInitializedException;
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AnonymousUserSession;
 
@@ -456,7 +457,8 @@ function format_rss_item($title, $link, $description, $args = array()) {
  *   An array where each item represents an element and is either a:
  *   - (key => value) pair (<key>value</key>)
  *   - Associative array with fields:
- *     - 'key': element name
+ *     - 'key': The element name. Element names are not sanitized, so do not
+ *       pass user input.
  *     - 'value': element contents
  *     - 'attributes': associative array of element attributes
  *
@@ -485,7 +487,9 @@ function format_xml_elements($array) {
       $output .= ' <' . $key . '>' . (is_array($value) ? format_xml_elements($value) : String::checkPlain($value)) . "</$key>\n";
     }
   }
-  return $output;
+  // @todo This is marking the output string as safe HTML, but we have only
+  //   sanitized the attributes and tag values, not the tag names.
+  return SafeMarkup::set($output);
 }
 
 /**
@@ -886,8 +890,7 @@ function l($text, $path, array $options = array()) {
 
   // Sanitize the link text if necessary.
   $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
-
-  return '<a href="' . $url . '"' . $attributes . '>' . $text . '</a>';
+  return SafeMarkup::set('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
 }
 
 /**
@@ -2759,12 +2762,17 @@ function drupal_pre_render_conditional_comments($elements) {
 /**
  * Pre-render callback: Renders a generic HTML tag with attributes into #markup.
  *
+ * Note: It is the caller's responsibility to sanitize any input parameters.
+ * This callback does not perform any sanitization.
+ *
  * @param array $element
  *   An associative array containing:
  *   - #tag: The tag name to output. Typical tags added to the HTML HEAD:
  *     - meta: To provide meta information, such as a page refresh.
  *     - link: To refer to stylesheets and other contextual information.
  *     - script: To load JavaScript.
+ *     The value of #tag is not escaped or sanitized, so do not pass in user
+ *     input.
  *   - #attributes: (optional) An array of HTML attributes to apply to the
  *     tag.
  *   - #value: (optional) A string containing tag content, such as inline
@@ -2777,7 +2785,11 @@ function drupal_pre_render_conditional_comments($elements) {
 function drupal_pre_render_html_tag($element) {
   $attributes = isset($element['#attributes']) ? new Attribute($element['#attributes']) : '';
   if (!isset($element['#value'])) {
-    $markup = '<' . $element['#tag'] . $attributes . " />\n";
+    // This function is intended for internal use, so we assume that no unsafe
+    // values are passed in #tag. The attributes are already safe because
+    // Attribute output is already automatically sanitized.
+    // @todo Escape this properly instead? https://www.drupal.org/node/2296101
+    $markup = SafeMarkup::set('<' . $element['#tag'] . $attributes . " />\n");
   }
   else {
     $markup = '<' . $element['#tag'] . $attributes . '>';
@@ -2789,6 +2801,9 @@ function drupal_pre_render_html_tag($element) {
       $markup .= $element['#value_suffix'];
     }
     $markup .= '</' . $element['#tag'] . ">\n";
+    // @todo We cannot actually guarantee this markup is safe. Consider a fix
+    //   in: https://www.drupal.org/node/2296101
+    $markup = SafeMarkup::set($markup);
   }
   if (!empty($element['#noscript'])) {
     $element['#markup'] = '<noscript>' . $markup . '</noscript>';
@@ -3228,6 +3243,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
       if (!$is_recursive_call) {
         _drupal_render_process_post_render_cache($elements);
       }
+      $elements['#markup'] = SafeMarkup::set($elements['#markup']);
       return $elements['#markup'];
     }
   }
@@ -3280,6 +3296,11 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     $elements['#children'] = '';
   }
 
+  // @todo Simplify after https://drupal.org/node/2273925
+  if (isset($elements['#markup'])) {
+    $elements['#markup'] = SafeMarkup::set($elements['#markup']);
+  }
+
   // Assume that if #theme is set it represents an implemented hook.
   $theme_is_implemented = isset($elements['#theme']);
 
@@ -3303,6 +3324,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     foreach ($children as $key) {
       $elements['#children'] .= drupal_render($elements[$key], TRUE);
     }
+    $elements['#children'] = SafeMarkup::set($elements['#children']);
   }
 
   // If #theme is not implemented and the element has raw #markup as a
@@ -3313,7 +3335,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
   // required. Eventually #theme_wrappers will expect both #markup and
   // #children to be a single string as #children.
   if (!$theme_is_implemented && isset($elements['#markup'])) {
-    $elements['#children'] = $elements['#markup'] . $elements['#children'];
+    $elements['#children'] = SafeMarkup::set($elements['#markup'] . $elements['#children']);
   }
 
   // Let the theme functions in #theme_wrappers add markup around the rendered
@@ -3403,6 +3425,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
   }
 
   $elements['#printed'] = TRUE;
+  $elements['#markup'] = SafeMarkup::set($elements['#markup']);
   return $elements['#markup'];
 }
 
@@ -3424,13 +3447,13 @@ function drupal_render_children(&$element, $children_keys = NULL) {
   if ($children_keys === NULL) {
     $children_keys = Element::children($element);
   }
-  $output = '';
+  $markups = array();
   foreach ($children_keys as $key) {
     if (!empty($element[$key])) {
-      $output .= drupal_render($element[$key]);
+      $markups[] = drupal_render($element[$key]);
     }
   }
-  return $output;
+  return SafeMarkup::implode('', $markups);
 }
 
 /**
diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index ead20d4..2251d61 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -8,6 +8,7 @@
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Page\DefaultHtmlPageRenderer;
 use Drupal\Core\Utility\Error;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Symfony\Component\HttpFoundation\Response;
 
@@ -212,7 +213,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
         // Generate a backtrace containing only scalar argument values.
         $message .= '<pre class="backtrace">' . format_backtrace($backtrace) . '</pre>';
       }
-      drupal_set_message($message, $class, TRUE);
+      drupal_set_message(SafeMarkup::set($message), $class, TRUE);
     }
 
     if ($fatal) {
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 1610f80..793bccf 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\Number;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
@@ -979,7 +980,7 @@ function form_select_options($element, $choices = NULL) {
       $options .= '<option value="' . String::checkPlain($key) . '"' . $selected . '>' . String::checkPlain($choice) . '</option>';
     }
   }
-  return $options;
+  return SafeMarkup::set($options);
 }
 
 /**
@@ -1647,7 +1648,7 @@ function theme_tableselect($variables) {
           // A header can span over multiple cells and in this case the cells
           // are passed in an array. The order of this array determines the
           // order in which they are added.
-          if (!isset($element['#options'][$key][$fieldname]['data']) && is_array($element['#options'][$key][$fieldname])) {
+          if (is_array($element['#options'][$key][$fieldname]) && !isset($element['#options'][$key][$fieldname]['data'])) {
             foreach ($element['#options'][$key][$fieldname] as $cell) {
               $row['data'][] = $cell;
             }
@@ -1993,13 +1994,13 @@ function form_process_machine_name($element, &$form_state) {
   $element['#machine_name']['suffix'] = '#' . $suffix_id;
 
   if ($element['#machine_name']['standalone']) {
-    $element['#suffix'] .= ' <small id="' . $suffix_id . '">&nbsp;</small>';
+    $element['#suffix'] = SafeMarkup::set($element['#suffix'] . ' <small id="' . $suffix_id . '">&nbsp;</small>');
   }
   else {
     // Append a field suffix to the source form element, which will contain
     // the live preview of the machine name.
     $source += array('#field_suffix' => '');
-    $source['#field_suffix'] .= ' <small id="' . $suffix_id . '">&nbsp;</small>';
+    $source['#field_suffix'] = SafeMarkup::set($source['#field_suffix'] . ' <small id="' . $suffix_id . '">&nbsp;</small>');
 
     $parents = array_merge($element['#machine_name']['source'], array('#field_suffix'));
     NestedArray::setValue($form_state['complete_form'], $parents, $source['#field_suffix']);
@@ -3171,6 +3172,8 @@ function _form_set_attributes(&$element, $class = array()) {
  *   - css: Array of paths to CSS files to be used on the progress page.
  *   - url_options: options passed to url() when constructing redirect URLs for
  *     the batch.
+ *   - safe_strings: Internal use only. Used to store and retrieve strings
+ *     marked as safe between requests.
  */
 function batch_set($batch_definition) {
   if ($batch_definition) {
@@ -3294,6 +3297,9 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = NU
         $request->query->remove('destination');
       }
 
+      // Store safe strings.
+      $batch['safe_strings'] = SafeMarkup::getAll();
+
       // Store the batch.
       \Drupal::service('batch.storage')->create($batch);
 
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 158f8a9..eee34fd 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -15,6 +15,7 @@
 use Drupal\Core\Page\DefaultHtmlPageRenderer;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\StringTranslation\Translator\FileTranslation;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
@@ -1703,10 +1704,10 @@ function install_finished(&$install_state) {
   // @todo Temporary hack to satisfy PIFR.
   // @see https://drupal.org/node/1317548
   $pifr_assertion = '<span style="display: none;">Drupal installation complete</span>';
-
-  drupal_set_message(t('Congratulations, you installed @drupal!', array(
+  $success_message = t('Congratulations, you installed @drupal!', array(
     '@drupal' => drupal_install_profile_distribution_name(),
-  )) . $pifr_assertion);
+  ));
+  drupal_set_message(SafeMarkup::set($success_message . $pifr_assertion));
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index ddd9d70..6f5cacc 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -8,6 +8,7 @@
  * customized by user themes.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
@@ -20,6 +21,7 @@
 use Drupal\Core\Page\MetaElement;
 use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\RenderWrapper;
 use Drupal\Core\Theme\ThemeSettings;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Render\Element;
@@ -584,7 +586,7 @@ function _theme($hook, $variables = array()) {
   $output = '';
   if (isset($info['function'])) {
     if (function_exists($info['function'])) {
-      $output = $info['function']($variables);
+      $output = SafeMarkup::set($info['function']($variables));
     }
   }
   else {
@@ -1986,7 +1988,7 @@ function template_preprocess_html(&$variables) {
   // Construct page title.
   if ($page->hasTitle()) {
     $head_title = array(
-      'title' => trim(strip_tags($page->getTitle())),
+      'title' => SafeMarkup::set(trim(strip_tags($page->getTitle()))),
       'name' => String::checkPlain($site_config->get('name')),
     );
   }
@@ -2006,7 +2008,7 @@ function template_preprocess_html(&$variables) {
   }
 
   $variables['head_title_array'] = $head_title;
-  $variables['head_title'] = implode(' | ', $head_title);
+  $variables['head_title'] = SafeMarkup::implode(' | ', $head_title);
 
   // @todo Remove drupal_*_html_head() and refactor accordingly.
   $html_heads = drupal_get_html_head(FALSE);
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 8a19a49..9bced2e 100644
--- a/core/includes/theme.maintenance.inc
+++ b/core/includes/theme.maintenance.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Site\Settings;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Sets up the theming system for maintenance page.
@@ -110,6 +111,8 @@ function _drupal_maintenance_theme() {
  * @param $variables
  *   An associative array containing:
  *   - items: An associative array of maintenance tasks.
+ *     It's the caller's responsibility to ensure this array's items contain no
+ *     dangerous HTML such as SCRIPT tags.
  *   - active: The key for the currently active maintenance task.
  *
  * @ingroup themeable
@@ -187,6 +190,8 @@ function theme_authorize_report($variables) {
  * @param $variables
  *   An associative array containing:
  *   - message: The log message.
+ *     It's the caller's responsibility to ensure this string contains no
+ *     dangerous HTML such as SCRIPT tags.
  *   - success: A boolean indicating failure or success.
  *
  * @ingroup themeable
diff --git a/core/lib/Drupal/Component/Diff/Engine/HWLDFWordAccumulator.php b/core/lib/Drupal/Component/Diff/Engine/HWLDFWordAccumulator.php
index 384593b..8c4ebea 100644
--- a/core/lib/Drupal/Component/Diff/Engine/HWLDFWordAccumulator.php
+++ b/core/lib/Drupal/Component/Diff/Engine/HWLDFWordAccumulator.php
@@ -4,6 +4,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  *  Additions by Axel Boldt follow, partly taken from diff.php, phpwiki-1.3.3
@@ -46,7 +47,9 @@ protected function _flushGroup($new_tag) {
   protected function _flushLine($new_tag) {
     $this->_flushGroup($new_tag);
     if ($this->line != '') {
-      array_push($this->lines, $this->line);
+      // @todo This is probably not the right place to do this. To be
+      //   addressed in https://drupal.org/node/2280963
+      array_push($this->lines, SafeMarkup::set($this->line));
     }
     else {
       // make empty lines visible by inserting an NBSP
diff --git a/core/lib/Drupal/Component/Utility/SafeMarkup.php b/core/lib/Drupal/Component/Utility/SafeMarkup.php
new file mode 100644
index 0000000..bfa6eed
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/SafeMarkup.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Utility\SafeMarkup.
+ */
+
+namespace Drupal\Component\Utility;
+
+/**
+ * Marks strings that are already escaped for XSS prevention.
+ *
+ * @todo Add detailed documentation about how to use SafeMarkup and how it is
+ *   handled during rendering and in the theme layer.
+ */
+class SafeMarkup {
+
+  protected static $safeStrings = array();
+
+  /**
+   * Adds a string to a list of strings marked as secure.
+   *
+   * @param string $string
+   *   The content to be marked as secure.
+   * @param string $strategy
+   *   The escaping strategy used for this string. Two values are supported
+   *   by default:
+   *   - 'html': (default) The string is safe for use in HTML code.
+   *   - 'all': The string is safe for all use cases.
+   *   See the
+   *   @link http://twig.sensiolabs.org/doc/filters/escape.html Twig escape documentation @endlink
+   *   for more information on escaping strategies in Twig.
+   *
+   * @return string
+   *   The input string that was marked as safe.
+   */
+  public static function set($string, $strategy = 'html') {
+    $string = (string) $string;
+    static::$safeStrings[$string][$strategy] = TRUE;
+    return $string;
+  }
+
+  /**
+   * Checks if a string is safe to output.
+   *
+   * @param string $string
+   *   The content to be checked.
+   * @param string $strategy
+   *   The escaping strategy. See SafeMarkup::set(). Defaults to 'html'.
+   *
+   * @return bool
+   *   TRUE if the string has been marked secure, FALSE otherwise.
+   */
+  public static function isSafe($string, $strategy = 'html') {
+    return isset(static::$safeStrings[(string) $string][$strategy]) ||
+      isset(static::$safeStrings[(string) $string]['all']);
+  }
+
+  /**
+   * Safely implodes strings by escaping any that are not known to be safe.
+   *
+   * @param string $delimiter
+   *   The delimiter to use when imploding. Note that the delimiter is assumed
+   *   to be safe, so do not pass unsanitized input!
+   * @param array $array
+   *   An array of the strings to implode.
+   * @param string $strategy
+   *   The escaping strategy. See SafeMarkup::set(). Defaults to 'html'.
+   *
+   * @return \Drupal\Component\Utility\SafeMarkup|string
+   *   The imploded string, which is now also marked as safe.
+   *
+   * @todo Should $delimeter also be processed with checkPlain() if it's not a
+   *   known-safe string?
+   */
+  public static function implode($delimiter, array $array, $strategy = 'html') {
+    foreach ($array as $key => $string) {
+      if (!(isset(static::$safeStrings[(string) $string][$strategy]) ||
+        isset(static::$safeStrings[(string) $string]['all']))) {
+        $array[$key] = String::checkPlain($string);
+      }
+    }
+    return SafeMarkup::set(implode($delimiter, $array), $strategy);
+  }
+
+  /**
+   * Adds previously retrieved known safe strings to the safe string list.
+   *
+   * This is useful for the batch and form APIs, where it is important to
+   * preserve the safe markup state across page requests. The strings will be
+   * added to any safe strings already marked for the current request.
+   *
+   * @param array $safe_strings
+   *   A list of safe strings as previously retrieved by SafeMarkup::getAll().
+   *
+   * @throws \UnexpectedValueException
+   */
+  public static function setMultiple(array $safe_strings) {
+    foreach ($safe_strings as $string => $strategies) {
+      foreach ($strategies as $strategy => $value) {
+        $string = (string) $string;
+        if ($value === TRUE) {
+          static::$safeStrings[$string][$strategy] = TRUE;
+        }
+        else {
+          // Danger - something is very wrong.
+          throw new \UnexpectedValueException('Only the value TRUE is accepted for safe strings');
+        }
+      }
+    }
+  }
+
+  /**
+  * Retrieves all strings currently marked as safe.
+  *
+  * This is useful for the batch and form APIs, where it is important to
+  * preserve the safe markup state across page requests.
+  *
+  * @return array
+  *   Returns all strings currently marked safe.
+  */
+  public static function getAll() {
+    return static::$safeStrings;
+  }
+}
diff --git a/core/lib/Drupal/Component/Utility/String.php b/core/lib/Drupal/Component/Utility/String.php
index 9796a5f..f2707c5 100644
--- a/core/lib/Drupal/Component/Utility/String.php
+++ b/core/lib/Drupal/Component/Utility/String.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Component\Utility;
 
+use Drupal\Component\Utility\SafeMarkup;
+
 /**
  * Provides helpers to operate on strings.
  *
@@ -17,7 +19,8 @@ class String {
   /**
    * Encodes special characters in a plain-text string for display as HTML.
    *
-   * Also validates strings as UTF-8.
+   * Also validates strings as UTF-8. All processed strings are also
+   * automatically flagged as safe markup strings for rendering.
    *
    * @param string $text
    *   The text to be checked or processed.
@@ -29,9 +32,10 @@ class String {
    * @ingroup sanitization
    *
    * @see drupal_validate_utf8()
+   * @see \Drupal\Component\Utility\SafeMarkup
    */
   public static function checkPlain($text) {
-    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
+    return SafeMarkup::set(htmlspecialchars($text, ENT_QUOTES, 'UTF-8'));
   }
 
   /**
@@ -65,7 +69,8 @@ public static function decodeEntities($text) {
    * addition to formatting it.
    *
    * @param $string
-   *   A string containing placeholders.
+   *   A string containing placeholders. The string itself is not escaped, any
+   *   unsafe content must be in $args and inserted via placeholders.
    * @param $args
    *   An associative array of replacements to make. Occurrences in $string of
    *   any key in $args are replaced with the corresponding value, after
@@ -108,7 +113,7 @@ public static function format($string, array $args = array()) {
           // Pass-through.
       }
     }
-    return strtr($string, $args);
+    return SafeMarkup::set(strtr($string, $args));
   }
 
   /**
@@ -123,7 +128,8 @@ public static function format($string, array $args = array()) {
    *   The formatted text (html).
    */
   public static function placeholder($text) {
-    return '<em class="placeholder">' . static::checkPlain($text) . '</em>';
+    return SafeMarkup::set('<em class="placeholder">' . static::checkPlain($text) . '</em>');
   }
 
+
 }
diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index dc49913..9189c08 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Component\Utility;
 
+use Drupal\Component\Utility\SafeMarkup;
+
 /**
  * Provides helper to filter for cross-site scripting.
  *
@@ -41,12 +43,14 @@ 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 four things:
+   * This code does five 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
@@ -63,6 +67,7 @@ class Xss {
    *   valid UTF-8.
    *
    * @see \Drupal\Component\Utility\Unicode::validateUtf8()
+   * @see \Drupal\Component\Utility\SafeMarkup
    *
    * @ingroup sanitization
    */
@@ -90,7 +95,7 @@ public static function filter($string, $html_tags = array('a', 'em', 'strong', '
     $splitter = function ($matches) use ($html_tags, $mode) {
       return static::split($matches[1], $html_tags, $mode);
     };
-    return preg_replace_callback('%
+    return SafeMarkup::set(preg_replace_callback('%
       (
       <(?=[^a-zA-Z!/])  # a lone <
       |                 # or
@@ -99,7 +104,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));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Controller/ExceptionController.php b/core/lib/Drupal/Core/Controller/ExceptionController.php
index 4067074..13d2f73 100644
--- a/core/lib/Drupal/Core/Controller/ExceptionController.php
+++ b/core/lib/Drupal/Core/Controller/ExceptionController.php
@@ -18,6 +18,7 @@
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Drupal\Component\Utility\String;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\Debug\Exception\FlattenException;
 use Drupal\Core\ContentNegotiation;
 use Drupal\Core\Utility\Error;
@@ -316,7 +317,7 @@ public function on500Html(FlattenException $exception, Request $request) {
         // Generate a backtrace containing only scalar argument values.
         $message .= '<pre class="backtrace">' . Error::formatFlattenedBacktrace($backtrace) . '</pre>';
       }
-      drupal_set_message($message, $class, TRUE);
+      drupal_set_message(SafeMarkup::set($message), $class, TRUE);
     }
 
     $content = $this->t('The website has encountered an error. Please try again later.');
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index c72cafc..9c77f4d 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -95,9 +95,7 @@ public static function registerTwig(ContainerBuilder $container) {
         // When in the installer, twig_cache must be FALSE until we know the
         // files folder is writable.
         'cache' => drupal_installation_attempted() ? FALSE : Settings::get('twig_cache', TRUE),
-        // @todo Remove in followup issue
-        // @see http://drupal.org/node/1712444.
-        'autoescape' => FALSE,
+        'autoescape' => TRUE,
         'debug' => Settings::get('twig_debug', FALSE),
         'auto_reload' => Settings::get('twig_auto_reload', NULL),
       ))
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index d3bedc1..211216a 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\DependencyInjection\ClassResolverInterface;
@@ -363,6 +364,11 @@ public function getCache($form_build_id, &$form_state) {
               require_once DRUPAL_ROOT . '/' . $file;
             }
           }
+          // Retrieve the list of previously known safe strings and store it
+          // for this request.
+          $form_state['build_info'] += array('safe_strings' => array());
+          SafeMarkup::setMultiple($form_state['build_info']['safe_strings']);
+          unset($form_state['build_info']['safe_strings']);
         }
         return $form;
       }
@@ -385,6 +391,10 @@ public function setCache($form_build_id, $form, $form_state) {
     }
 
     // Cache form state.
+
+    // Store the known list of safe strings for form re-use.
+    $form_state['build_info']['safe_strings'] = SafeMarkup::getAll();
+
     if ($data = array_diff_key($form_state, array_flip($this->getUncacheableKeys()))) {
       $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire);
     }
diff --git a/core/lib/Drupal/Core/Page/HeadElement.php b/core/lib/Drupal/Core/Page/HeadElement.php
index 49c1f2d..b104c54 100644
--- a/core/lib/Drupal/Core/Page/HeadElement.php
+++ b/core/lib/Drupal/Core/Page/HeadElement.php
@@ -8,9 +8,12 @@
 namespace Drupal\Core\Page;
 
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * This class represents an HTML element that appears in the HEAD tag.
+ *
+ * @see template_preprocess_html()
  */
 class HeadElement {
 
@@ -52,7 +55,7 @@ public function __toString() {
     if ($this->noScript) {
       $string = "<noscript>$string</noscript>";
     }
-    return $string;
+    return SafeMarkup::set($string);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Page/HtmlPage.php b/core/lib/Drupal/Core/Page/HtmlPage.php
index eb4c63d..2f3d5f1 100644
--- a/core/lib/Drupal/Core/Page/HtmlPage.php
+++ b/core/lib/Drupal/Core/Page/HtmlPage.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Page;
 
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Data object for an HTML page.
@@ -84,7 +85,10 @@ public function getHtmlAttributes() {
    *   A string of meta and link tags.
    */
   public function getHead() {
-    return implode("\n", $this->getMetaElements()) . implode("\n", $this->getLinkElements());
+    // Each MetaElement or LinkElement is a subclass of
+    // \Drupal\Core\Page\HeadElement and generates safe output when __toString()
+    // is called on it. Thus, the whole concatenation is also safe.
+    return SafeMarkup::set(implode("\n", $this->getMetaElements()) . implode("\n", $this->getLinkElements()));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
index 23deb8c..68a77e5 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Defines a chained translation implementation combining multiple translators.
@@ -140,7 +141,7 @@ public function translate($string, array $args = array(), array $options = array
     $string = $translation === FALSE ? $string : $translation;
 
     if (empty($args)) {
-      return $string;
+      return SafeMarkup::set($string);
     }
     else {
       return String::format($string, $args);
@@ -160,7 +161,7 @@ public function formatPlural($count, $singular, $plural, array $args = array(),
     $translated_array = explode(LOCALE_PLURAL_DELIMITER, $translated_strings);
 
     if ($count == 1) {
-      return $translated_array[0];
+      return SafeMarkup::set($translated_array[0]);
     }
 
     // Get the plural index through the gettext formula.
@@ -168,20 +169,21 @@ public function formatPlural($count, $singular, $plural, array $args = array(),
     $index = (function_exists('locale_get_plural')) ? locale_get_plural($count, isset($options['langcode']) ? $options['langcode'] : NULL) : -1;
     if ($index == 0) {
       // Singular form.
-      return $translated_array[0];
+      $return = $translated_array[0];
     }
     else {
       if (isset($translated_array[$index])) {
         // N-th plural form.
-        return $translated_array[$index];
+        $return = $translated_array[$index];
       }
       else {
         // If the index cannot be computed or there's no translation, use
         // the second plural form as a fallback (which allows for most flexiblity
         // with the replaceable @count value).
-        return $translated_array[1];
+        $return = $translated_array[1];
       }
     }
+    return SafeMarkup::set($return);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
index ead5d05..9b2d270 100644
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -6,8 +6,7 @@
  */
 
 namespace Drupal\Core\Template;
-
-use Drupal\Component\Utility\String;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * A class that can be used for collecting then rendering HTML attributtes.
@@ -117,7 +116,7 @@ public function __toString() {
         $return .= ' ' . $rendered;
       }
     }
-    return $return;
+    return SafeMarkup::set($return);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index 4cb31ca..01e37a4 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -38,15 +38,22 @@ public function getFunctions() {
   public function getFilters() {
     return array(
       // Translation filters.
-      new \Twig_SimpleFilter('t', 't'),
-      new \Twig_SimpleFilter('trans', 't'),
+      new \Twig_SimpleFilter('t', 't', array('is_safe' => array('html'))),
+      new \Twig_SimpleFilter('trans', 't', array('is_safe' => array('html'))),
       // The "raw" filter is not detectable when parsing "trans" tags. To detect
       // which prefix must be used for translation (@, !, %), we must clone the
       // "raw" filter and give it identifiable names. These filters should only
       // be used in "trans" tags.
       // @see TwigNodeTrans::compileString()
-      new \Twig_SimpleFilter('passthrough', 'twig_raw_filter'),
-      new \Twig_SimpleFilter('placeholder', 'twig_raw_filter'),
+      new \Twig_SimpleFilter('passthrough', 'twig_raw_filter', array('is_safe' => array('html'))),
+      new \Twig_SimpleFilter('placeholder', 'twig_raw_filter', array('is_safe' => array('html'))),
+
+      // Replace twig's escape filter with our own.
+      new \Twig_SimpleFilter('drupal_escape', 'twig_drupal_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
+
+      // Implements safe joining.
+      // @todo Make that the default for |join?
+      new \Twig_SimpleFilter('safe_join', 'twig_drupal_join_filter', array('is_safe' => array('html'))),
 
       // Array filters.
       new \Twig_SimpleFilter('without', 'twig_without'),
diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
index 5bfc6b3..29dea5a 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
@@ -133,7 +133,13 @@ protected function compileString(\Twig_NodeInterface $body) {
           while ($n instanceof \Twig_Node_Expression_Filter) {
             $n = $n->getNode('node');
           }
-          $args = $n->getNode('arguments')->getNode(0);
+
+          $args = $n;
+
+          // Support twig_render_var function in chain.
+          if ($args instanceof \Twig_Node_Expression_Function) {
+            $args = $n->getNode('arguments')->getNode(0);
+          }
 
           // Detect if a token implements one of the filters reserved for
           // modifying the prefix of a token. The default prefix used for
diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
index a24ee50..4915c16 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
@@ -32,6 +32,11 @@ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
     // We use this to inject a call to render_var -> twig_render_var()
     // before anything is printed.
     if ($node instanceof \Twig_Node_Print) {
+      if (!empty($this->skipRenderVarFunction)) {
+        // No need to add the callback, we have escape active already.
+        unset($this->skipRenderVarFunction);
+        return $node;
+      }
       $class = get_class($node);
       $line = $node->getLine();
       return new $class(
@@ -39,6 +44,17 @@ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
         $line
       );
     }
+    // Change the 'escape' filter to our own 'drupal_escape' filter.
+    else if ($node instanceof \Twig_Node_Expression_Filter) {
+      $name = $node->getNode('filter')->getAttribute('value');
+      if ('escape' == $name || 'e' == $name) {
+        // Use our own escape filter that is SafeMarkup aware.
+        $node->getNode('filter')->setAttribute('value', 'drupal_escape');
+
+        // Store that we have a filter active already that knows how to deal with render arrays.
+        $this->skipRenderVarFunction = TRUE;
+      }
+    }
 
     return $node;
   }
@@ -47,7 +63,8 @@ function leaveNode(\Twig_NodeInterface $node, \Twig_Environment $env) {
    * {@inheritdoc}
    */
   function getPriority() {
-    return 1;
+    // Just above the Optimizer, which is the normal last one.
+    return 256;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Utility/Error.php b/core/lib/Drupal/Core/Utility/Error.php
index e3b084f..2bfee62 100644
--- a/core/lib/Drupal/Core/Utility/Error.php
+++ b/core/lib/Drupal/Core/Utility/Error.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Drupal error utility class.
@@ -101,7 +102,7 @@ public static function renderExceptionSafe(\Exception $exception) {
     // no longer function correctly (as opposed to a user-triggered error), so
     // we assume that it is safe to include a verbose backtrace.
     $output .= '<pre>' . static::formatBacktrace($backtrace) . '</pre>';
-    return $output;
+    return SafeMarkup::set($output);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index b547d95..712d447 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -11,8 +11,9 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Path\AliasManagerInterface;
-use Drupal\Core\Template\Attribute;
 use Drupal\Core\Routing\UrlGeneratorInterface;
+use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Url;
 
 /**
@@ -122,8 +123,7 @@ public function generateFromUrl($text, Url $url) {
 
     // Sanitize the link text if necessary.
     $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
-
-    return '<a href="' . $url . '"' . $attributes . '>' . $text . '</a>';
+    return SafeMarkup::set('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
   }
 
   /**
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index 4379582..ab1ea12 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Render\Element;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Returns HTML for a book administration form.
@@ -36,9 +37,9 @@ function theme_book_admin_table($variables) {
 
     $indentation = array('#theme' => 'indentation', '#size' => $form[$key]['depth']['#value'] - 2);
     $data = array(
-      drupal_render($indentation) . drupal_render($form[$key]['title']),
+      SafeMarkup::set(drupal_render($indentation) . drupal_render($form[$key]['title'])),
       drupal_render($form[$key]['weight']),
-      drupal_render($form[$key]['pid']) . drupal_render($form[$key]['nid']),
+      SafeMarkup::set(drupal_render($form[$key]['pid']) . drupal_render($form[$key]['nid'])),
     );
     $links = array();
     $links['view'] = array(
diff --git a/core/modules/book/src/BookExport.php b/core/modules/book/src/BookExport.php
index f28f855..c447ad4 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\Component\Utility\SafeMarkup;
 use Drupal\node\NodeInterface;
 
 /**
@@ -105,18 +106,16 @@ protected function exportTraverse(array $tree, $callable) {
     // If there is no valid callable, use the default callback.
     $callable = !empty($callable) ? $callable : array($this, 'bookNodeExport');
 
-    $output = '';
+    $build = array();
     foreach ($tree as $data) {
       // Note- access checking is already performed when building the tree.
       if ($node = $this->nodeStorage->load($data['link']['nid'])) {
         $children = $data['below'] ? $this->exportTraverse($data['below'], $callable) : '';
-
-        $callable_output = call_user_func($callable, $node, $children);
-        $output .= drupal_render($callable_output);
+        $build[] = call_user_func($callable, $node, $children);
       }
     }
 
-    return $output;
+    return drupal_render($build);
   }
 
   /**
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 8899d5a..0dca287 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -7,6 +7,7 @@
 use Drupal\Core\Asset\CssOptimizer;
 use Drupal\Component\Utility\Bytes;
 use Drupal\Component\Utility\Environment;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Routing\RouteMatchInterface;
 
@@ -274,7 +275,7 @@ function template_preprocess_color_scheme_form(&$variables) {
 
   // Attempt to load preview HTML if the theme provides it.
   $preview_html_path = DRUPAL_ROOT . '/' . (isset($info['preview_html']) ? drupal_get_path('theme', $theme) . '/' . $info['preview_html'] : drupal_get_path('module', 'color') . '/preview.html');
-  $variables['html_preview'] = file_get_contents($preview_html_path);
+  $variables['html_preview'] = SafeMarkup::set(file_get_contents($preview_html_path));
 }
 
 /**
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 8930d76..cd79f89 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -886,7 +886,7 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
     }
   }
 
-  $return = '';
+  $build = array();
 
   if ($index_comments) {
     foreach (\Drupal::service('comment.manager')->getFields('node') as $field_name => $info) {
@@ -900,12 +900,11 @@ function comment_node_update_index(EntityInterface $node, $langcode) {
       if ($node->get($field_name)->status && $cids = comment_get_thread($node, $field_name, $mode, $comments_per_page)) {
         $comments = entity_load_multiple('comment', $cids);
         comment_prepare_thread($comments);
-        $build = comment_view_multiple($comments);
-        $return .= drupal_render($build);
+        $build[] = comment_view_multiple($comments);
       }
     }
   }
-  return $return;
+  return drupal_render($build);
 }
 
 /**
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 3874c4e..746c2c9 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -5,6 +5,7 @@
  */
 
 use Drupal\Component\Utility\Html;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Config\ConfigImporter;
 use Drupal\Core\Entity\EntityTypeInterface;
@@ -260,7 +261,7 @@ function field_entity_bundle_delete($entity_type, $bundle) {
  *   UTF-8.
  */
 function field_filter_xss($string) {
-  return Html::normalize(Xss::filter($string, _field_filter_xss_allowed_tags()));
+  return SafeMarkup::set(Html::normalize(Xss::filter($string, _field_filter_xss_allowed_tags())));
 }
 
 /**
diff --git a/core/modules/field/src/Plugin/views/field/Field.php b/core/modules/field/src/Plugin/views/field/Field.php
index c678554..6a168d2 100644
--- a/core/modules/field/src/Plugin/views/field/Field.php
+++ b/core/modules/field/src/Plugin/views/field/Field.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\field\Plugin\views\field;
 
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\EntityInterface;
@@ -687,11 +689,11 @@ public function submitGroupByForm(&$form, &$form_state) {
   protected function renderItems($items) {
     if (!empty($items)) {
       if (!$this->options['group_rows']) {
-        return implode('', $items);
+        return SafeMarkup::implode('', $items);
       }
 
       if ($this->options['multi_type'] == 'separator') {
-        return implode(Xss::filterAdmin($this->options['separator']), $items);
+        return SafeMarkup::implode(Xss::filterAdmin($this->options['separator']), $items);
       }
       else {
         $item_list = array(
diff --git a/core/modules/field_ui/src/DisplayOverviewBase.php b/core/modules/field_ui/src/DisplayOverviewBase.php
index 8a98e93..3d7b95a 100644
--- a/core/modules/field_ui/src/DisplayOverviewBase.php
+++ b/core/modules/field_ui/src/DisplayOverviewBase.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Field\PluginSettingsInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -404,7 +405,7 @@ protected function buildFieldRow(FieldDefinitionInterface $field_definition, Ent
 
         if (!empty($summary)) {
           $field_row['settings_summary'] = array(
-            '#markup' => '<div class="field-plugin-summary">' . implode('<br />', $summary) . '</div>',
+            '#markup' => '<div class="field-plugin-summary">' . SafeMarkup::implode('<br />', $summary) . '</div>',
             '#cell_attributes' => array('class' => array('field-plugin-summary-cell')),
           );
         }
diff --git a/core/modules/field_ui/src/FieldConfigListBuilder.php b/core/modules/field_ui/src/FieldConfigListBuilder.php
index 0da1bf9..16456d3 100644
--- a/core/modules/field_ui/src/FieldConfigListBuilder.php
+++ b/core/modules/field_ui/src/FieldConfigListBuilder.php
@@ -7,11 +7,13 @@
 
 namespace Drupal\field_ui;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -117,7 +119,7 @@ public function buildRow(EntityInterface $field) {
         $usage[] = $this->bundles[$field->entity_type][$bundle]['label'];
       }
     }
-    $row['data']['usage'] = implode(', ', $usage);
+    $row['data']['usage'] = SafeMarkup::implode(', ', $usage);
     return $row;
   }
 
diff --git a/core/modules/field_ui/src/Form/FieldInstanceEditForm.php b/core/modules/field_ui/src/Form/FieldInstanceEditForm.php
index 7ac1aff..963dba7 100644
--- a/core/modules/field_ui/src/Form/FieldInstanceEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldInstanceEditForm.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormBase;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\field\FieldInstanceConfigInterface;
 use Drupal\field_ui\FieldUI;
@@ -122,7 +123,7 @@ public function buildForm(array $form, array &$form_state, FieldInstanceConfigIn
       '#title' => $this->t('Help text'),
       '#default_value' => $this->instance->getDescription(),
       '#rows' => 5,
-      '#description' => $this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())) . '<br />' . $this->t('This field supports tokens.'),
+      '#description' => SafeMarkup::set($this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', array('@tags' => _field_filter_xss_display_allowed_tags())) . '<br />' . $this->t('This field supports tokens.')),
       '#weight' => -10,
     );
 
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index 68a0f00..f610340 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -9,6 +9,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Returns HTML for an individual file upload widget.
@@ -121,7 +122,7 @@ function template_preprocess_file_widget_multiple(&$variables) {
       $row[] = $display;
     }
     $row[] = $weight;
-    $row[] = $operations;
+    $row[] = SafeMarkup::set($operations);
     $rows[] = array(
       'data' => $row,
       'class' => isset($widget['#attributes']['class']) ? array_merge($widget['#attributes']['class'], array('draggable')) : array('draggable'),
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 7256399..ed745ba 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -5,6 +5,7 @@
  * Defines a "managed_file" Form API field and a "file" field for Field module.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Render\Element;
@@ -942,10 +943,10 @@ function file_save_upload($form_field_name, $validators = array(), $destination
           '#theme' => 'item_list',
           '#items' => $errors,
         );
-        $message .= drupal_render($item_list);
+        $message = SafeMarkup::implode('', array($message, drupal_render($item_list)));
       }
       else {
-        $message .= ' ' . array_pop($errors);
+        $message = SafeMarkup::implode('', array($message, ' ', array_pop($errors)));
       }
       drupal_set_message($message, 'error');
       $files[$i] = FALSE;
diff --git a/core/modules/file/templates/file-upload-help.html.twig b/core/modules/file/templates/file-upload-help.html.twig
index fe4d194..8fa6b3e 100644
--- a/core/modules/file/templates/file-upload-help.html.twig
+++ b/core/modules/file/templates/file-upload-help.html.twig
@@ -11,4 +11,4 @@
  * @ingroup themeable
  */
 #}
-{{ descriptions|join('<br />') }}
+{{ descriptions|safe_join('<br />') }}
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 126ba0d..c4d1b7e 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -15,6 +15,7 @@
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\filter\FilterFormatInterface;
 use Drupal\filter\Plugin\FilterInterface;
 
diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index bccaf02..796b6f7 100644
--- a/core/modules/filter/src/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
@@ -11,6 +11,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\filter\FilterProcessResult;
 use Drupal\filter\Plugin\FilterBase;
 
@@ -82,7 +83,7 @@ public function process($text, $langcode) {
         // caption.
         $filter_caption = array(
           '#theme' => 'filter_caption',
-          '#node' => $node->C14N(),
+          '#node' => SafeMarkup::set($node->C14N()),
           '#tag' => $node->tagName,
           '#caption' => $caption,
           '#align' => $align,
diff --git a/core/modules/filter/templates/filter-guidelines.html.twig b/core/modules/filter/templates/filter-guidelines.html.twig
index 88a3b47..ecf9b94 100644
--- a/core/modules/filter/templates/filter-guidelines.html.twig
+++ b/core/modules/filter/templates/filter-guidelines.html.twig
@@ -20,6 +20,6 @@
  */
 #}
 <div{{ attributes }}>
-  <h4 class="label">{{ format.name|escape }}</h4>
+  <h4 class="label">{{ format.name }}</h4>
   {{ tips }}
 </div>
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 84c5dc0..704bd6d 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -4,8 +4,11 @@
  * @file
  * Administration pages for image settings.
  */
+
 use Drupal\Component\Utility\String;
 use Drupal\Core\Render\Element;
+use Drupal\Component\Utility\SafeMarkup;
+
 /**
  * Returns HTML for a listing of the effects within a specific image style.
  *
@@ -30,7 +33,8 @@ function theme_image_style_effects($variables) {
     }
     else {
       // Add the row for adding a new image effect.
-      $row[] = '<div class="image-style-new">' . drupal_render($form['new']['new']) . drupal_render($form['new']['add']) . '</div>';
+      $cell = '<div class="image-style-new">' . drupal_render($form['new']['new']) . drupal_render($form['new']['add']) . '</div>';
+      $row[] = SafeMarkup::set($cell);
       $row[] = drupal_render($form['new']['weight']);
       $row[] = '';
     }
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index 8a07c4c..ff44bd2 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Render\Element;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\locale\SourceString;
 use Drupal\locale\TranslationString;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -292,7 +293,7 @@ function theme_locale_translate_edit_form_strings($variables) {
     }
     $source .= empty($string['context']) ? '' : '<br /><small>' . t('In Context') . ':&nbsp;' . $string['context']['#value'] . '</small>';
     $rows[] = array(
-      array('data' => $source),
+      array('data' => SafeMarkup::set($source)),
       array('data' => $string['translations']),
     );
   }
diff --git a/core/modules/locale/templates/locale-translation-update-info.html.twig b/core/modules/locale/templates/locale-translation-update-info.html.twig
index 809028a..15cb4ce 100644
--- a/core/modules/locale/templates/locale-translation-update-info.html.twig
+++ b/core/modules/locale/templates/locale-translation-update-info.html.twig
@@ -21,7 +21,7 @@
 <div class="inner" tabindex="0" role="button">
   <span class="update-description-prefix visually-hidden">Show description</span>
   {% if modules %}
-    {% set module_list = modules|join(', ') %}
+    {% set module_list = modules|safe_join(', ') %}
     <span class="text">{% trans %}Updates for: {{ module_list }}{% endtrans %}</span>
   {% elseif missing_updates_status %}
     <span class="text">{{ missing_updates_status }}</span>
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index d0d6d1c..dc5bd4f 100644
--- a/core/modules/node/node.install
+++ b/core/modules/node/node.install
@@ -7,6 +7,7 @@
 
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Language\Language;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Implements hook_requirements().
@@ -29,7 +30,9 @@ function node_requirements($phase) {
     $requirements['node_access'] = array(
       'title' => t('Node Access Permissions'),
       'value' => $value,
-      'description' => $description . ' ' . l(t('Rebuild permissions'), 'admin/reports/status/rebuild'),
+      // The result of t() is safe and so is the result of l(). Preserving
+      // safe object.
+      'description' => SafeMarkup::set($description . ' ' . l(t('Rebuild permissions'), 'admin/reports/status/rebuild')),
     );
   }
   return $requirements;
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index 9757596..3215943 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -10,6 +10,7 @@
  */
 
 use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Drupal\node\NodeInterface;
 
diff --git a/core/modules/node/src/NodeViewBuilder.php b/core/modules/node/src/NodeViewBuilder.php
index 606ef36..3c5e29d 100644
--- a/core/modules/node/src/NodeViewBuilder.php
+++ b/core/modules/node/src/NodeViewBuilder.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Render controller for nodes.
diff --git a/core/modules/node/src/Plugin/Search/NodeSearch.php b/core/modules/node/src/Plugin/Search/NodeSearch.php
index 3409383..0e8ccd5 100644
--- a/core/modules/node/src/Plugin/Search/NodeSearch.php
+++ b/core/modules/node/src/Plugin/Search/NodeSearch.php
@@ -18,6 +18,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Access\AccessibleInterface;
 use Drupal\Core\Database\Query\Condition;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\node\NodeInterface;
 use Drupal\search\Plugin\ConfigurableSearchPluginBase;
 use Drupal\search\Plugin\SearchIndexingInterface;
@@ -266,10 +267,12 @@ public function execute() {
       $node = $node_storage->load($item->sid)->getTranslation($item->langcode);
       $build = $node_render->view($node, 'search_result', $item->langcode);
       unset($build['#theme']);
-      $node->rendered = drupal_render($build);
 
       // Fetch comment count for snippet.
-      $node->rendered .= ' ' . $this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode));
+      $node->rendered = SafeMarkup::implode(' ', array(
+        drupal_render($build),
+        $this->moduleHandler->invoke('comment', 'node_update_index', array($node, $item->langcode)),
+      ));
 
       $extra = $this->moduleHandler->invokeAll('node_search_result', array($node, $item->langcode));
 
diff --git a/core/modules/node/src/Plugin/views/row/Rss.php b/core/modules/node/src/Plugin/views/row/Rss.php
index 9bb5574..a6466db 100644
--- a/core/modules/node/src/Plugin/views/row/Rss.php
+++ b/core/modules/node/src/Plugin/views/row/Rss.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\node\Plugin\views\row;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\views\Plugin\views\row\RowPluginBase;
 
@@ -162,7 +163,7 @@ public function render($row) {
     }
 
     $item = new \stdClass();
-    $item->description = $item_text;
+    $item->description = SafeMarkup::set($item_text);
     $item->title = $node->label();
     $item->link = $node->link;
     $item->elements = $node->rss_elements;
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index e55510b..86a750c 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -5,6 +5,7 @@
  * Enables semantically enriched output for Drupal sites in the form of RDFa.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Template\Attribute;
@@ -457,8 +458,8 @@ function rdf_preprocess_comment(&$variables) {
     $author_attributes = array('rel' => $author_mapping['properties']);
     // Wraps the author variable and the submitted variable which are both
     // available in comment.html.twig.
-    $variables['author'] = '<span ' . new Attribute($author_attributes) . '>' . $variables['author'] . '</span>';
-    $variables['submitted'] = '<span ' . new Attribute($author_attributes) . '>' . $variables['submitted'] . '</span>';
+    $variables['author'] = SafeMarkup::set('<span ' . new Attribute($author_attributes) . '>' . $variables['author'] . '</span>');
+    $variables['submitted'] = SafeMarkup::set('<span ' . new Attribute($author_attributes) . '>' . $variables['submitted'] . '</span>');
   }
   // Adds RDFa markup for the date of the comment.
   $created_mapping = $mapping->getPreparedFieldMapping('created');
@@ -474,8 +475,8 @@ function rdf_preprocess_comment(&$variables) {
     $created_metadata_markup = drupal_render($rdf_metadata);
     // Appends the markup to the created variable and the submitted variable
     // which are both available in comment.html.twig.
-    $variables['created'] .= $created_metadata_markup;
-    $variables['submitted'] .= $created_metadata_markup;
+    $variables['created'] = SafeMarkup::implode('', array($variables['created'], $created_metadata_markup));
+    $variables['submitted'] = SafeMarkup::set($variables['submitted'] . $created_metadata_markup);
   }
   $title_mapping = $mapping->getPreparedFieldMapping('subject');
   if (!empty($title_mapping)) {
diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module
index 36972c4..10cac05 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\breakpoint\Entity\Breakpoint;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Routing\RouteMatchInterface;
 use \Drupal\Core\Template\Attribute;
 
@@ -190,7 +191,6 @@ function theme_responsive_image($variables) {
   }
 
   $sources = array();
-  $output = array();
 
   // Fallback image, output as source with media query.
   $sources[] = array(
@@ -234,6 +234,7 @@ function theme_responsive_image($variables) {
   }
 
   if (!empty($sources)) {
+    $output = array();
     $output[] = '<picture>';
 
     // Add source tags to the output.
@@ -253,7 +254,7 @@ function theme_responsive_image($variables) {
     }
 
     $output[] = '</picture>';
-    return implode("\n", $output);
+    return SafeMarkup::implode("\n", $output);
   }
 }
 
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index b7c4ef1..4caa821 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -5,6 +5,7 @@
  * Enables site-wide keyword searching.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -721,7 +722,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 $text;
+  return SafeMarkup::set($text);
 }
 
 /**
diff --git a/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module b/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module
index 2561131..cc2526a 100644
--- a/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module
+++ b/core/modules/search/tests/modules/search_embedded_form/search_embedded_form.module
@@ -9,10 +9,12 @@
  * individual product (node) listed in the search results.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
+
 /**
  * Adds the test form to search results.
  */
 function search_embedded_form_preprocess_search_result(&$variables) {
   $form = \Drupal::formBuilder()->getForm('Drupal\search_embedded_form\Form\SearchEmbeddedForm');
-  $variables['snippet'] .= drupal_render($form);
+  $variables['snippet'] = SafeMarkup::implode('', array($variables['snippet'] , drupal_render($form)));
 }
diff --git a/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php b/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php
index 5caea64..32cfb77 100644
--- a/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php
+++ b/core/modules/search/tests/modules/search_extra_type/src/Plugin/Search/SearchExtraTypeSearch.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\search_extra_type\Plugin\Search;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\search\Plugin\ConfigurableSearchPluginBase;
 
 /**
@@ -58,7 +59,7 @@ public function execute() {
         'link' => url('node'),
         'type' => 'Dummy result type',
         'title' => 'Dummy title',
-        'snippet' => "Dummy search snippet to display. Keywords: {$this->keywords}\n\nConditions: " . print_r($this->searchParameters, TRUE),
+        'snippet' => SafeMarkup::set("Dummy search snippet to display. Keywords: {$this->keywords}\n\nConditions: " . print_r($this->searchParameters, TRUE)),
       ),
     );
   }
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index 299f305..e7106ff 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Form\FormBase;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 
@@ -155,7 +156,8 @@ public function buildForm(array $form, array &$form_state, $test_id = NULL) {
       $rows = array();
       foreach ($assertions as $assertion) {
         $row = array();
-        $row[] = $assertion->message;
+        // @todo Need to preserve safe markup, not create it.
+        $row[] = SafeMarkup::set($assertion->message);
         $row[] = $assertion->message_group;
         $row[] = drupal_basename($assertion->file);
         $row[] = $assertion->line;
diff --git a/core/modules/system/src/Form/DateFormatFormBase.php b/core/modules/system/src/Form/DateFormatFormBase.php
index 5ae6134..6a4e050 100644
--- a/core/modules/system/src/Form/DateFormatFormBase.php
+++ b/core/modules/system/src/Form/DateFormatFormBase.php
@@ -124,7 +124,7 @@ public function form(array $form, array &$form_state) {
       '#machine_name' => array(
         'exists' => array($this, 'exists'),
         'replace_pattern' =>'([^a-z0-9_]+)|(^custom$)',
-        'error' => 'The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".',
+        'error' => t('The machine-readable name must be unique, and can only contain lowercase letters, numbers, and underscores. Additionally, it can not be the reserved word "custom".'),
       ),
     );
 
diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php
index b58cab8..64d4787 100644
--- a/core/modules/system/src/Form/ModulesListForm.php
+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -19,6 +19,7 @@
 use Drupal\Core\Render\Element;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Access\AccessManager;
 
@@ -175,7 +176,7 @@ public function buildForm(array $form, array &$form_state) {
         '#open' => TRUE,
         '#theme' => 'system_modules_details',
         '#header' => array(
-          array('data' => '<span class="visually-hidden">' . $this->t('Installed') . '</span>', 'class' => array('checkbox')),
+          array('data' => SafeMarkup::set('<span class="visually-hidden">' . $this->t('Installed') . '</span>'), 'class' => array('checkbox')),
           array('data' => $this->t('Name'), 'class' => array('name')),
           array('data' => $this->t('Description'), 'class' => array('description', RESPONSIVE_PRIORITY_LOW)),
         ),
diff --git a/core/modules/system/src/Form/ModulesUninstallForm.php b/core/modules/system/src/Form/ModulesUninstallForm.php
index 7438e48..265e80f 100644
--- a/core/modules/system/src/Form/ModulesUninstallForm.php
+++ b/core/modules/system/src/Form/ModulesUninstallForm.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
diff --git a/core/modules/system/src/Tests/System/DateFormatsMachineNameTest.php b/core/modules/system/src/Tests/System/DateFormatsMachineNameTest.php
index 495ab87..34052dc 100644
--- a/core/modules/system/src/Tests/System/DateFormatsMachineNameTest.php
+++ b/core/modules/system/src/Tests/System/DateFormatsMachineNameTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\System;
 
+use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\simpletest\WebTestBase;
 
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 9ba1a1c..ce70f96 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -10,6 +10,7 @@
 use Drupal\Core\Extension\Extension;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Recursively check compatibility.
@@ -231,7 +232,7 @@ function theme_system_modules_details($variables) {
 
     // Add the module label and expand/collapse functionalty.
     $col2 = '<label id="module-' . $key . '" for="' . $module['enable']['#id'] . '" class="module-name table-filter-text-source">' . drupal_render($module['name']) . '</label>';
-    $row[] = array('class' => array('module'), 'data' => $col2);
+    $row[] = array('class' => array('module'), 'data' => SafeMarkup::set($col2));
 
     // Add the description, along with any modules it requires.
     $description = '';
@@ -259,9 +260,9 @@ function theme_system_modules_details($variables) {
     }
     $details = array(
       '#type' => 'details',
-      '#title' => '<span class="text"> ' . drupal_render($module['description']) . '</span>',
+      '#title' => SafeMarkup::set('<span class="text"> ' . drupal_render($module['description']) . '</span>'),
       '#attributes' => array('id' => $module['enable']['#id'] . '-description'),
-      '#description' => $description,
+      '#description' => SafeMarkup::set($description),
     );
     $col4 = drupal_render($details);
     $row[] = array('class' => array('description', 'expand'), 'data' => $col4);
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 6161819..9ada350 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -11,6 +11,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\StreamWrapper\PublicStream;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Implements hook_requirements().
@@ -57,7 +58,8 @@ function system_requirements($phase) {
   if (function_exists('phpinfo')) {
     $requirements['php'] = array(
       'title' => t('PHP'),
-      'value' => ($phase == 'runtime') ? $phpversion .' ('. l(t('more information'), 'admin/reports/status/php') .')' : $phpversion,
+      // $phpversion is safe and output of l() is safe, so this value is safe.
+      'value' => SafeMarkup::set(($phase == 'runtime') ? $phpversion . ' (' . l(t('more information'), 'admin/reports/status/php') . ')' : $phpversion),
     );
   }
   else {
@@ -319,7 +321,8 @@ function system_requirements($phase) {
       'title' => t('Cron maintenance tasks'),
       'severity' => $severity,
       'value' => $summary,
-      'description' => $description
+      // @todo Needs to preserve safe markup.
+      'description' => SafeMarkup::set($description),
     );
   }
   if ($phase != 'install') {
diff --git a/core/modules/system/templates/block--system-branding-block.html.twig b/core/modules/system/templates/block--system-branding-block.html.twig
index 2a12c7a..4cf0f1a 100644
--- a/core/modules/system/templates/block--system-branding-block.html.twig
+++ b/core/modules/system/templates/block--system-branding-block.html.twig
@@ -23,7 +23,7 @@
   {% endif %}
   {% if site_name %}
     <div class="site-name">
-      <a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home">{{ site_name|e }}</a>
+      <a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home">{{ site_name }}</a>
     </div>
   {% endif %}
   {% if site_slogan %}
diff --git a/core/modules/system/templates/datetime.html.twig b/core/modules/system/templates/datetime.html.twig
index 25ef788..183b834 100644
--- a/core/modules/system/templates/datetime.html.twig
+++ b/core/modules/system/templates/datetime.html.twig
@@ -25,5 +25,4 @@
  * @see http://www.w3.org/TR/html5-author/the-time-element.html#attr-time-datetime
  */
 #}
-{# @todo Revisit once http://drupal.org/node/1825952 is resolved. #}
-<time{{ attributes }}>{{ html ? text|raw : text|escape }}</time>
+<time{{ attributes }}>{{ html ? text|raw : text }}</time>
diff --git a/core/modules/system/templates/system-themes-page.html.twig b/core/modules/system/templates/system-themes-page.html.twig
index fa0e748..e40545b 100644
--- a/core/modules/system/templates/system-themes-page.html.twig
+++ b/core/modules/system/templates/system-themes-page.html.twig
@@ -39,7 +39,7 @@
             <h3>
               {{- theme.name }} {{ theme.version -}}
               {% if theme.notes %}
-                ({{ theme.notes|join(', ') }})
+                ({{ theme.notes|safe_join(', ') }})
               {%- endif -%}
             </h3>
             <div class="theme-description">{{ theme.description }}</div>
diff --git a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
index ca1ea6f..9a4c205 100644
--- a/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
+++ b/core/modules/system/tests/modules/batch_test/batch_test.callbacks.inc
@@ -4,6 +4,7 @@
  * @file
  * Batch callbacks for the Batch API tests.
  */
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Performs a simple batch operation.
@@ -94,7 +95,7 @@ function _batch_test_finished_helper($batch_id, $success, $results, $operations)
     $messages[] = t('An error occurred while processing @op with arguments:<br />@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE)));
   }
 
-  drupal_set_message(implode('<br>', $messages));
+  drupal_set_message(SafeMarkup::implode('<br>', $messages));
 }
 
 /**
diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig
index 7e0b485..a464e47 100644
--- a/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig
+++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant--foo.html.twig
@@ -2,4 +2,4 @@
 Template overridden based on suggestion alter hook determined by the base hook.
 
 <p>Theme hook suggestions:
-{{ theme_hook_suggestions|join("<br />") }}</p>
+{{ theme_hook_suggestions|safe_join("<br />") }}</p>
diff --git a/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig
index 655db4e..8ac8cd2 100644
--- a/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig
+++ b/core/modules/system/tests/themes/test_theme/templates/theme-test-specific-suggestions--variant.html.twig
@@ -2,4 +2,4 @@
 Template matching the specific theme call.
 
 <p>Theme hook suggestions:
-{{ theme_hook_suggestions|join("<br />") }}</p>
+{{ theme_hook_suggestions|safe_join("<br />") }}</p>
diff --git a/core/modules/text/src/TextProcessed.php b/core/modules/text/src/TextProcessed.php
index e9f0914..0c46f6a 100644
--- a/core/modules/text/src/TextProcessed.php
+++ b/core/modules/text/src/TextProcessed.php
@@ -8,6 +8,7 @@
 namespace Drupal\text;
 
 use Drupal\Component\Utility\String;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\TypedData\DataDefinitionInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\TypedData\TypedData;
@@ -60,7 +61,7 @@ public function getValue($langcode = NULL) {
     else {
       // Escape all HTML and retain newlines.
       // @see \Drupal\Core\Field\Plugin\Field\FieldFormatter\StringFormatter
-      $this->processed = nl2br(String::checkPlain($text));
+      $this->processed = SafeMarkup::set(nl2br(String::checkPlain($text)));
     }
     return $this->processed;
   }
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 967cc8b..9178c14 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -13,6 +13,7 @@
 
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Site\Settings;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 
 // These are internally used constants for this code, do not modify.
@@ -537,7 +538,8 @@ function _update_message_text($msg_type, $msg_reason, $report_link = FALSE, $lan
     }
   }
 
-  return $text;
+  // All strings are t() and empty space concatenated so return SafeMarkup.
+  return SafeMarkup::set($text);
 }
 
 /**
diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc
index b4410e0..15570af 100644
--- a/core/modules/update/update.report.inc
+++ b/core/modules/update/update.report.inc
@@ -5,6 +5,7 @@
  * Code required only when rendering the available updates report.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 
 /**
@@ -27,8 +28,10 @@ function theme_update_report($variables) {
   $output = drupal_render($update_last_check);
 
   if (!is_array($data)) {
+    // @todo when converting to Twig, $data might get double-escaped, so
+    //   check with the caller.
     $output .= '<p>' . $data . '</p>';
-    return $output;
+    return SafeMarkup::set($output);
   }
 
   $header = array();
@@ -269,7 +272,7 @@ function theme_update_report($variables) {
     $row_key = isset($project['title']) ? drupal_strtolower($project['title']) : drupal_strtolower($project['name']);
     $rows[$project['project_type']][$row_key] = array(
       'class' => array($class),
-      'data' => array($row),
+      'data' => array(SafeMarkup::set($row)),
     );
   }
 
@@ -305,7 +308,7 @@ function theme_update_report($variables) {
   );
   drupal_render($assets);
 
-  return $output;
+  return SafeMarkup::set($output);
 }
 
 /**
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index 565a53c..7df754a 100644
--- a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
+++ b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
@@ -11,6 +11,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\views\Plugin\views\HandlerBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ResultRow;
@@ -899,7 +900,7 @@ public function buildOptionsForm(&$form, &$form_state) {
       $form['alter']['help'] = array(
         '#type' => 'details',
         '#title' => t('Replacement patterns'),
-        '#value' => $output,
+        '#value' => SafeMarkup::set($output),
         '#states' => array(
           'visible' => array(
             array(
@@ -1181,6 +1182,9 @@ public function advancedRender(ResultRow $values) {
         $this->last_render = $this->renderText($alter);
       }
     }
+    // @todo This is very dicey!
+    $this->last_render = SafeMarkup::set($this->last_render);
+
 
     return $this->last_render;
   }
diff --git a/core/modules/views/src/Plugin/views/style/Rss.php b/core/modules/views/src/Plugin/views/style/Rss.php
index 0a6f8cb..8220e24 100644
--- a/core/modules/views/src/Plugin/views/style/Rss.php
+++ b/core/modules/views/src/Plugin/views/style/Rss.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\views\Plugin\views\style;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Default style plugin to render an RSS feed.
@@ -138,7 +139,7 @@ public function render() {
       '#theme' => $this->themeFunctions(),
       '#view' => $this->view,
       '#options' => $this->options,
-      '#rows' => $rows,
+      '#rows' => SafeMarkup::set($rows),
     );
     unset($this->view->row_index);
     return drupal_render($build);
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 4dfa5e8..03ef687 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -9,6 +9,7 @@
  * incoming page and block requests.
  */
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Database\Query\AlterableInterface;
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 7d1060c..01f7795 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -8,6 +8,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\views\Form\ViewsForm;
 use Drupal\views\ViewExecutable;
 
@@ -541,7 +542,8 @@ function template_preprocess_views_view_table(&$variables) {
             '#theme' => 'tablesort_indicator',
             '#style' => $initial,
           );
-          $label .= drupal_render($tablesort_indicator);
+          $markup = drupal_render($tablesort_indicator);
+          $label = SafeMarkup::implode('', array($label, $markup));
         }
 
         $query['order'] = $field;
@@ -632,7 +634,7 @@ function template_preprocess_views_view_table(&$variables) {
         $field_output = $handler->getField($num, $field);
         $element_type = $fields[$field]->elementType(TRUE, TRUE);
         if ($element_type) {
-          $field_output = '<' . $element_type . '>' . $field_output . '</' . $element_type . '>';
+          $field_output = SafeMarkup::implode('', array(SafeMarkup::set('<' . $element_type . '>'), $field_output, SafeMarkup::set('</' . $element_type . '>')));
         }
 
         // Only bother with separators and stuff if the field shows up.
@@ -640,13 +642,13 @@ function template_preprocess_views_view_table(&$variables) {
           // Place the field into the column, along with an optional separator.
           if (!empty($column_reference['content'])) {
             if (!empty($options['info'][$column]['separator'])) {
-              $column_reference['content'] .= Xss::filterAdmin($options['info'][$column]['separator']);
+              $column_reference['content'] = SafeMarkup::implode('', array($column_reference['content'], Xss::filterAdmin($options['info'][$column]['separator'])));
             }
           }
           else {
             $column_reference['content'] = '';
           }
-          $column_reference['content'] .= $field_output;
+          $column_reference['content'] = SafeMarkup::implode('', array($column_reference['content'], $field_output));
         }
       }
       $column_reference['attributes'] = new Attribute($column_reference['attributes']);
diff --git a/core/modules/views_ui/src/Controller/ViewsUIController.php b/core/modules/views_ui/src/Controller/ViewsUIController.php
index a09b063..72aff09 100644
--- a/core/modules/views_ui/src/Controller/ViewsUIController.php
+++ b/core/modules/views_ui/src/Controller/ViewsUIController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewStorageInterface;
 use Drupal\views\Views;
@@ -92,7 +93,7 @@ public function reportFields() {
       foreach ($views as $view) {
         $rows[$field_name]['data'][1][] = $this->l($view, 'views_ui.edit', array('view' => $view));
       }
-      $rows[$field_name]['data'][1] = implode(', ', $rows[$field_name]['data'][1]);
+      $rows[$field_name]['data'][1] = SafeMarkup::implode(', ', $rows[$field_name]['data'][1]);
     }
 
     // Sort rows by field name.
@@ -120,7 +121,7 @@ public function reportPlugins() {
       foreach ($row['views'] as $row_name => $view) {
         $row['views'][$row_name] = $this->l($view, 'views_ui.edit', array('view' => $view));
       }
-      $row['views'] = implode(', ', $row['views']);
+      $row['views'] = SafeMarkup::implode(', ', $row['views']);
     }
 
     // Sort rows by field name.
diff --git a/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php
index a80d33a..610be5a 100644
--- a/core/modules/views_ui/src/ViewListBuilder.php
+++ b/core/modules/views_ui/src/ViewListBuilder.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -96,7 +97,7 @@ public function buildRow(EntityInterface $view) {
           'class' => array('views-table-filter-text-source'),
         ),
         'tag' => $view->get('tag'),
-        'path' => implode(', ', $this->getDisplayPaths($view)),
+        'path' => SafeMarkup::implode(', ', $this->getDisplayPaths($view)),
         'operations' => $row['operations'],
       ),
       'title' => $this->t('Machine name: @name', array('@name' => $view->id())),
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 3b9cb81..8f1dc88 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Timer;
 use Drupal\Component\Utility\Xss;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\views\Views;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\views\ViewExecutable;
@@ -202,7 +203,7 @@ public function set($property_name, $value, $notify = TRUE) {
   }
 
   public static function getDefaultAJAXMessage() {
-    return '<div class="message">' . t("Click on an item to edit that item's details.") . '</div>';
+    return SafeMarkup::set('<div class="message">' . t("Click on an item to edit that item's details.") . '</div>');
   }
 
   /**
@@ -677,7 +678,10 @@ public function renderPreview($display_id, $args = array()) {
                 }
               }
             }
-            $rows['query'][] = array('<strong>' . t('Query') . '</strong>', '<pre>' . String::checkPlain(strtr($query_string, $quoted)) . '</pre>');
+            $rows['query'][] = array(
+              SafeMarkup::set('<strong>' . t('Query') . '</strong>'),
+              SafeMarkup::set('<pre>' . String::checkPlain(strtr($query_string, $quoted)) . '</pre>'),
+            );
             if (!empty($this->additionalQueries)) {
               $queries = '<strong>' . t('These queries were run during view rendering:') . '</strong>';
               foreach ($this->additionalQueries as $query) {
@@ -688,18 +692,24 @@ public function renderPreview($display_id, $args = array()) {
                 $queries .= t('[@time ms] @query', array('@time' => round($query['time'] * 100000, 1) / 100000.0, '@query' => $query_string));
               }
 
-              $rows['query'][] = array('<strong>' . t('Other queries') . '</strong>', '<pre>' . $queries . '</pre>');
+              $rows['query'][] = array(
+                SafeMarkup::set('<strong>' . t('Other queries') . '</strong>'),
+                SafeMarkup::set('<pre>' . $queries . '</pre>'),
+              );
             }
           }
           if ($show_info) {
-            $rows['query'][] = array('<strong>' . t('Title') . '</strong>', Xss::filterAdmin($this->executable->getTitle()));
+            $rows['query'][] = array(
+              SafeMarkup::set('<strong>' . t('Title') . '</strong>'),
+              Xss::filterAdmin($this->executable->getTitle()),
+            );
             if (isset($path)) {
               $path = l($path, $path);
             }
             else {
               $path = t('This display has no path.');
             }
-            $rows['query'][] = array('<strong>' . t('Path') . '</strong>', $path);
+            $rows['query'][] = array(SafeMarkup::set('<strong>' . t('Path') . '</strong>'), $path);
           }
 
           if ($show_stats) {
@@ -714,10 +724,10 @@ public function renderPreview($display_id, $args = array()) {
           // No query was run. Display that information in place of either the
           // query or the performance statistics, whichever comes first.
           if ($combined || ($show_location === 'above')) {
-            $rows['query'] = array(array('<strong>' . t('Query') . '</strong>', t('No query was run')));
+            $rows['query'] = array(array(SafeMarkup::set('<strong>' . t('Query') . '</strong>'), t('No query was run')));
           }
           else {
-            $rows['statistics'] = array(array('<strong>' . t('Query') . '</strong>', t('No query was run')));
+            $rows['statistics'] = array(array(SafeMarkup::set('<strong>' . t('Query') . '</strong>'), t('No query was run')));
           }
         }
       }
diff --git a/core/modules/views_ui/templates/views-ui-display-tab-setting.html.twig b/core/modules/views_ui/templates/views-ui-display-tab-setting.html.twig
index 1c67469..12cfda4 100644
--- a/core/modules/views_ui/templates/views-ui-display-tab-setting.html.twig
+++ b/core/modules/views_ui/templates/views-ui-display-tab-setting.html.twig
@@ -20,6 +20,6 @@
     <span class="label">{{ description }}</span>
   {%- endif %}
   {% if settings_links %}
-    {{ settings_links|join('<span class="label">&nbsp;|&nbsp;</span>') }}
+    {{ settings_links|safe_join('<span class="label">&nbsp;|&nbsp;</span>') }}
   {% endif %}
 </div>
diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc
index 430f14a..ce113bd 100644
--- a/core/modules/views_ui/views_ui.theme.inc
+++ b/core/modules/views_ui/views_ui.theme.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Prepares variables for Views UI display tab setting templates.
@@ -88,7 +89,7 @@ function template_preprocess_views_ui_display_tab_bucket(&$variables) {
  */
 function template_preprocess_views_ui_view_info(&$variables) {
   $variables['title'] = $variables['view']->label();
-  $variables['displays'] = empty($variables['displays']) ? t('None') : format_plural(count($variables['displays']), 'Display', 'Displays') . ': ' . '<em>' . implode(', ', $variables['displays']) . '</em>';
+  $variables['displays'] = empty($variables['displays']) ? t('None') : SafeMarkup::set(format_plural(count($variables['displays']), 'Display', 'Displays') . ': ' . '<em>' . SafeMarkup::implode(', ', $variables['displays']) . '</em>');
 }
 
 /**
diff --git a/core/themes/bartik/templates/block--system-branding-block.html.twig b/core/themes/bartik/templates/block--system-branding-block.html.twig
index 5917f58..f6147a6 100644
--- a/core/themes/bartik/templates/block--system-branding-block.html.twig
+++ b/core/themes/bartik/templates/block--system-branding-block.html.twig
@@ -23,7 +23,7 @@
     <div class="site-branding-text">
       {% if site_name %}
         <strong class="site-name">
-          <a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home">{{ site_name|e }}</a>
+          <a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home">{{ site_name }}</a>
         </strong>
       {% endif %}
       {% if site_slogan %}
diff --git a/core/themes/engines/twig/twig.engine b/core/themes/engines/twig/twig.engine
index 1595bf8..a951a8b 100644
--- a/core/themes/engines/twig/twig.engine
+++ b/core/themes/engines/twig/twig.engine
@@ -5,7 +5,9 @@
  * Handles integration of Twig templates with the Drupal theme system.
  */
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\Extension\Extension;
+use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Implements hook_theme().
@@ -45,6 +47,7 @@ function twig_init(Extension $theme) {
  *   The output generated by the template, plus any debug information.
  */
 function twig_render_template($template_file, $variables) {
+  /** @var \Twig_Environment $twig_service */
   $twig_service = \Drupal::service('twig');
   $output = array(
     'debug_prefix'    => '',
@@ -93,7 +96,7 @@ function twig_render_template($template_file, $variables) {
     $output['debug_info']   .= "\n<!-- BEGIN OUTPUT from '{$template_file}' -->\n";
     $output['debug_suffix'] .= "\n<!-- END OUTPUT from '{$template_file}' -->\n\n";
   }
-  return implode('', $output);
+  return SafeMarkup::set(implode('', $output));
 }
 
 /**
@@ -127,8 +130,8 @@ function twig_render_var($arg) {
     return NULL;
   }
 
-  // Keep Twig_Markup objects intact to prepare for later autoescaping support.
-  if ($arg instanceOf Twig_Markup) {
+  // Optimize for strings as its likely they come from escape filter.
+  if (is_string($arg)) {
     return $arg;
   }
 
@@ -140,7 +143,9 @@ function twig_render_var($arg) {
     if (method_exists($arg, '__toString')) {
       return (string) $arg;
     }
-    throw new Exception(t('Object of type "@class" cannot be printed.', array('@class' => get_class($arg))));
+    else {
+      throw new Exception(t('Object of type "@class" cannot be printed.', array('@class' => get_class($arg))));
+    }
   }
 
   // This is a normal render array.
@@ -179,3 +184,70 @@ function twig_without($element) {
   }
   return $filtered_element;
 }
+
+/**
+ * Overrides twig_escape_filter().
+ *
+ * Replacement function for twig's escape filter.
+ */
+function twig_drupal_escape_filter(\Twig_Environment $env, $arg, $strategy = 'html', $charset = null, $autoescape = false) {
+  // Check for numeric zero.
+  if ($arg === 0) {
+    return 0;
+  }
+
+  // Return early for NULL and also true for empty arrays.
+  if ($arg == NULL) {
+    return NULL;
+  }
+
+  // Keep Twig_Markup objects intact to support autoescaping.
+  if ($autoescape && $arg instanceOf \Twig_Markup) {
+    return $arg;
+  }
+
+  $return = NULL;
+
+  if (is_scalar($arg)) {
+    $return = (string) $arg;
+  } else if (is_object($arg)) {
+    if (method_exists($arg, '__toString')) {
+      $return = (string) $arg;
+    }
+    else {
+      throw new Exception(t('Object of type "@class" cannot be printed.', array('@class' => get_class($arg))));
+    }
+  }
+
+  // We have a string or an object converted to a string: Autoescape it!
+  if (isset($return)) {
+    if ($strategy != 'html') {
+      // Drupal only supports HTML strategy, fallback for other strategies.
+      // @todo Add optional strategy parameter to SafeMarkup function calls,
+      //   to avoid this when its already safe for this strategy.
+      return twig_escape_filter($env, $return, $strategy, $charset, $autoescape);
+    }
+    if (!SafeMarkup::isSafe($return)) {
+      return String::checkPlain($return);
+    }
+    return $return;
+  }
+
+  // This is a normal render array, which is safe by definition.
+  return render($arg);
+}
+
+/**
+ * Overrides twig_join_filter().
+ *
+ * Safely joins several strings together.
+ *
+ * Note: Glue is considered safe here and _not_ escaped.
+ */
+function twig_drupal_join_filter($value, $glue = '') {
+  if (is_object($value) && $value instanceof Traversable) {
+      $value = iterator_to_array($value, false);
+  }
+
+  return SafeMarkup::implode($glue, (array) $value);
+}
