diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc
index 7ab2059..9d482cc 100644
--- a/core/includes/ajax.inc
+++ b/core/includes/ajax.inc
@@ -387,7 +387,7 @@ function ajax_pre_render_element($element) {
       // uniquely, in which case a match on value is also needed.
       // @see _form_button_was_clicked()
       if (!empty($element['#is_button']) && empty($element['#has_garbage_value'])) {
-        $settings['submit']['_triggering_element_value'] = $element['#value'];
+        $settings['submit']['_triggering_element_value'] = (string) $element['#value'];
       }
     }
 
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 3d41fcb..87eea56 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -16,6 +16,7 @@
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\Site\Settings;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\Core\Utility\Title;
 use Drupal\Core\Utility\Error;
 use Symfony\Component\ClassLoader\ApcClassLoader;
@@ -1180,6 +1181,9 @@ function watchdog($type, $message, array $variables = array(), $severity = WATCH
  */
 function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) {
   if ($message) {
+    if ($message instanceof SafeMarkup) {
+      $message = chr(0) . $message;
+    }
     if (!isset($_SESSION['messages'][$type])) {
       $_SESSION['messages'][$type] = array();
     }
@@ -1223,6 +1227,13 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
  */
 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
   if ($messages = drupal_set_message()) {
+    foreach ($messages as $type => $messages_typed) {
+      foreach ($messages_typed as $key => $message) {
+        if (!$message instanceof SafeMarkup && $message[0] == chr(0)) {
+          $messages[$type][$key] = new SafeMarkup(substr($message, 1));
+        }
+      }
+    }
     if ($type) {
       if ($clear_queue) {
         unset($_SESSION['messages'][$type]);
@@ -2028,7 +2039,10 @@ function drupal_classloader($class_loader = NULL) {
  */
 function drupal_classloader_register($name, $path) {
   $loader = drupal_classloader();
-  $loader->addPsr4('Drupal\\' . $name . '\\', DRUPAL_ROOT . '/' . $path . '/src');
+  $loader->addPsr4('Drupal\\' . $name . '\\', array(
+    DRUPAL_ROOT . '/' . $path . '/lib/Drupal/' . $name,
+    DRUPAL_ROOT . '/' . $path . '/src',
+  ));
 }
 
 /**
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 5572e54..c2b1687 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\Core\Template\SafeMarkup;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AnonymousUserSession;
 
@@ -886,8 +887,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 new SafeMarkup('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
 }
 
 /**
@@ -1440,6 +1440,9 @@ function drupal_html_class($class) {
   // static instead of drupal_static().
   static $classes = array();
 
+  // @todo Needs safe makrup preservation or should be cast to string before
+  // arg is passed to this function?
+  $class = (string) $class;
   if (!isset($classes[$class])) {
     $classes[$class] = drupal_clean_css_identifier(drupal_strtolower($class));
   }
@@ -2829,6 +2832,7 @@ function drupal_pre_render_conditional_comments($elements) {
  *     - meta: To provide meta information, such as a page refresh.
  *     - link: To refer to stylesheets and other contextual information.
  *     - script: To load JavaScript.
+ *     This is not HTML escaped, 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
@@ -2841,7 +2845,9 @@ 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";
+    // Attributes are safe and we are assuming people don't use this function
+    // and second they don't pass unsafe variables to #tag.
+    $markup = new SafeMarkup('<' . $element['#tag'] . $attributes . " />\n");
   }
   else {
     $markup = '<' . $element['#tag'] . $attributes . '>';
@@ -2853,6 +2859,8 @@ function drupal_pre_render_html_tag($element) {
       $markup .= $element['#value_suffix'];
     }
     $markup .= '</' . $element['#tag'] . ">\n";
+    // @TODO Creating safe markup, avoid if possible!
+    $markup = new SafeMarkup($markup);
   }
   if (!empty($element['#noscript'])) {
     $element['#markup'] = '<noscript>' . $markup . '</noscript>';
@@ -3261,7 +3269,7 @@ function drupal_render_page($page) {
  * @param bool $is_recursive_call
  *   Whether this is a recursive call or not, for internal use.
  *
- * @return string
+ * @return \Drupal\Core\Template\SafeMarkup
  *   The rendered HTML.
  *
  * @see element_info()
@@ -3286,6 +3294,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     $cached_element = drupal_render_cache_get($elements);
     if ($cached_element !== FALSE) {
       $elements = $cached_element;
+      $elements['#markup'] = new SafeMarkup($elements['#markup']);
       // Only when we're not in a recursive drupal_render() call,
       // #post_render_cache callbacks must be executed, to prevent breaking the
       // render cache in case of nested elements with #cache set.
@@ -3336,6 +3345,33 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
     $elements['#children'] = '';
   }
 
+  // Assume still safe unless the element or it's __toString return value aren't
+  // instances of Twig_Markup.
+  $get_string = function ($element)  {
+    if (is_object($element) && method_exists($element, '__toString')) {
+      $markup = $element->__toString();
+    }
+    else {
+      $markup = $element;
+    }
+    // This is necessary because $element can be a RenderWrapper.
+    if ($markup && !$element instanceof SafeMarkup && !$markup instanceof SafeMarkup) {
+      $markup = String::checkPlain($markup);
+    }
+    return $markup;
+  };
+
+  // @TODO: remove wrapping of #markup.
+  if (isset($elements['#markup'])) {
+    if (is_object($elements['#markup']) && method_exists($elements['#markup'], '__toString')) {
+      $markup = $elements['#markup']->__toString();
+    }
+    else {
+      $markup = $elements['#markup'];
+    }
+    $elements['#markup'] = new SafeMarkup($markup);
+  }
+
   // Assume that if #theme is set it represents an implemented hook.
   $theme_is_implemented = isset($elements['#theme']);
 
@@ -3357,8 +3393,9 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
   // process as drupal_render_children() but is inlined for speed.
   if ((!$theme_is_implemented || isset($elements['#render_children'])) && empty($elements['#children'])) {
     foreach ($children as $key) {
-      $elements['#children'] .= drupal_render($elements[$key], TRUE);
+      $elements['#children'] .= $get_string(drupal_render($elements[$key], TRUE));
     }
+    $elements['#children'] = new SafeMarkup($elements['#children']);
   }
 
   // If #theme is not implemented and the element has raw #markup as a
@@ -3369,7 +3406,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'] = new SafeMarkup($get_string($elements['#markup']) . $get_string($elements['#children']));
   }
 
   // Let the theme functions in #theme_wrappers add markup around the rendered
@@ -3453,6 +3490,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
   }
 
   $elements['#printed'] = TRUE;
+  $elements['#markup'] = new SafeMarkup($elements['#markup']);
   return $elements['#markup'];
 }
 
@@ -3466,7 +3504,7 @@ function drupal_render(&$elements, $is_recursive_call = FALSE) {
  *   can be passed in to save another run of
  *   \Drupal\Core\Render\Element::children().
  *
- * @return string
+ * @return \Drupal\Core\Template\SafeMarkup
  *   The rendered HTML of all children of the element.
 
  * @see drupal_render()
@@ -3475,13 +3513,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/form.inc b/core/includes/form.inc
index 42206a4..9464fe9 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -15,6 +15,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\Core\Utility\Color;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 
@@ -983,7 +984,7 @@ function form_select_options($element, $choices = NULL) {
       $options .= form_select_options($element, $choice);
       $options .= '</optgroup>';
     }
-    elseif (is_object($choice)) {
+    elseif (is_object($choice) && isset($choice->option)) {
       $options .= form_select_options($element, $choice->option);
     }
     else {
@@ -998,7 +999,7 @@ function form_select_options($element, $choices = NULL) {
       $options .= '<option value="' . String::checkPlain($key) . '"' . $selected . '>' . String::checkPlain($choice) . '</option>';
     }
   }
-  return $options;
+  return new SafeMarkup($options);
 }
 
 /**
@@ -1659,7 +1660,7 @@ function theme_tableselect($variables) {
       foreach ($element['#header'] as $fieldname => $title) {
         // A row cell can span over multiple headers, which means less row cells
         // than headers could be present.
-        if (isset($element['#options'][$key][$fieldname])) {
+        if (isset($element['#options'][$key][$fieldname]) && is_array($element['#options'][$key][$fieldname])) {
           // 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.
@@ -1976,7 +1977,7 @@ function form_process_machine_name($element, &$form_state) {
   $element['#machine_name'] += array(
     'source' => array('label'),
     'target' => '#' . $element['#id'],
-    'label' => t('Machine name'),
+    'label' => (string) t('Machine name'),
     'replace_pattern' => '[^a-z0-9_]+',
     'replace' => '_',
     'standalone' => FALSE,
@@ -2009,13 +2010,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::concat($element['#suffix'], new SafeMarkup(' <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::concat($source['#field_suffix'], new SafeMarkup(' <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']);
@@ -2348,6 +2349,9 @@ function template_preprocess_input(&$variables) {
 function theme_input($variables) {
   $element = $variables['element'];
   $attributes = $variables['attributes'];
+  if (!$attributes instanceof Attribute) {
+    throw new \LogicException('Attributes are expected to be an instance of attributes');
+  }
   return '<input' . $attributes . ' />' . drupal_render_children($element);
 }
 
@@ -2976,8 +2980,6 @@ function theme_form_element_label($variables) {
     return '';
   }
 
-  $title = Xss::filterAdmin($element['#title']);
-
   $attributes = array();
   // Style the label as class option to display inline with the element.
   if ($element['#title_display'] == 'after') {
@@ -3008,6 +3010,8 @@ function theme_form_element_label($variables) {
     $attributes['class'][] = 'form-required';
   }
 
+  $title = Xss::filterAdmin($element['#title']);
+
   return '<label' . new Attribute($attributes) . '>' . $title . '</label>';
 }
 
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 2b06429..0c81cb8 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -21,6 +21,7 @@
 use Drupal\Core\Page\MetaElement;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Template\RenderWrapper;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\Core\Theme\ThemeSettings;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Render\Element;
@@ -579,7 +580,7 @@ function _theme($hook, $variables = array()) {
   $output = '';
   if (isset($info['function'])) {
     if (function_exists($info['function'])) {
-      $output = $info['function']($variables);
+      $output = new SafeMarkup($info['function']($variables));
     }
   }
   else {
@@ -650,7 +651,7 @@ function _theme($hook, $variables = array()) {
 
   // restore path_to_theme()
   $theme_path = $temp;
-  return (string) $output;
+  return $output;
 }
 
 /**
@@ -2053,7 +2054,10 @@ function template_preprocess_html(&$variables) {
 
   // Wrap function calls in an object so they can be called when printed.
   $variables['head'] = new RenderWrapper(function() use ($page) {
-    return implode("\n", $page->getMetaElements()) . implode("\n", $page->getLinkElements());
+    // HeadElement itself is safe and MetaElement and LinkElement both extend
+    // it so each element are safe. The result of concatenating a lot of them
+    // together is also safe.
+    return new SafeMarkup(implode("\n", $page->getMetaElements()) . implode("\n", $page->getLinkElements()));
   });
   $variables['styles'] = new RenderWrapper('drupal_get_css');
   $variables['scripts'] = new RenderWrapper('drupal_get_js');
diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc
index 8a19a49..09957df 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\Core\Template\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 contains no
+ *     XSS holes.
  *   - active: The key for the currently active maintenance task.
  *
  * @ingroup themeable
@@ -178,6 +181,7 @@ function theme_authorize_report($variables) {
     }
     $output .= '</div>';
   }
+
   return $output;
 }
 
@@ -187,6 +191,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
+ *     XSS holes.
  *   - success: A boolean indicating failure or success.
  *
  * @ingroup themeable
diff --git a/core/lib/Drupal/Component/Utility/String.php b/core/lib/Drupal/Component/Utility/String.php
index 9796a5f..7af8d67 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\Core\Template\SafeMarkup;
+
 /**
  * Provides helpers to operate on strings.
  *
@@ -22,7 +24,7 @@ class String {
    * @param string $text
    *   The text to be checked or processed.
    *
-   * @return string
+   * @return \Drupal\Core\Template\SafeMarkup
    *   An HTML safe version of $text, or an empty string if $text is not
    *   valid UTF-8.
    *
@@ -31,7 +33,7 @@ class String {
    * @see drupal_validate_utf8()
    */
   public static function checkPlain($text) {
-    return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
+    return new SafeMarkup(htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'));
   }
 
   /**
@@ -65,7 +67,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 +111,7 @@ public static function format($string, array $args = array()) {
           // Pass-through.
       }
     }
-    return strtr($string, $args);
+    return new SafeMarkup(strtr($string, $args));
   }
 
   /**
@@ -123,7 +126,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 new SafeMarkup('<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..dd0df76 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\Core\Template\SafeMarkup;
+
 /**
  * Provides helper to filter for cross-site scripting.
  *
@@ -90,7 +92,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 new SafeMarkup(preg_replace_callback('%
       (
       <(?=[^a-zA-Z!/])  # a lone <
       |                 # or
@@ -99,7 +101,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/Ajax/AjaxResponseRenderer.php b/core/lib/Drupal/Core/Ajax/AjaxResponseRenderer.php
index eac0b62..b2f87df 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponseRenderer.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponseRenderer.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Ajax;
 
 use Drupal\Core\Page\HtmlFragment;
+use Drupal\Core\Template\SafeMarkup;
 use Symfony\Component\HttpFoundation\Response;
 
 /**
@@ -36,9 +37,9 @@ public function render($content) {
       $content = $content->getContent();
     }
     // Most controllers return a render array, but some return a string.
-    if (!is_array($content)) {
+    if (is_string($content) || $content instanceof SafeMarkup) {
       $content = array(
-        '#markup' => $content,
+        '#markup' => (string) $content,
       );
     }
 
@@ -74,11 +75,9 @@ public function render($content) {
 
   /**
    * Wraps drupal_render().
-   *
-   * @todo: Remove as part of https://drupal.org/node/2182149
    */
   protected function drupalRender(&$elements, $is_recursive_call = FALSE) {
-    return drupal_render($elements, $is_recursive_call);
+    return (string) drupal_render($elements, $is_recursive_call);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Ajax/InsertCommand.php b/core/lib/Drupal/Core/Ajax/InsertCommand.php
index 9871ef2..e46eac0 100644
--- a/core/lib/Drupal/Core/Ajax/InsertCommand.php
+++ b/core/lib/Drupal/Core/Ajax/InsertCommand.php
@@ -57,7 +57,7 @@ class InsertCommand implements CommandInterface {
    */
   public function __construct($selector, $html, array $settings = NULL) {
     $this->selector = $selector;
-    $this->html = $html;
+    $this->html = (string) $html;
     $this->settings = $settings;
   }
 
diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php
index 4a5fd1d..475e1e4 100644
--- a/core/lib/Drupal/Core/Config/StorableConfigBase.php
+++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Config\Schema\Ignore;
 use Drupal\Core\Config\Schema\SchemaIncompleteException;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\Core\TypedData\PrimitiveInterface;
 use Drupal\Core\TypedData\Type\FloatInterface;
 use Drupal\Core\TypedData\Type\IntegerInterface;
@@ -169,6 +170,10 @@ protected function validateValue($key, $value) {
    *   Exception on unsupported/undefined data type deducted.
    */
   protected function castValue($key, $value) {
+    if ($value instanceof SafeMarkup) {
+      $value = (string) $value;
+    }
+
     $element = FALSE;
     try {
       $element = $this->getSchemaWrapper()->get($key);
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index fe23fb6..99784f7 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -101,7 +101,7 @@ public static function registerTwig(ContainerBuilder $container) {
         '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/Database/Install/Tasks.php b/core/lib/Drupal/Core/Database/Install/Tasks.php
index c4d88d2..6d0dfe7 100644
--- a/core/lib/Drupal/Core/Database/Install/Tasks.php
+++ b/core/lib/Drupal/Core/Database/Install/Tasks.php
@@ -92,14 +92,14 @@ protected function hasPdoDriver() {
    * Assert test as failed.
    */
   protected function fail($message) {
-    $this->results[$message] = FALSE;
+    $this->results[(string) $message] = FALSE;
   }
 
   /**
    * Assert test as a pass.
    */
   protected function pass($message) {
-    $this->results[$message] = TRUE;
+    $this->results[(string) $message] = TRUE;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index fe43d86..2b788dc 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -243,7 +243,7 @@ public function discoverServiceProviders() {
       $this->moduleList = isset($extensions['module']) ? $extensions['module'] : array();
     }
     $module_filenames = $this->getModuleFileNames();
-    $this->classLoaderAddMultiplePsr4($this->getModuleNamespacesPsr4($module_filenames));
+    $this->registerNamespacesPsr4($this->getModuleNamespacesPsr4($module_filenames));
 
     // Load each module's serviceProvider class.
     foreach ($this->moduleList as $module => $weight) {
@@ -437,7 +437,7 @@ protected function initializeContainer() {
     if (isset($this->container)) {
       // All namespaces must be registered before we attempt to use any service
       // from the container.
-      $this->classLoaderAddMultiplePsr4($this->container->getParameter('container.namespaces'));
+      $this->registerNamespacesPsr4($this->container->getParameter('container.namespaces'));
     }
     else {
       $this->container = $this->buildContainer();
@@ -710,18 +710,40 @@ protected function getModuleFileNames() {
   /**
    * Gets the PSR-4 base directories for module namespaces.
    *
-   * @param string[] $module_file_names
+   * @param array $module_file_names
    *   Array where each key is a module name, and each value is a path to the
    *   respective *.module or *.profile file.
    *
-   * @return string[]
+   * @return array
    *   Array where each key is a module namespace like 'Drupal\system', and each
-   *   value is the PSR-4 base directory associated with the module namespace.
+   *   value is an array of PSR-4 base directories associated with the module
+   *   namespace.
    */
   protected function getModuleNamespacesPsr4($module_file_names) {
     $namespaces = array();
     foreach ($module_file_names as $module => $filename) {
-      $namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/src';
+      // @todo Remove lib/Drupal/$module, once the switch to PSR-4 is complete.
+      $namespaces["Drupal\\$module"][] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $module;
+      $namespaces["Drupal\\$module"][] = DRUPAL_ROOT . '/' . dirname($filename) . '/src';
+    }
+    return $namespaces;
+  }
+
+  /**
+   * Gets the PSR-0 base directories for module namespaces.
+   *
+   * @param array $module_file_names
+   *   Array where each key is a module name, and each value is a path to the
+   *   respective *.module or *.profile file.
+   *
+   * @return array
+   *   Array where each key is a module namespace like 'Drupal\system', and each
+   *   value is a PSR-0 base directory associated with the module namespace.
+   */
+  protected function getModuleNamespaces($module_file_names) {
+    $namespaces = array();
+    foreach ($module_file_names as $module => $filename) {
+      $namespaces["Drupal\\$module"] = DRUPAL_ROOT . '/' . dirname($filename) . '/lib';
     }
     return $namespaces;
   }
@@ -734,10 +756,23 @@ protected function getModuleNamespacesPsr4($module_file_names) {
    *   is either a PSR-4 base directory, or an array of PSR-4 base directories
    *   associated with this namespace.
    */
-  protected function classLoaderAddMultiplePsr4(array $namespaces = array()) {
+  protected function registerNamespacesPsr4(array $namespaces = array()) {
     foreach ($namespaces as $prefix => $paths) {
       $this->classLoader->addPsr4($prefix . '\\', $paths);
     }
   }
 
+  /**
+   * Registers a list of namespaces with PSR-0 directories for class loading.
+   *
+   * @param array $namespaces
+   *   Array where each key is a namespace like 'Drupal\system', and each value
+   *   is either a PSR-0 base directory, or an array of PSR-0 base directories
+   *   associated with this namespace.
+   */
+  protected function registerNamespaces(array $namespaces = array()) {
+    foreach ($namespaces as $prefix => $path) {
+      $this->classLoader->add($prefix, $path);
+    }
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
index b21958b..2214894 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php
@@ -7,14 +7,8 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Component\Utility\String;
 use Drupal\Core\Database\Connection;
-use Drupal\Core\Database\Database;
 use Drupal\Core\Entity\Query\QueryInterface;
-use Drupal\Core\Entity\Schema\ContentEntitySchemaHandler;
-use Drupal\Core\Entity\Sql\DefaultTableMapping;
-use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Language\Language;
 use Drupal\field\FieldConfigUpdateForbiddenException;
@@ -30,28 +24,8 @@
  *
  * This class can be used as-is by most simple entity types. Entity types
  * requiring special handling can extend the class.
- *
- * The class uses \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler
- * internally in order to automatically generate the database schema based on
- * the defined base fields. Entity types can override
- * ContentEntityDatabaseStorage::getSchema() to customize the generated
- * schema; e.g., to add additional indexes.
  */
-class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements SqlEntityStorageInterface {
-
-  /**
-   * The storage field definitions for this entity type.
-   *
-   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
-   */
-  protected $fieldStorageDefinitions;
-
-  /**
-   * The mapping of field columns to SQL tables.
-   *
-   * @var \Drupal\Core\Entity\Sql\TableMappingInterface
-   */
-  protected $tableMapping;
+class ContentEntityDatabaseStorage extends ContentEntityStorageBase {
 
   /**
    * Name of entity's revision database table field, if it supports revisions.
@@ -63,20 +37,6 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements S
   protected $revisionKey = FALSE;
 
   /**
-   * The entity langcode key.
-   *
-   * @var string|bool
-   */
-  protected $langcodeKey = FALSE;
-
-  /**
-   * The base table of the entity.
-   *
-   * @var string
-   */
-  protected $baseTable;
-
-  /**
    * The table that stores revisions, if the entity supports revisions.
    *
    * @var string
@@ -112,13 +72,6 @@ class ContentEntityDatabaseStorage extends ContentEntityStorageBase implements S
   protected $entityManager;
 
   /**
-   * The entity schema handler.
-   *
-   * @var \Drupal\Core\Entity\Schema\EntitySchemaHandlerInterface
-   */
-  protected $schemaHandler;
-
-  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
@@ -144,193 +97,24 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa
 
     $this->database = $database;
     $this->entityManager = $entity_manager;
-    $this->fieldStorageDefinitions = $entity_manager->getBaseFieldDefinitions($entity_type->id());
-
-    // @todo Remove table names from the entity type definition in
-    //   https://drupal.org/node/2232465
-    $this->baseTable = $this->entityType->getBaseTable() ?: $this->entityTypeId;
-
-    $revisionable = $this->entityType->isRevisionable();
-    if ($revisionable) {
-      $this->revisionKey = $this->entityType->getKey('revision') ?: 'revision_id';
-      $this->revisionTable = $this->entityType->getRevisionTable() ?: $this->entityTypeId . '_revision';
-    }
-    // @todo Remove the data table check once all entity types are using
-    // entity query and we have a views data controller. See:
-    // - https://drupal.org/node/2068325
-    // - https://drupal.org/node/1740492
-    $translatable = $this->entityType->isTranslatable() && $this->entityType->getDataTable();
-    if ($translatable) {
-      $this->dataTable = $this->entityType->getDataTable() ?: $this->entityTypeId . '_field_data';
-      $this->langcodeKey = $this->entityType->getKey('langcode') ?: 'langcode';
-      $this->defaultLangcodeKey = $this->entityType->getKey('default_langcode') ?: 'default_langcode';
-    }
-    if ($revisionable && $translatable) {
-      $this->revisionDataTable = $this->entityType->getRevisionDataTable() ?: $this->entityTypeId . '_field_revision';
-    }
-  }
-
-  /**
-   * Returns the base table name.
-   *
-   * @return string
-   *   The table name.
-   */
-  public function getBaseTable() {
-    return $this->baseTable;
-  }
-
-  /**
-   * Returns the revision table name.
-   *
-   * @return string|false
-   *   The table name or FALSE if it is not available.
-   */
-  public function getRevisionTable() {
-    return $this->revisionTable;
-  }
-
-  /**
-   * Returns the data table name.
-   *
-   * @return string|false
-   *   The table name or FALSE if it is not available.
-   */
-  public function getDataTable() {
-    return $this->dataTable;
-  }
 
-  /**
-   * Returns the revision data table name.
-   *
-   * @return string|false
-   *   The table name or FALSE if it is not available.
-   */
-  public function getRevisionDataTable() {
-    return $this->revisionDataTable;
-  }
+    // Check if the entity type supports UUIDs.
+    $this->uuidKey = $this->entityType->getKey('uuid');
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    return $this->schemaHandler()->getSchema();
-  }
-
-  /**
-   * Gets the schema handler for this storage controller.
-   *
-   * @return \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler
-   *   The schema handler.
-   */
-  protected function schemaHandler() {
-    if (!isset($this->schemaHandler)) {
-      $this->schemaHandler = new ContentEntitySchemaHandler($this->entityManager, $this->entityType, $this);
+    if ($this->entityType->isRevisionable()) {
+      $this->revisionKey = $this->entityType->getKey('revision');
+      $this->revisionTable = $this->entityType->getRevisionTable();
     }
-    return $this->schemaHandler;
-  }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getTableMapping() {
-    if (!isset($this->tableMapping)) {
-
-      $definitions = array_filter($this->fieldStorageDefinitions, function (FieldDefinitionInterface $definition) {
-        // @todo Remove the check for FieldDefinitionInterface::isMultiple() when
-        //   multiple-value base fields are supported in
-        //   https://drupal.org/node/2248977.
-        return !$definition->isComputed() && !$definition->hasCustomStorage() && !$definition->isMultiple();
-      });
-      $this->tableMapping = new DefaultTableMapping($definitions);
-
-      $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)));
-      $all_fields = array_keys($definitions);
-      $revisionable_fields = array_keys(array_filter($definitions, function (FieldStorageDefinitionInterface $definition) {
-        return $definition->isRevisionable();
-      }));
-      // Make sure the key fields come first in the list of fields.
-      $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields));
-
-      // Nodes have all three of these fields, while custom blocks only have
-      // log.
-      // @todo Provide automatic definitions for revision metadata fields in
-      //   https://drupal.org/node/2248983.
-      // @todo Rename 'log' to 'revision_log' in
-      //   https://drupal.org/node/2248991.
-      $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'log'), $all_fields);
-
-      $revisionable = $this->entityType->isRevisionable();
-      // @todo Remove the data table check once all entity types are using
-      // entity query and we have a views data controller. See:
-      // - https://drupal.org/node/2068325
-      // - https://drupal.org/node/1740492
-      $translatable = $this->entityType->getDataTable() && $this->entityType->isTranslatable();
-      if (!$revisionable && !$translatable) {
-        // The base layout stores all the base field values in the base table.
-        $this->tableMapping->setFieldNames($this->baseTable, $all_fields);
-      }
-      elseif ($revisionable && !$translatable) {
-        // The revisionable layout stores all the base field values in the base
-        // table, except for revision metadata fields. Revisionable fields
-        // denormalized in the base table but also stored in the revision table
-        // together with the entity ID and the revision ID as identifiers.
-        $this->tableMapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields));
-        $revision_key_fields = array($this->idKey, $this->revisionKey);
-        $this->tableMapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields));
-      }
-      elseif (!$revisionable && $translatable) {
-        // Multilingual layouts store key field values in the base table. The
-        // other base field values are stored in the data table, no matter
-        // whether they are translatable or not. The data table holds also a
-        // denormalized copy of the bundle field value to allow for more
-        // performant queries. This means that only the UUID is not stored on
-        // the data table.
-        $this->tableMapping
-          ->setFieldNames($this->baseTable, $key_fields)
-          ->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey))))
-          // Add the denormalized 'default_langcode' field to the mapping. Its
-          // value is identical to the query expression
-          // "base_table.langcode = data_table.langcode"
-          ->setExtraColumns($this->dataTable, array('default_langcode'));
-      }
-      elseif ($revisionable && $translatable) {
-        // The revisionable multilingual layout stores key field values in the
-        // base table, except for language, which is stored in the revision
-        // table along with revision metadata. The revision data table holds
-        // data field values for all the revisionable fields and the data table
-        // holds the data field values for all non-revisionable fields. The data
-        // field values of revisionable fields are denormalized in the data
-        // table, as well.
-        $this->tableMapping->setFieldNames($this->baseTable, array_values(array_diff($key_fields, array($this->langcodeKey))));
-
-        // Like in the multilingual, non-revisionable case the UUID is not
-        // in the data table. Additionally, do not store revision metadata
-        // fields in the data table.
-        $data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields));
-        $this->tableMapping
-          ->setFieldNames($this->dataTable, $data_fields)
-          // Add the denormalized 'default_langcode' field to the mapping. Its
-          // value is identical to the query expression
-          // "base_langcode = data_table.langcode" where "base_langcode" is
-          // the language code of the default revision.
-          ->setExtraColumns($this->dataTable, array('default_langcode'));
-
-        $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields);
-        $this->tableMapping->setFieldNames($this->revisionTable, $revision_base_fields);
-
-        $revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey);
-        $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields);
-        $this->tableMapping
-          ->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields))
-          // Add the denormalized 'default_langcode' field to the mapping. Its
-          // value is identical to the query expression
-          // "revision_table.langcode = data_table.langcode".
-          ->setExtraColumns($this->revisionDataTable, array('default_langcode'));
+    // Check if the entity type has a dedicated table for fields.
+    if ($data_table = $this->entityType->getDataTable()) {
+      $this->dataTable = $data_table;
+      // Entity types having both revision and translation support should always
+      // define a revision data table.
+      if ($this->revisionTable && $revision_data_table = $this->entityType->getRevisionDataTable()) {
+        $this->revisionDataTable = $revision_data_table;
       }
     }
-
-    return $this->tableMapping;
   }
 
   /**
@@ -431,14 +215,13 @@ protected function attachPropertyData(array &$entities) {
       }
 
       $data = $query->execute();
-
-      $table_mapping = $this->getTableMapping();
+      $field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($this->entityTypeId);
       $translations = array();
       if ($this->revisionDataTable) {
-        $data_fields = array_diff_key($table_mapping->getFieldNames($this->revisionDataTable), $table_mapping->getFieldNames($this->baseTable));
+        $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityType->getRevisionDataTable()), drupal_schema_fields_sql($this->entityType->getBaseTable())));
       }
       else {
-        $data_fields = $table_mapping->getFieldNames($this->dataTable);
+        $data_column_names = array_flip(drupal_schema_fields_sql($this->entityType->getDataTable()));
       }
 
       foreach ($data as $values) {
@@ -449,16 +232,23 @@ protected function attachPropertyData(array &$entities) {
         $langcode = empty($values['default_langcode']) ? $values['langcode'] : Language::LANGCODE_DEFAULT;
         $translations[$id][$langcode] = TRUE;
 
-
-        foreach ($data_fields as $field_name) {
-          $columns = $table_mapping->getColumnNames($field_name);
-          // Do not key single-column fields by property name.
-          if (count($columns) == 1) {
-            $entities[$id][$field_name][$langcode] = $values[reset($columns)];
+        foreach (array_keys($field_definitions) as $field_name) {
+          // Handle columns named directly after the field.
+          if (isset($data_column_names[$field_name])) {
+            $entities[$id][$field_name][$langcode] = $values[$field_name];
           }
           else {
-            foreach ($columns as $property_name => $column_name) {
-              $entities[$id][$field_name][$langcode][$property_name] = $values[$column_name];
+            // @todo Change this logic to be based on a mapping of field
+            // definition properties (translatability, revisionability) in
+            // https://drupal.org/node/2144631.
+            foreach ($data_column_names as $data_column_name) {
+              // Handle columns named [field_name]__[column_name], for which we
+              // need to look through all column names from the table that start
+              // with the name of the field.
+              if (($data_field_name = strstr($data_column_name, '__', TRUE)) && $data_field_name === $field_name) {
+                $property_name = substr($data_column_name, strpos($data_column_name, '__') + 2);
+                $entities[$id][$field_name][$langcode][$property_name] = $values[$data_column_name];
+              }
             }
           }
         }
@@ -562,12 +352,11 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     }
 
     // Add fields from the {entity} table.
-    $table_mapping = $this->getTableMapping();
-    $entity_fields = $table_mapping->getAllColumns($this->baseTable);
+    $entity_fields = drupal_schema_fields_sql($this->entityType->getBaseTable());
 
     if ($this->revisionTable) {
       // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = $table_mapping->getAllColumns($this->revisionTable);
+      $entity_revision_fields = drupal_schema_fields_sql($this->entityType->getRevisionTable());
       $entity_revision_fields = array_combine($entity_revision_fields, $entity_revision_fields);
       // The ID field is provided by entity, so remove it.
       unset($entity_revision_fields[$this->idKey]);
@@ -688,12 +477,7 @@ protected function doSave($id, EntityInterface $entity) {
     $is_new = $entity->isNew();
     if (!$is_new) {
       if ($entity->isDefaultRevision()) {
-        $this->database
-          ->update($this->baseTable)
-          ->fields((array) $record)
-          ->condition($this->idKey, $record->{$this->idKey})
-          ->execute();
-        $return = SAVED_UPDATED;
+        $return = drupal_write_record($this->entityType->getBaseTable(), $record, $this->idKey);
       }
       else {
         // @todo, should a different value be returned when saving an entity
@@ -701,13 +485,13 @@ protected function doSave($id, EntityInterface $entity) {
         $return = FALSE;
       }
       if ($this->revisionTable) {
-        $entity->{$this->revisionKey}->value = $this->saveRevision($entity);
+        $record->{$this->revisionKey} = $this->saveRevision($entity);
       }
       if ($this->dataTable) {
         $this->savePropertyData($entity);
       }
       if ($this->revisionDataTable) {
-        $this->savePropertyData($entity, $this->revisionDataTable);
+        $this->savePropertyData($entity, 'revision_data_table');
       }
       if ($this->revisionTable) {
         $entity->setNewRevision(FALSE);
@@ -718,17 +502,7 @@ protected function doSave($id, EntityInterface $entity) {
       // Ensure the entity is still seen as new after assigning it an id,
       // while storing its data.
       $entity->enforceIsNew();
-      $insert_id = $this->database
-        ->insert($this->baseTable, array('return' => Database::RETURN_INSERT_ID))
-        ->fields((array) $record)
-        ->execute();
-      // Even if this is a new entity the ID key might have been set, in which
-      // case we should not override the provided ID. An empty value for the
-      // ID is interpreted as NULL and thus overridden.
-      if (empty($record->{$this->idKey})) {
-        $record->{$this->idKey} = $insert_id;
-      }
-      $return = SAVED_NEW;
+      $return = drupal_write_record($this->entityType->getBaseTable(), $record);
       $entity->{$this->idKey}->value = (string) $record->{$this->idKey};
       if ($this->revisionTable) {
         $entity->setNewRevision();
@@ -738,9 +512,8 @@ protected function doSave($id, EntityInterface $entity) {
         $this->savePropertyData($entity);
       }
       if ($this->revisionDataTable) {
-        $this->savePropertyData($entity, $this->revisionDataTable);
+        $this->savePropertyData($entity, 'revision_data_table');
       }
-
       $entity->enforceIsNew(FALSE);
       if ($this->revisionTable) {
         $entity->setNewRevision(FALSE);
@@ -770,14 +543,13 @@ protected function has($id, EntityInterface $entity) {
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity object.
-   * @param string $table_name
-   *   (optional) The table name to save to. Defaults to the data table.
+   * @param string $table_key
+   *   (optional) The entity key identifying the target table. Defaults to
+   *   'data_table'.
    */
-  protected function savePropertyData(EntityInterface $entity, $table_name = NULL) {
-    if (!isset($table_name)) {
-      $table_name = $this->dataTable;
-    }
-    $revision = $table_name != $this->dataTable;
+  protected function savePropertyData(EntityInterface $entity, $table_key = 'data_table') {
+    $table_name = $this->entityType->get($table_key);
+    $revision = $table_key != 'data_table';
 
     if (!$revision || !$entity->isNewRevision()) {
       $key = $revision ? $this->revisionKey : $this->idKey;
@@ -792,7 +564,7 @@ protected function savePropertyData(EntityInterface $entity, $table_name = NULL)
 
     foreach ($entity->getTranslationLanguages() as $langcode => $language) {
       $translation = $entity->getTranslation($langcode);
-      $record = $this->mapToDataStorageRecord($translation, $table_name);
+      $record = $this->mapToDataStorageRecord($translation, $table_key);
       $values = (array) $record;
       $query
         ->fields(array_keys($values))
@@ -817,43 +589,63 @@ protected function invokeHook($hook, EntityInterface $entity) {
    *
    * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity object.
-   * @param string $table_name
-   *   (optional) The table name to map records to. Defaults to the base table.
+   * @param string $table_key
+   *   (optional) The entity key identifying the target table. Defaults to
+   *   'base_table'.
    *
    * @return \stdClass
    *   The record to store.
    */
-  protected function mapToStorageRecord(ContentEntityInterface $entity, $table_name = NULL) {
-    if (!isset($table_name)) {
-      $table_name = $this->baseTable;
-    }
-
+  protected function mapToStorageRecord(ContentEntityInterface $entity, $table_key = 'base_table') {
     $record = new \stdClass();
-    $table_mapping = $this->getTableMapping();
-    foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
+    $values = array();
+    $schema = drupal_get_schema($this->entityType->get($table_key));
+    $is_new = $entity->isNew();
 
-      if (empty($this->fieldStorageDefinitions[$field_name])) {
-        throw new EntityStorageException(String::format('Table mapping contains invalid field %field.', array('%field' => $field_name)));
+    $multi_column_fields = array();
+    foreach (drupal_schema_fields_sql($this->entityType->get($table_key)) as $name) {
+      // Check for fields which store data in multiple columns and process them
+      // separately.
+      if ($field = strstr($name, '__', TRUE)) {
+        $multi_column_fields[$field] = TRUE;
+        continue;
       }
-      $definition = $this->fieldStorageDefinitions[$field_name];
-      $columns = $table_mapping->getColumnNames($field_name);
-
-      foreach ($columns as $column_name => $schema_name) {
-        // If there is no main property and only a single column, get all
-        // properties from the first field item and assume that they will be
-        // stored serialized.
-        // @todo Give field types more control over this behavior in
-        //   https://drupal.org/node/2232427.
-        if (!$definition->getMainPropertyName() && count($columns) == 1) {
-          $value = $entity->$field_name->first()->getValue();
+      $values[$name] = NULL;
+      if ($entity->hasField($name)) {
+        // Only the first field item is stored.
+        $field_item = $entity->get($name)->first();
+        $main_property = $entity->getFieldDefinition($name)->getMainPropertyName();
+        if ($main_property && isset($field_item->$main_property)) {
+          // If the field has a main property, store the value of that.
+          $values[$name] = $field_item->$main_property;
         }
-        else {
-          $value = isset($entity->$field_name->$column_name) ? $entity->$field_name->$column_name : NULL;
+        elseif (!$main_property) {
+          // If there is no main property, get all properties from the first
+          // field item and assume that they will be stored serialized.
+          // @todo Give field types more control over this behavior in
+          //   https://drupal.org/node/2232427.
+          $values[$name] = $field_item->getValue();
         }
-        if (!empty($definition->getSchema()['columns'][$column_name]['serialize'])) {
-          $value = serialize($value);
+      }
+    }
+
+    // Handle fields that store multiple properties and match each property name
+    // to its schema column name.
+    foreach (array_keys($multi_column_fields) as $field_name) {
+      $field_items = $entity->get($field_name);
+      $field_value = $field_items->getValue();
+      foreach (array_keys($field_items->getFieldDefinition()->getColumns()) as $field_schema_column) {
+        if (isset($schema['fields'][$field_name . '__' . $field_schema_column])) {
+          $values[$field_name . '__' . $field_schema_column] = isset($field_value[0][$field_schema_column]) ? $field_value[0][$field_schema_column] : NULL;
         }
-        $record->$schema_name = drupal_schema_get_field_value($definition->getSchema()['columns'][$column_name], $value);
+      }
+    }
+
+    foreach ($values as $field_name => $value) {
+      // If we are creating a new entity, we must not populate the record with
+      // NULL values otherwise defaults would not be applied.
+      if (isset($value) || !$is_new) {
+        $record->$field_name = drupal_schema_get_field_value($schema['fields'][$field_name], $value);
       }
     }
 
@@ -865,17 +657,15 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_nam
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity object.
-   * @param string $table_name
-   *   (optional) The table name to map records to. Defaults to the data table.
+   * @param string $table_key
+   *   (optional) The entity key identifying the target table. Defaults to
+   *   'data_table'.
    *
    * @return \stdClass
    *   The record to store.
    */
-  protected function mapToDataStorageRecord(EntityInterface $entity, $table_name = NULL) {
-    if (!isset($table_name)) {
-      $table_name = $this->dataTable;
-    }
-    $record = $this->mapToStorageRecord($entity, $table_name);
+  protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = 'data_table') {
+    $record = $this->mapToStorageRecord($entity, $table_key);
     $record->langcode = $entity->language()->id;
     $record->default_langcode = intval($record->langcode == $entity->getUntranslated()->language()->id);
     return $record;
@@ -891,20 +681,12 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $table_name =
    *   The revision id.
    */
   protected function saveRevision(EntityInterface $entity) {
-    $record = $this->mapToStorageRecord($entity, $this->revisionTable);
+    $record = $this->mapToStorageRecord($entity, 'revision_table');
 
     $entity->preSaveRevision($this, $record);
 
     if ($entity->isNewRevision()) {
-      $insert_id = $this->database
-        ->insert($this->revisionTable, array('return' => Database::RETURN_INSERT_ID))
-        ->fields((array) $record)
-        ->execute();
-      // Even if this is a new revsision, the revision ID key might have been
-      // set in which case we should not override the provided revision ID.
-      if (!isset($record->{$this->revisionKey})) {
-        $record->{$this->revisionKey} = $insert_id;
-      }
+      drupal_write_record($this->revisionTable, $record);
       if ($entity->isDefaultRevision()) {
         $this->database->update($this->entityType->getBaseTable())
           ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
@@ -913,11 +695,7 @@ protected function saveRevision(EntityInterface $entity) {
       }
     }
     else {
-      $this->database
-        ->update($this->revisionTable)
-        ->fields((array) $record)
-        ->condition($this->revisionKey, $record->{$this->revisionKey})
-        ->execute();
+      drupal_write_record($this->revisionTable, $record, $this->revisionKey);
     }
 
     // Make sure to update the new revision key for the entity.
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index cc7b316..557ddf3 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -555,18 +555,14 @@ protected static function invalidateTagsOnDelete(array $entities) {
    * Acts on entities of which this entity is a bundle entity type.
    */
   protected function onUpdateBundleEntity() {
+    // If this entity is a bundle entity type of another entity type, and we're
+    // updating an existing entity, and that other entity type has a view
+    // builder class, then invalidate the render cache of entities for which
+    // this entity is a bundle.
     $bundle_of = $this->getEntityType()->getBundleOf();
-    if ($bundle_of !== FALSE) {
-      // If this entity is a bundle entity type of another entity type, and we're
-      // updating an existing entity, and that other entity type has a view
-      // builder class, then invalidate the render cache of entities for which
-      // this entity is a bundle.
-      $entity_manager = $this->entityManager();
-      if ($entity_manager->hasController($bundle_of, 'view_builder')) {
-        $entity_manager->getViewBuilder($bundle_of)->resetCache();
-      }
-      // Entity bundle field definitions may depend on bundle settings.
-      $entity_manager->clearCachedFieldDefinitions();
+    $entity_manager = \Drupal::entityManager();
+    if ($bundle_of !== FALSE && $entity_manager->hasController($bundle_of, 'view_builder')) {
+      $entity_manager->getViewBuilder($bundle_of)->resetCache();
     }
   }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageBase.php b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
index f10eba9..5d24441 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageBase.php
@@ -76,7 +76,6 @@ public function __construct(EntityTypeInterface $entity_type) {
     $this->entityTypeId = $entity_type->id();
     $this->entityType = $entity_type;
     $this->idKey = $this->entityType->getKey('id');
-    $this->uuidKey = $this->entityType->getKey('uuid');
     $this->entityClass = $this->entityType->getClass();
   }
 
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index 6926603..50ab59b 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -12,7 +12,6 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Entity\Query\QueryException;
-use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
 use Drupal\field\Entity\FieldConfig;
 use Drupal\field\FieldConfigInterface;
 
@@ -36,6 +35,7 @@ class Tables implements TablesInterface {
    */
   protected $entityTables = array();
 
+
   /**
    * Field table array, key is table name, value is alias.
    *
@@ -46,18 +46,10 @@ class Tables implements TablesInterface {
   protected $fieldTables = array();
 
   /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManager
-   */
-  protected $entityManager;
-
-  /**
    * @param \Drupal\Core\Database\Query\SelectInterface $sql_query
    */
   public function __construct(SelectInterface $sql_query) {
     $this->sqlQuery = $sql_query;
-    $this->entityManager = \Drupal::entityManager();
   }
 
   /**
@@ -65,6 +57,7 @@ public function __construct(SelectInterface $sql_query) {
    */
   public function addField($field, $type, $langcode) {
     $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
+    $entity_manager = \Drupal::entityManager();
     $age = $this->sqlQuery->getMetaData('age');
     // This variable ensures grouping works correctly. For example:
     // ->condition('tags', 2, '>')
@@ -80,13 +73,13 @@ public function addField($field, $type, $langcode) {
     // This will contain the definitions of the last specifier seen by the
     // system.
     $propertyDefinitions = array();
-    $entity_type = $this->entityManager->getDefinition($entity_type_id);
+    $entity_type = $entity_manager->getDefinition($entity_type_id);
 
     $field_storage_definitions = array();
     // @todo Needed for menu links, make this implementation content entity
     //   specific after https://drupal.org/node/2256521.
     if ($entity_type instanceof ContentEntityTypeInterface) {
-      $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
+      $field_storage_definitions = $entity_manager->getFieldStorageDefinitions($entity_type_id);
     }
     for ($key = 0; $key <= $count; $key ++) {
       // If there is revision support and only the current revision is being
@@ -161,10 +154,10 @@ public function addField($field, $type, $langcode) {
         $entity_tables = array();
         if ($data_table = $entity_type->getDataTable()) {
           $this->sqlQuery->addMetaData('simple_query', FALSE);
-          $entity_tables[$data_table] = $this->getTableMapping($data_table, $entity_type_id);
+          $entity_tables[$data_table] = drupal_get_schema($data_table);
         }
         $entity_base_table = $entity_type->getBaseTable();
-        $entity_tables[$entity_base_table] = $this->getTableMapping($entity_base_table, $entity_type_id);
+        $entity_tables[$entity_base_table] = drupal_get_schema($entity_base_table);
         $sql_column = $specifier;
         $table = $this->ensureEntityTable($index_prefix, $specifier, $type, $langcode, $base_table, $entity_id_field, $entity_tables);
       }
@@ -181,8 +174,8 @@ public function addField($field, $type, $langcode) {
         if (isset($propertyDefinitions[$relationship_specifier]) && $field->getPropertyDefinition('entity')->getDataType() == 'entity_reference' ) {
           // If it is, use the entity type.
           $entity_type_id = $propertyDefinitions[$relationship_specifier]->getTargetDefinition()->getEntityTypeId();
-          $entity_type = $this->entityManager->getDefinition($entity_type_id);
-          $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
+          $entity_type = $entity_manager->getDefinition($entity_type_id);
+          $field_storage_definitions = $entity_manager->getFieldStorageDefinitions($entity_type_id);
           // Add the new entity base table using the table and sql column.
           $join_condition= '%alias.' . $entity_type->getKey('id') . " = $table.$sql_column";
           $base_table = $this->sqlQuery->leftJoin($entity_type->getBaseTable(), NULL, $join_condition);
@@ -206,8 +199,8 @@ public function addField($field, $type, $langcode) {
    * @throws \Drupal\Core\Entity\Query\QueryException
    */
   protected function ensureEntityTable($index_prefix, $property, $type, $langcode, $base_table, $id_field, $entity_tables) {
-    foreach ($entity_tables as $table => $mapping) {
-      if (isset($mapping[$property])) {
+    foreach ($entity_tables as $table => $schema) {
+      if (isset($schema['fields'][$property])) {
         if (!isset($this->entityTables[$index_prefix . $table])) {
           $this->entityTables[$index_prefix . $table] = $this->addJoin($type, $table, "%alias.$id_field = $base_table.$id_field", $langcode);
         }
@@ -248,27 +241,4 @@ protected function addJoin($type, $table, $join_condition, $langcode) {
     return $this->sqlQuery->addJoin($type, $table, NULL, $join_condition, $arguments);
   }
 
-  /**
-   * Returns the schema for the given table.
-   *
-   * @param string $table
-   *   The table name.
-   *
-   * @return array|bool
-   *   The table field mapping for the given table or FALSE if not available.
-   */
-  protected function getTableMapping($table, $entity_type_id) {
-    $storage = $this->entityManager->getStorage($entity_type_id);
-    if ($storage instanceof SqlEntityStorageInterface) {
-      $mapping = $storage->getTableMapping()->getAllColumns($table);
-    }
-    else {
-      // @todo Stop calling drupal_get_schema() once menu links are converted
-      //   to the Entity Field API. See https://drupal.org/node/1842858.
-      $schema = drupal_get_schema($table);
-      $mapping = array_keys($schema['fields']);
-    }
-    return array_flip($mapping);
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php b/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php
deleted file mode 100644
index 5e7c0e5..0000000
--- a/core/lib/Drupal/Core/Entity/Schema/ContentEntitySchemaHandler.php
+++ /dev/null
@@ -1,518 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler.
- */
-
-namespace Drupal\Core\Entity\Schema;
-
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
-use Drupal\Core\Entity\ContentEntityTypeInterface;
-use Drupal\Core\Entity\EntityManagerInterface;
-
-/**
- * Defines a schema handler that supports revisionable, translatable entities.
- */
-class ContentEntitySchemaHandler implements EntitySchemaHandlerInterface {
-
-  /**
-   * The entity type this schema builder is responsible for.
-   *
-   * @var \Drupal\Core\Entity\ContentEntityTypeInterface
-   */
-  protected $entityType;
-
-  /**
-   * The storage field definitions for this entity type.
-   *
-   * @var \Drupal\Core\Field\FieldDefinitionInterface[]
-   */
-  protected $fieldStorageDefinitions;
-
-  /**
-   * The storage object for the given entity type.
-   *
-   * @var \Drupal\Core\Entity\ContentEntityDatabaseStorage
-   */
-  protected $storage;
-
-  /**
-   * A static cache of the generated schema array.
-   *
-   * @var array
-   */
-  protected $schema;
-
-  /**
-   * Constructs a ContentEntitySchemaHandler.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
-   *   The entity type.
-   * @param \Drupal\Core\Entity\ContentEntityDatabaseStorage $storage
-   *   The storage of the entity type. This must be an SQL-based storage.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, ContentEntityTypeInterface $entity_type, ContentEntityDatabaseStorage $storage) {
-    $this->entityType = $entity_type;
-    $this->fieldStorageDefinitions = $entity_manager->getFieldStorageDefinitions($entity_type->id());
-    $this->storage = $storage;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    // Prepare basic information about the entity type.
-    $tables = $this->getTables();
-
-    if (!isset($this->schema[$this->entityType->id()])) {
-      // Initialize the table schema.
-      $schema[$tables['base_table']] = $this->initializeBaseTable();
-      if (isset($tables['revision_table'])) {
-        $schema[$tables['revision_table']] = $this->initializeRevisionTable();
-      }
-      if (isset($tables['data_table'])) {
-        $schema[$tables['data_table']] = $this->initializeDataTable();
-      }
-      if (isset($tables['revision_data_table'])) {
-        $schema[$tables['revision_data_table']] = $this->initializeRevisionDataTable();
-      }
-
-      $table_mapping = $this->storage->getTableMapping();
-      foreach ($table_mapping->getTableNames() as $table_name) {
-        // Add the schema from field definitions.
-        foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
-          $column_names = $table_mapping->getColumnNames($field_name);
-          $this->addFieldSchema($schema[$table_name], $field_name, $column_names);
-        }
-
-        // Add the schema for extra fields.
-        foreach ($table_mapping->getExtraColumns($table_name) as $column_name) {
-          if ($column_name == 'default_langcode') {
-            $this->addDefaultLangcodeSchema($schema[$table_name]);
-          }
-        }
-      }
-
-      // Process tables after having gathered field information.
-      $this->processBaseTable($schema[$tables['base_table']]);
-      if (isset($tables['revision_table'])) {
-        $this->processRevisionTable($schema[$tables['revision_table']]);
-      }
-      if (isset($tables['data_table'])) {
-        $this->processDataTable($schema[$tables['data_table']]);
-      }
-      if (isset($tables['revision_data_table'])) {
-        $this->processRevisionDataTable($schema[$tables['revision_data_table']]);
-      }
-
-      $this->schema[$this->entityType->id()] = $schema;
-    }
-
-    return $this->schema[$this->entityType->id()];
-  }
-
-  /**
-   * Gets a list of entity type tables.
-   *
-   * @return array
-   *   A list of entity type tables, keyed by table key.
-   */
-  protected function getTables() {
-    return array_filter(array(
-      'base_table' => $this->storage->getBaseTable(),
-      'revision_table' => $this->storage->getRevisionTable(),
-      'data_table' => $this->storage->getDataTable(),
-      'revision_data_table' => $this->storage->getRevisionDataTable(),
-    ));
-  }
-
-  /**
-   * Returns the schema for a single field definition.
-   *
-   * @param array $schema
-   *   The table schema to add the field schema to, passed by reference.
-   * @param string $field_name
-   *   The name of the field.
-   * @param string[] $column_mapping
-   *   A mapping of field column names to database column names.
-   */
-  protected function addFieldSchema(array &$schema, $field_name, array $column_mapping) {
-    $field_schema = $this->fieldStorageDefinitions[$field_name]->getSchema();
-    $field_description = $this->fieldStorageDefinitions[$field_name]->getDescription();
-
-    foreach ($column_mapping as $field_column_name => $schema_field_name) {
-      $column_schema = $field_schema['columns'][$field_column_name];
-
-      $schema['fields'][$schema_field_name] = $column_schema;
-      $schema['fields'][$schema_field_name]['description'] = $field_description;
-      // Only entity keys are required.
-      $keys = $this->entityType->getKeys() + array('langcode' => 'langcode');
-      // The label is an entity key, but label fields are not necessarily
-      // required.
-      // Because entity ID and revision ID are both serial fields in the base
-      // and revision table respectively, the revision ID is not known yet, when
-      // inserting data into the base table. Instead the revision ID in the base
-      // table is updated after the data has been inserted into the revision
-      // table. For this reason the revision ID field cannot be marked as NOT
-      // NULL.
-      unset($keys['label'], $keys['revision']);
-      // Key fields may not be NULL.
-      if (in_array($field_name, $keys)) {
-        $schema['fields'][$schema_field_name]['not null'] = TRUE;
-      }
-    }
-
-    if (!empty($field_schema['indexes'])) {
-      $indexes = $this->getFieldIndexes($field_name, $field_schema, $column_mapping);
-      $schema['indexes'] = array_merge($schema['indexes'], $indexes);
-    }
-
-    if (!empty($field_schema['unique keys'])) {
-      $unique_keys = $this->getFieldUniqueKeys($field_name, $field_schema, $column_mapping);
-      $schema['unique keys'] = array_merge($schema['unique keys'], $unique_keys);
-    }
-
-    if (!empty($field_schema['foreign keys'])) {
-      $foreign_keys = $this->getFieldForeignKeys($field_name, $field_schema, $column_mapping);
-      $schema['foreign keys'] = array_merge($schema['foreign keys'], $foreign_keys);
-    }
-  }
-
-  /**
-   * Returns an index schema array for a given field.
-   *
-   * @param string $field_name
-   *   The name of the field.
-   * @param array $field_schema
-   *   The schema of the field.
-   * @param string[] $column_mapping
-   *   A mapping of field column names to database column names.
-   *
-   * @return array
-   *   The schema definition for the indexes.
-   */
-  protected function getFieldIndexes($field_name, array $field_schema, array $column_mapping) {
-    return $this->getFieldSchemaData($field_name, $field_schema, $column_mapping, 'indexes');
-  }
-
-  /**
-   * Returns a unique key schema array for a given field.
-   *
-   * @param string $field_name
-   *   The name of the field.
-   * @param array $field_schema
-   *   The schema of the field.
-   * @param string[] $column_mapping
-   *   A mapping of field column names to database column names.
-   *
-   * @return array
-   *   The schema definition for the unique keys.
-   */
-  protected function getFieldUniqueKeys($field_name, array $field_schema, array $column_mapping) {
-    return $this->getFieldSchemaData($field_name, $field_schema, $column_mapping, 'unique keys');
-  }
-
-  /**
-   * Returns field schema data for the given key.
-   *
-   * @param string $field_name
-   *   The name of the field.
-   * @param array $field_schema
-   *   The schema of the field.
-   * @param string[] $column_mapping
-   *   A mapping of field column names to database column names.
-   * @param string $schema_key
-   *   The type of schema data. Either 'indexes' or 'unique keys'.
-   *
-   * @return array
-   *   The schema definition for the specified key.
-   */
-  protected function getFieldSchemaData($field_name, array $field_schema, array $column_mapping, $schema_key) {
-    $data = array();
-
-    foreach ($field_schema[$schema_key] as $key => $columns) {
-      // To avoid clashes with entity-level indexes or unique keys we use
-      // "{$entity_type_id}_field__" as a prefix instead of just
-      // "{$entity_type_id}__". We additionally namespace the specifier by the
-      // field name to avoid clashes when multiple fields of the same type are
-      // added to an entity type.
-      $entity_type_id = $this->entityType->id();
-      $real_key = "{$entity_type_id}_field__{$field_name}__{$key}";
-      foreach ($columns as $column) {
-        // Allow for indexes and unique keys to specified as an array of column
-        // name and length.
-        if (is_array($column)) {
-          list($column_name, $length) = $column;
-          $data[$real_key][] = array($column_mapping[$column_name], $length);
-        }
-        else {
-          $data[$real_key][] = $column_mapping[$column];
-        }
-      }
-    }
-
-    return $data;
-  }
-
-  /**
-   * Returns field foreign keys.
-   *
-   * @param string $field_name
-   *   The name of the field.
-   * @param array $field_schema
-   *   The schema of the field.
-   * @param string[] $column_mapping
-   *   A mapping of field column names to database column names.
-   *
-   * @return array
-   *   The schema definition for the foreign keys.
-   */
-  protected function getFieldForeignKeys($field_name, array $field_schema, array $column_mapping) {
-    $foreign_keys = array();
-
-    foreach ($field_schema['foreign keys'] as $specifier => $specification) {
-      // To avoid clashes with entity-level foreign keys we use
-      // "{$entity_type_id}_field__" as a prefix instead of just
-      // "{$entity_type_id}__". We additionally namespace the specifier by the
-      // field name to avoid clashes when multiple fields of the same type are
-      // added to an entity type.
-      $entity_type_id = $this->entityType->id();
-      $real_specifier = "{$entity_type_id}_field__{$field_name}__{$specifier}";
-      $foreign_keys[$real_specifier]['table'] = $specification['table'];
-      foreach ($specification['columns'] as $column => $referenced) {
-        $foreign_keys[$real_specifier]['columns'][$column_mapping[$column]] = $referenced;
-      }
-    }
-
-    return $foreign_keys;
-  }
-
-  /**
-   * Returns the schema for the 'default_langcode' metadata field.
-   *
-   * @param array $schema
-   *   The table schema to add the field schema to, passed by reference.
-   *
-   * @return array
-   *   A schema field array for the 'default_langcode' metadata field.
-   */
-  protected function addDefaultLangcodeSchema(&$schema) {
-    $schema['fields']['default_langcode'] =  array(
-      'description' => 'Boolean indicating whether field values are in the default entity language.',
-      'type' => 'int',
-      'size' => 'tiny',
-      'not null' => TRUE,
-      'default' => 1,
-    );
-  }
-
-  /**
-   * Initializes common information for a base table.
-   *
-   * @return array
-   *   A partial schema array for the base table.
-   */
-  protected function initializeBaseTable() {
-    $entity_type_id = $this->entityType->id();
-
-    $schema = array(
-      'description' => "The base table for $entity_type_id entities.",
-      'primary key' => array($this->entityType->getKey('id')),
-      'indexes' => array(),
-      'foreign keys' => array(),
-    );
-
-    if ($this->entityType->hasKey('uuid')) {
-      $uuid_key = $this->entityType->getKey('uuid');
-      $schema['unique keys'] = array(
-        $this->getEntityIndexName($uuid_key) => array($uuid_key),
-      );
-    }
-
-    if ($this->entityType->hasKey('revision')) {
-      $revision_key = $this->entityType->getKey('revision');
-      $key_name = $this->getEntityIndexName($revision_key);
-      $schema['unique keys'][$key_name] = array($revision_key);
-      $schema['foreign keys'][$entity_type_id . '__revision'] = array(
-        'table' => $this->storage->getRevisionTable(),
-        'columns' => array($revision_key => $revision_key),
-      );
-    }
-
-    return $schema;
-  }
-
-  /**
-   * Initializes common information for a revision table.
-   *
-   * @return array
-   *   A partial schema array for the revision table.
-   */
-  protected function initializeRevisionTable() {
-    $entity_type_id = $this->entityType->id();
-    $id_key = $this->entityType->getKey('id');
-    $revision_key = $this->entityType->getKey('revision');
-
-    $schema = array(
-      'description' => "The revision table for $entity_type_id entities.",
-      'primary key' => array($revision_key),
-      'indexes' => array(),
-      'foreign keys' => array(
-         $entity_type_id . '__revisioned' => array(
-          'table' => $this->storage->getBaseTable(),
-          'columns' => array($id_key => $id_key),
-        ),
-      ),
-    );
-
-    $schema['indexes'][$this->getEntityIndexName($id_key)] = array($id_key);
-
-    return $schema;
-  }
-
-  /**
-   * Initializes common information for a data table.
-   *
-   * @return array
-   *   A partial schema array for the data table.
-   */
-  protected function initializeDataTable() {
-    $entity_type_id = $this->entityType->id();
-    $id_key = $this->entityType->getKey('id');
-
-    $schema = array(
-      'description' => "The data table for $entity_type_id entities.",
-      // @todo Use the language entity key when https://drupal.org/node/2143729
-      //   is in.
-      'primary key' => array($id_key, 'langcode'),
-      'indexes' => array(),
-      'foreign keys' => array(
-        $entity_type_id => array(
-          'table' => $this->storage->getBaseTable(),
-          'columns' => array($id_key => $id_key),
-        ),
-      ),
-    );
-
-    if ($this->entityType->hasKey('revision')) {
-      $key = $this->entityType->getKey('revision');
-      $schema['indexes'][$this->getEntityIndexName($key)] = array($key);
-    }
-
-    return $schema;
-  }
-
-  /**
-   * Initializes common information for a revision data table.
-   *
-   * @return array
-   *   A partial schema array for the revision data table.
-   */
-  protected function initializeRevisionDataTable() {
-    $entity_type_id = $this->entityType->id();
-    $id_key = $this->entityType->getKey('id');
-    $revision_key = $this->entityType->getKey('revision');
-
-    $schema = array(
-      'description' => "The revision data table for $entity_type_id entities.",
-      // @todo Use the language entity key when https://drupal.org/node/2143729
-      //   is in.
-      'primary key' => array($revision_key, 'langcode'),
-      'indexes' => array(),
-      'foreign keys' => array(
-        $entity_type_id => array(
-          'table' => $this->storage->getBaseTable(),
-          'columns' => array($id_key => $id_key),
-        ),
-        $entity_type_id . '__revision' => array(
-          'table' => $this->storage->getRevisionTable(),
-          'columns' => array($revision_key => $revision_key),
-        )
-      ),
-    );
-
-    return $schema;
-  }
-
-  /**
-   * Processes the gathered schema for a base table.
-   *
-   * @param array $schema
-   *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
-   */
-  protected function processBaseTable(array &$schema) {
-    $this->processIdentifierSchema($schema, $this->entityType->getKey('id'));
-  }
-
-  /**
-   * Processes the gathered schema for a base table.
-   *
-   * @param array $schema
-   *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
-   */
-  protected function processRevisionTable(array &$schema) {
-    $this->processIdentifierSchema($schema, $this->entityType->getKey('revision'));
-  }
-
-  /**
-   * Processes the gathered schema for a base table.
-   *
-   * @param array $schema
-   *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
-   */
-  protected function processDataTable(array &$schema) {
-  }
-
-  /**
-   * Processes the gathered schema for a base table.
-   *
-   * @param array $schema
-   *   The table schema, passed by reference.
-   *
-   * @return array
-   *   A partial schema array for the base table.
-   */
-  protected function processRevisionDataTable(array &$schema) {
-  }
-
-  /**
-   * Processes the specified entity key.
-   *
-   * @param array $schema
-   *   The table schema, passed by reference.
-   * @param string $key
-   *   The entity key name.
-   */
-  protected function processIdentifierSchema(&$schema, $key) {
-    if ($schema['fields'][$key]['type'] == 'int') {
-      $schema['fields'][$key]['type'] = 'serial';
-    }
-    unset($schema['fields'][$key]['default']);
-  }
-
-  /**
-   * Returns the name to be used for the given entity index.
-   *
-   * @param string $index
-   *   The index column name.
-   *
-   * @return string
-   *   The index name.
-   */
-  protected function getEntityIndexName($index) {
-    return $this->entityType->id() . '__' . $index;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaHandlerInterface.php b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaHandlerInterface.php
deleted file mode 100644
index a38b82c..0000000
--- a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaHandlerInterface.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Schema\EntitySchemaHandlerInterface.
- */
-
-namespace Drupal\Core\Entity\Schema;
-
-/**
- * Defines an interface for handling the storage schema of entities.
- */
-interface EntitySchemaHandlerInterface extends EntitySchemaProviderInterface {
-}
diff --git a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaProviderInterface.php b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaProviderInterface.php
deleted file mode 100644
index c976782..0000000
--- a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaProviderInterface.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Schema\EntitySchemaProviderInterface.
- */
-
-namespace Drupal\Core\Entity\Schema;
-
-/**
- * Defines a common interface to return the storage schema for entities.
- */
-interface EntitySchemaProviderInterface {
-
-  /**
-   * Gets the full schema array for a given entity type.
-   *
-   * @return array
-   *   A schema array for the entity type's tables.
-   */
-  public function getSchema();
-
-}
diff --git a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php b/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
deleted file mode 100644
index e872d57..0000000
--- a/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php
+++ /dev/null
@@ -1,180 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Sql\DefaultTableMapping.
- */
-
-namespace Drupal\Core\Entity\Sql;
-
-/**
- * Defines a default table mapping class.
- */
-class DefaultTableMapping implements TableMappingInterface {
-
-  /**
-   * A list of field storage definitions that are available for this mapping.
-   *
-   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
-   */
-  protected $fieldStorageDefinitions = array();
-
-  /**
-   * A list of field names per table.
-   *
-   * This corresponds to the return value of
-   * TableMappingInterface::getFieldNames() except that this variable is
-   * additionally keyed by table name.
-   *
-   * @var array[]
-   */
-  protected $fieldNames = array();
-
-  /**
-   * A list of database columns which store denormalized data per table.
-   *
-   * This corresponds to the return value of
-   * TableMappingInterface::getExtraColumns() except that this variable is
-   * additionally keyed by table name.
-   *
-   * @var array[]
-   */
-  protected $extraColumns = array();
-
-  /**
-   * A mapping of column names per field name.
-   *
-   * This corresponds to the return value of
-   * TableMappingInterface::getColumnNames() except that this variable is
-   * additionally keyed by field name.
-   *
-   * This data is derived from static::$storageDefinitions, but is stored
-   * separately to avoid repeated processing.
-   *
-   * @var array[]
-   */
-  protected $columnMapping = array();
-
-  /**
-   * A list of all database columns per table.
-   *
-   * This corresponds to the return value of
-   * TableMappingInterface::getAllColumns() except that this variable is
-   * additionally keyed by table name.
-   *
-   * This data is derived from static::$storageDefinitions, static::$fieldNames,
-   * and static::$extraColumns, but is stored separately to avoid repeated
-   * processing.
-   *
-   * @var array[]
-   */
-  protected $allColumns = array();
-
-  /**
-   * Constructs a DefaultTableMapping.
-   *
-   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $storage_definitions
-   *   A list of field storage definitions that should be available for the
-   *   field columns of this table mapping.
-   */
-  public function __construct(array $storage_definitions) {
-    $this->fieldStorageDefinitions = $storage_definitions;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getTableNames() {
-    return array_unique(array_merge(array_keys($this->fieldNames), array_keys($this->extraColumns)));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getAllColumns($table_name) {
-    if (!isset($this->allColumns[$table_name])) {
-      $this->allColumns[$table_name] = array();
-
-      foreach ($this->getFieldNames($table_name) as $field_name) {
-        $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], array_values($this->getColumnNames($field_name)));
-      }
-
-      $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], $this->getExtraColumns($table_name));
-    }
-    return $this->allColumns[$table_name];
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFieldNames($table_name) {
-    if (isset($this->fieldNames[$table_name])) {
-      return $this->fieldNames[$table_name];
-    }
-    return array();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getColumnNames($field_name) {
-    if (!isset($this->columnMapping[$field_name])) {
-      $column_names = array_keys($this->fieldStorageDefinitions[$field_name]->getColumns());
-      if (count($column_names) == 1) {
-        $this->columnMapping[$field_name] = array(reset($column_names) => $field_name);
-      }
-      else {
-        $this->columnMapping[$field_name] = array();
-        foreach ($column_names as $column_name) {
-          $this->columnMapping[$field_name][$column_name] = $field_name . '__' . $column_name;
-        }
-      }
-    }
-    return $this->columnMapping[$field_name];
-  }
-
-  /**
-   * Adds field columns for a table to the table mapping.
-   *
-   * @param string $table_name
-   *   The name of the table to add the field column for.
-   * @param string[] $field_names
-   *   A list of field names to add the columns for.
-   *
-   * @return $this
-   */
-  public function setFieldNames($table_name, array $field_names) {
-    $this->fieldNames[$table_name] = $field_names;
-    // Force the re-computation of the column list.
-    unset($this->allColumns[$table_name]);
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getExtraColumns($table_name) {
-    if (isset($this->extraColumns[$table_name])) {
-      return $this->extraColumns[$table_name];
-    }
-    return array();
-  }
-
-  /**
-   * Adds a extra columns for a table to the table mapping.
-   *
-   * @param string $table_name
-   *   The name of table to add the extra columns for.
-   * @param string[] $column_names
-   *   The list of column names.
-   *
-   * @return $this
-   */
-  public function setExtraColumns($table_name, array $column_names) {
-    $this->extraColumns[$table_name] = $column_names;
-    // Force the re-computation of the column list.
-    unset($this->allColumns[$table_name]);
-    return $this;
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/Sql/SqlEntityStorageInterface.php
deleted file mode 100644
index 8c4e3d8..0000000
--- a/core/lib/Drupal/Core/Entity/Sql/SqlEntityStorageInterface.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Sql\SqlEntityStorageInterface.
- */
-
-namespace Drupal\Core\Entity\Sql;
-
-use Drupal\Core\Entity\EntityStorageInterface;
-use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
-
-/**
- * A common interface for SQL-based storage controllers.
- */
-interface SqlEntityStorageInterface extends EntityStorageInterface, EntitySchemaProviderInterface {
-
-  /**
-   * Gets a table mapping for the entity's SQL tables.
-   *
-   * @return \Drupal\Core\Entity\Sql\TableMappingInterface
-   *   A table mapping object for the entity's tables.
-   */
-  public function getTableMapping();
-
-}
diff --git a/core/lib/Drupal/Core/Entity/Sql/TableMappingInterface.php b/core/lib/Drupal/Core/Entity/Sql/TableMappingInterface.php
deleted file mode 100644
index c6b5706..0000000
--- a/core/lib/Drupal/Core/Entity/Sql/TableMappingInterface.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Sql\TableMappingInterface.
- */
-
-namespace Drupal\Core\Entity\Sql;
-
-/**
- * Provides a common interface for mapping field columns to SQL tables.
- */
-interface TableMappingInterface {
-
-  /**
-   * Returns a list of table names for this mapping.
-   *
-   * @return string[]
-   *   An array of table names.
-   */
-  public function getTableNames();
-
-  /**
-   * Returns a list of all database columns for a given table.
-   *
-   * @param string $table_name
-   *   The name of the table to return the columns for.
-   *
-   * @return string[]
-   *   An array of database column names for this table. Both field columns and
-   *   extra columns are returned.
-   */
-  public function getAllColumns($table_name);
-
-  /**
-   * Returns a list of names of fields stored in the specified table.
-   *
-   * @param string $table_name
-   *   The name of the table to return the field names for.
-   *
-   * @return string[]
-   *   An array of field names for the given table.
-   */
-  public function getFieldNames($table_name);
-
-  /**
-   * Returns a mapping of field columns to database columns for a given field.
-   *
-   * @param string $field_name
-   *   The name of the entity field to return the column mapping for.
-   *
-   * @return string[]
-   *   The keys of this array are the keys of the array returned by
-   *   FieldStorageDefinitionInterface::getColumns() while the respective values
-   *   are the names of the database columns for this table mapping.
-   */
-  public function getColumnNames($field_name);
-
-  /**
-   * Returns a list of extra database columns, which store denormalized data.
-   *
-   * These database columns do not belong to any entity fields. Any normalized
-   * data that is stored should be associated with an entity field.
-   *
-   * @param string $table_name
-   *   The name of the table to return the columns for.
-   *
-   * @return string[]
-   *   An array of column names for the given table.
-   */
-  public function getExtraColumns($table_name);
-
-}
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index 89b47b3..140a124 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -11,7 +11,6 @@
 use Drupal\Component\Serialization\Yaml;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -797,22 +796,6 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
         }
         drupal_set_installed_schema_version($module, $version);
 
-        // Install any entity schemas belonging to the module.
-        $entity_manager = \Drupal::entityManager();
-        $schema = \Drupal::database()->schema();
-        foreach ($entity_manager->getDefinitions() as $entity_type) {
-          if ($entity_type->getProvider() == $module) {
-            $storage = $entity_manager->getStorage($entity_type->id());
-            if ($storage instanceof EntitySchemaProviderInterface) {
-              foreach ($storage->getSchema() as $table_name => $table_schema) {
-                if (!$schema->tableExists($table_name)) {
-                  $schema->createTable($table_name, $table_schema);
-                }
-              }
-            }
-          }
-        }
-
         // Record the fact that it was installed.
         $modules_installed[] = $module;
 
@@ -905,22 +888,6 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
       // Remove all configuration belonging to the module.
       \Drupal::service('config.manager')->uninstall('module', $module);
 
-      // Remove any entity schemas belonging to the module.
-      $entity_manager = \Drupal::entityManager();
-      $schema = \Drupal::database()->schema();
-      foreach ($entity_manager->getDefinitions() as $entity_type) {
-        if ($entity_type->getProvider() == $module) {
-          $storage = $entity_manager->getStorage($entity_type->id());
-          if ($storage instanceof EntitySchemaProviderInterface) {
-            foreach ($storage->getSchema() as $table_name => $table_schema) {
-              if ($schema->tableExists($table_name)) {
-                $schema->dropTable($table_name);
-              }
-            }
-          }
-        }
-      }
-
       // Remove the schema.
       drupal_uninstall_schema($module);
 
diff --git a/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php
index dbe2c78..8354bea 100644
--- a/core/lib/Drupal/Core/Extension/ThemeHandler.php
+++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php
@@ -626,6 +626,8 @@ public function getName($theme) {
     if (!isset($themes[$theme])) {
       throw new \InvalidArgumentException(String::format('Requested the name of a non-existing theme @theme', array('@theme' => $theme)));
     }
+    // @todo For testing please remove before commit.
+    return \Drupal::config('system.site')->get('name');
     return String::checkPlain($themes[$theme]->info['name']);
   }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
index 7a02254..7fb16ab 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/EntityReferenceItem.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\Core\Field\Plugin\Field\FieldType;
 
-use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\TypedData\EntityDataDefinition;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldItemBase;
@@ -115,9 +114,7 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
         'target_id' => array(
           'description' => 'The ID of the target entity.',
           'type' => 'varchar',
-          // If the target entities act as bundles for another entity type,
-          // their IDs should not exceed the maximum length for bundles.
-          'length' => $target_type_info->getBundleOf() ? EntityTypeInterface::BUNDLE_MAX_LENGTH : 255,
+          'length' => '255',
         ),
       );
     }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
index 2d3fa30..2bafa8b 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/IntegerItem.php
@@ -41,9 +41,6 @@ public static function defaultInstanceSettings() {
       'max' => '',
       'prefix' => '',
       'suffix' => '',
-      // Valid size property values include: 'tiny', 'small', 'medium', 'normal'
-      // and 'big'.
-      'size' => 'normal',
     ) + parent::defaultInstanceSettings();
   }
 
@@ -94,9 +91,6 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
           'not null' => FALSE,
           // Expose the 'unsigned' setting in the field item schema.
           'unsigned' => $field_definition->getSetting('unsigned'),
-          // Expose the 'size' setting in the field item schema. For instance,
-          // supply 'big' as a value to produce a 'bigint' type.
-          'size' => $field_definition->getSetting('size'),
         ),
       ),
     );
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index dbef995..693e4ce 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -52,8 +52,7 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
     return array(
       'columns' => array(
         'value' => array(
-          'type' => 'varchar',
-          'length' => (int) $field_definition->getSetting('max_length'),
+          'type' => 'text',
           'not null' => TRUE,
         ),
       ),
diff --git a/core/lib/Drupal/Core/Form/OptGroup.php b/core/lib/Drupal/Core/Form/OptGroup.php
index f94d62d..21e9855 100644
--- a/core/lib/Drupal/Core/Form/OptGroup.php
+++ b/core/lib/Drupal/Core/Form/OptGroup.php
@@ -43,7 +43,7 @@ public static function flattenOptions(array $array) {
    */
   protected static function doFlattenOptions(array $array, array &$options) {
     foreach ($array as $key => $value) {
-      if (is_object($value)) {
+      if (is_object($value) && isset($value->option)) {
         static::doFlattenOptions($value->option, $options);
       }
       elseif (is_array($value)) {
diff --git a/core/lib/Drupal/Core/Page/HeadElement.php b/core/lib/Drupal/Core/Page/HeadElement.php
index 85055ad..70cf2b7 100644
--- a/core/lib/Drupal/Core/Page/HeadElement.php
+++ b/core/lib/Drupal/Core/Page/HeadElement.php
@@ -8,11 +8,12 @@
 namespace Drupal\Core\Page;
 
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\SafeMarkup;
 
 /**
  * This class represents an HTML element that appears in the HEAD tag.
  */
-class HeadElement {
+class HeadElement extends SafeMarkup {
 
   /**
    * An array of attributes for this element.
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
index bd1b3b3..c34c4c0 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\Core\Template\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 new SafeMarkup($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 new SafeMarkup($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 new SafeMarkup($return);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php
index ead5d05..7432b6d 100644
--- a/core/lib/Drupal/Core/Template/Attribute.php
+++ b/core/lib/Drupal/Core/Template/Attribute.php
@@ -31,7 +31,7 @@
  *  // Produces <cat class="cat black-cat white-cat black-white-cat" id="socks">
  * @endcode
  */
-class Attribute implements \ArrayAccess, \IteratorAggregate {
+class Attribute extends \Twig_Markup implements \ArrayAccess, \IteratorAggregate {
 
   /**
    * Stores the attribute data.
@@ -86,9 +86,12 @@ protected function createAttributeValue($name, $value) {
     elseif (is_bool($value)) {
       $value = new AttributeBoolean($name, $value);
     }
-    elseif (!is_object($value)) {
+    elseif (!is_object($value) || $value instanceof SafeMarkup) {
       $value = new AttributeString($name, $value);
     }
+    elseif (!method_exists($value, 'render')) {
+      throw new \Exception('boo!');
+    }
     return $value;
   }
 
diff --git a/core/lib/Drupal/Core/Template/AttributeArray.php b/core/lib/Drupal/Core/Template/AttributeArray.php
index 95e0ef3..4d8145f 100644
--- a/core/lib/Drupal/Core/Template/AttributeArray.php
+++ b/core/lib/Drupal/Core/Template/AttributeArray.php
@@ -66,7 +66,7 @@ public function offsetExists($offset) {
    * Implements the magic __toString() method.
    */
   public function __toString() {
-    return String::checkPlain(implode(' ', $this->value));
+    return (string) String::checkPlain(implode(' ', $this->value));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Template/AttributeBoolean.php b/core/lib/Drupal/Core/Template/AttributeBoolean.php
index 4e9ea67..b5a0957 100644
--- a/core/lib/Drupal/Core/Template/AttributeBoolean.php
+++ b/core/lib/Drupal/Core/Template/AttributeBoolean.php
@@ -42,7 +42,7 @@ public function render() {
    * Implements the magic __toString() method.
    */
   public function __toString() {
-    return $this->value === FALSE ? '' : String::checkPlain($this->name);
+    return $this->value === FALSE ? '' : (string) String::checkPlain($this->name);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Template/AttributeString.php b/core/lib/Drupal/Core/Template/AttributeString.php
index 07211be..e1c69dc 100644
--- a/core/lib/Drupal/Core/Template/AttributeString.php
+++ b/core/lib/Drupal/Core/Template/AttributeString.php
@@ -30,7 +30,7 @@ class AttributeString extends AttributeValueBase {
    * Implements the magic __toString() method.
    */
   public function __toString() {
-    return String::checkPlain($this->value);
+    return (string) String::checkPlain($this->value);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Template/SafeMarkup.php b/core/lib/Drupal/Core/Template/SafeMarkup.php
new file mode 100644
index 0000000..58d85c4
--- /dev/null
+++ b/core/lib/Drupal/Core/Template/SafeMarkup.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Template\SafeMarkup.
+ */
+
+namespace Drupal\Core\Template;
+use Drupal\Component\Utility\String;
+
+/**
+ * A class marking strings as already escaped for XSS purposes.
+ */
+class SafeMarkup extends \Twig_Markup {
+
+  /**
+   * Constructs a SafeMarkup object.
+   *
+   * @param string $content
+   *   The callback function name.
+   */
+  public function __construct($content) {
+    parent::__construct((string) $content, 'utf-8');
+  }
+
+  /**
+   * @todo.
+   *
+   * @return \Drupal\Core\Template\SafeMarkup
+   */
+  public static function concat() {
+    return SafeMarkup::implode('', func_get_args());
+  }
+
+  /**
+   * @todo.
+   *
+   * @return \Drupal\Core\Template\SafeMarkup
+   */
+  public static function implode($delimiter, array $array) {
+    foreach ($array as $key => $string) {
+      if (!$string instanceof SafeMarkup) {
+        $array[$key] = String::checkPlain($string);
+      }
+    }
+    return new SafeMarkup(implode($delimiter, $array));
+  }
+
+  public static function strReplace($search, $replace, $subject, &$count = NULL) {
+    $safe = $subject instanceof SafeMarkup;
+    $replacement = str_replace($search, $replace, $subject, $count);
+    return $safe ? new SafeMarkup($replacement) : $replacement;
+  }
+
+  /**
+   * Renders the markup.
+   *
+   * @return string
+   *   The results of the callback function.
+   */
+  public function render() {
+    return $this->__toString();
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index b547d95..231f3aa 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\Core\Template\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 new SafeMarkup('<a href="' . $url . '"' . $attributes . '>' . $text . '</a>');
   }
 
   /**
diff --git a/core/modules/action/action.views_execution.inc b/core/modules/action/action.views_execution.inc
index 32bc883..66d18bf 100644
--- a/core/modules/action/action.views_execution.inc
+++ b/core/modules/action/action.views_execution.inc
@@ -19,6 +19,6 @@ function action_views_form_substitutions() {
     '#attributes' => array('class' => array('action-table-select-all')),
   );
   return array(
-    $select_all_placeholder => drupal_render($select_all),
+    (string) $select_all_placeholder => drupal_render($select_all),
   );
 }
diff --git a/core/modules/aggregator/aggregator.install b/core/modules/aggregator/aggregator.install
index 2b755e1..e4bef0e 100644
--- a/core/modules/aggregator/aggregator.install
+++ b/core/modules/aggregator/aggregator.install
@@ -22,3 +22,179 @@ function aggregator_requirements($phase) {
   }
   return $requirements;
 }
+
+/**
+ * Implements hook_schema().
+ */
+function aggregator_schema() {
+  $schema['aggregator_feed'] = array(
+    'description' => 'Stores feeds to be parsed by the aggregator.',
+    'fields' => array(
+      'fid' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique feed ID.',
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Title of the feed.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this feed.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'url' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'description' => 'URL to the feed.',
+      ),
+      'refresh' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'How often to check for new feed items, in seconds.',
+      ),
+      'checked' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Last time feed was checked for new items, as Unix timestamp.',
+      ),
+      'queued' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Time when this feed was queued for refresh, 0 if not queued.',
+      ),
+      'link' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'description' => 'The parent website of the feed; comes from the <link> element in the feed.',
+      ),
+      'description' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'big',
+        'description' => "The parent website's description; comes from the <description> element in the feed.",
+      ),
+      'image' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'big',
+        'description' => 'An image representing the feed.',
+      ),
+      'hash' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Calculated hash of the feed data, used for validating cache.',
+      ),
+      'etag' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Entity tag HTTP response header, used for validating cache.',
+      ),
+      'modified' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'When the feed was last modified, as a Unix timestamp.',
+      ),
+    ),
+    'primary key' => array('fid'),
+    'indexes' => array(
+      'url'  => array(array('url', 255)),
+      'queued' => array('queued'),
+    ),
+    'unique keys' => array(
+      'title' => array('title'),
+    ),
+  );
+
+  $schema['aggregator_item'] = array(
+    'description' => 'Stores the individual items imported from feeds.',
+    'fields' => array(
+      'iid'  => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique ID for feed item.',
+      ),
+      'fid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {aggregator_feed}.fid to which this item belongs.',
+      ),
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Title of the feed item.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this feed item.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'link' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'description' => 'Link to the feed item.',
+      ),
+      'author' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Author of the feed item.',
+      ),
+      'description' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'big',
+        'description' => 'Body of the feed item.',
+      ),
+      'timestamp' => array(
+        'type' => 'int',
+        'not null' => FALSE,
+        'description' => 'Posted date of the feed item, as a Unix timestamp.',
+      ),
+      'guid' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+        'description' => 'Unique identifier for the feed item.',
+      )
+    ),
+    'primary key' => array('iid'),
+    'indexes' => array(
+      'fid' => array('fid'),
+      'timestamp' => array('timestamp'),
+    ),
+    'foreign keys' => array(
+      'aggregator_feed' => array(
+        'table' => 'aggregator_feed',
+        'columns' => array('fid' => 'fid'),
+      ),
+    ),
+  );
+
+  return $schema;
+}
diff --git a/core/modules/aggregator/src/Entity/Feed.php b/core/modules/aggregator/src/Entity/Feed.php
index 4222152..24430b9 100644
--- a/core/modules/aggregator/src/Entity/Feed.php
+++ b/core/modules/aggregator/src/Entity/Feed.php
@@ -153,6 +153,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('URL'))
       ->setDescription(t('The fully-qualified URL of the feed.'))
       ->setRequired(TRUE)
+      ->setSetting('max_length', NULL)
       ->setDisplayOptions('form', array(
         'type' => 'uri',
         'weight' => -3,
@@ -175,13 +176,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['checked'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Checked'))
-      ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'))
-      ->setSetting('default_value', 0);
+      ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'));
 
     $fields['queued'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Queued'))
-      ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.'))
-      ->setSetting('default_value', 0);
+      ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.'));
 
     $fields['link'] = FieldDefinition::create('uri')
       ->setLabel(t('Link'))
diff --git a/core/modules/aggregator/src/Entity/Item.php b/core/modules/aggregator/src/Entity/Item.php
index 162bb19..bb9e0aa 100644
--- a/core/modules/aggregator/src/Entity/Item.php
+++ b/core/modules/aggregator/src/Entity/Item.php
@@ -71,7 +71,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Author'))
       ->setDescription(t('The author of the feed item.'));
 
-    $fields['description'] = FieldDefinition::create('string_long')
+    // @todo Convert to a text field in https://drupal.org/node/2149845.
+    $fields['description'] = FieldDefinition::create('string')
       ->setLabel(t('Description'))
       ->setDescription(t('The body of the feed item.'));
 
@@ -80,7 +81,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDescription(t('Posted date of the feed item, as a Unix timestamp.'));
 
     // @todo Convert to a real UUID field in https://drupal.org/node/2149851.
-    $fields['guid'] = FieldDefinition::create('string_long')
+    $fields['guid'] = FieldDefinition::create('string')
       ->setLabel(t('GUID'))
       ->setDescription(t('Unique identifier for the feed item.'));
 
diff --git a/core/modules/aggregator/src/FeedStorage.php b/core/modules/aggregator/src/FeedStorage.php
index da784f5..5824240 100644
--- a/core/modules/aggregator/src/FeedStorage.php
+++ b/core/modules/aggregator/src/FeedStorage.php
@@ -21,29 +21,6 @@ class FeedStorage extends ContentEntityDatabaseStorage implements FeedStorageInt
   /**
    * {@inheritdoc}
    */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['aggregator_feed']['fields']['url']['not null'] = TRUE;
-    $schema['aggregator_feed']['fields']['queued']['not null'] = TRUE;
-    $schema['aggregator_feed']['fields']['title']['not null'] = TRUE;
-
-    $schema['aggregator_feed']['indexes'] += array(
-      'aggregator_feed__url'  => array(array('url', 255)),
-      'aggregator_feed__queued' => array('queued'),
-    );
-    $schema['aggregator_feed']['unique keys'] += array(
-      'aggregator_feed__title' => array('title'),
-    );
-
-    return $schema;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getFeedDuplicates(FeedInterface $feed) {
     $query = \Drupal::entityQuery('aggregator_feed');
 
diff --git a/core/modules/aggregator/src/ItemStorage.php b/core/modules/aggregator/src/ItemStorage.php
index f2b4aa6..bedea41 100644
--- a/core/modules/aggregator/src/ItemStorage.php
+++ b/core/modules/aggregator/src/ItemStorage.php
@@ -22,29 +22,6 @@ class ItemStorage extends ContentEntityDatabaseStorage implements ItemStorageInt
   /**
    * {@inheritdoc}
    */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['aggregator_item']['fields']['timestamp']['not null'] = TRUE;
-
-    $schema['aggregator_item']['indexes'] += array(
-      'aggregator_item__timestamp' => array('timestamp'),
-    );
-    $schema['aggregator_item']['foreign keys'] += array(
-      'aggregator_item__aggregator_feed' => array(
-        'table' => 'aggregator_feed',
-        'columns' => array('fid' => 'fid'),
-      ),
-    );
-
-    return $schema;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getItemCount(FeedInterface $feed) {
     $query = \Drupal::entityQuery('aggregator_item')
       ->condition('fid', $feed->id())
diff --git a/core/modules/aggregator/src/Tests/FeedParserTest.php b/core/modules/aggregator/src/Tests/FeedParserTest.php
index 5d4199f..96ee61f 100644
--- a/core/modules/aggregator/src/Tests/FeedParserTest.php
+++ b/core/modules/aggregator/src/Tests/FeedParserTest.php
@@ -87,7 +87,7 @@ function testHtmlEntitiesSample() {
   function testRedirectFeed() {
     // Simulate a typo in the URL to force a curl exception.
     $invalid_url = url('aggregator/redirect', array('absolute' => TRUE));
-    $feed = entity_create('aggregator_feed', array('url' => $invalid_url, 'title' => $this->randomName()));
+    $feed = entity_create('aggregator_feed', array('url' => $invalid_url));
     $feed->save();
     $feed->refreshItems();
 
diff --git a/core/modules/aggregator/src/Tests/Views/IntegrationTest.php b/core/modules/aggregator/src/Tests/Views/IntegrationTest.php
index bd8f17c..3f4d7b5 100644
--- a/core/modules/aggregator/src/Tests/Views/IntegrationTest.php
+++ b/core/modules/aggregator/src/Tests/Views/IntegrationTest.php
@@ -21,7 +21,7 @@ class IntegrationTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'entity', 'field', 'options');
+  public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'entity', 'field');
 
   /**
    * Views used by this test.
@@ -55,8 +55,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('aggregator_item');
-    $this->installEntitySchema('aggregator_feed');
+    $this->installSchema('aggregator', array('aggregator_item', 'aggregator_feed'));
 
     ViewTestData::createTestViews(get_class($this), array('aggregator_test_views'));
 
diff --git a/core/modules/block/custom_block/custom_block.install b/core/modules/block/custom_block/custom_block.install
new file mode 100644
index 0000000..6f85a6a
--- /dev/null
+++ b/core/modules/block/custom_block/custom_block.install
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the custom block module.
+ */
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Implements hook_schema().
+ */
+function custom_block_schema() {
+  $schema = array();
+  $schema['custom_block'] = array(
+    'description' => 'Stores contents of custom-made blocks.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => "The block's {custom_block}.id.",
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'info' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Block description.',
+      ),
+      // Defaults to NULL in order to avoid a brief period of potential
+      // deadlocks on the index.
+      'revision_id' => array(
+        'description' => 'The current {block_custom_revision}.revision_id version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+      ),
+      'type' => array(
+        'description' => 'The type of this custom block.',
+        'type' => 'varchar',
+        'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the custom block was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this node.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'indexes' => array(
+      'block_custom_type' => array('type'),
+    ),
+    'unique keys' => array(
+      'revision_id' => array('revision_id'),
+      'uuid' => array('uuid'),
+      'info' => array('info'),
+    ),
+    'foreign keys' => array(
+      'custom_block_revision' => array(
+        'table' => 'custom_block_revision',
+        'columns' => array('revision_id' => 'revision_id'),
+      ),
+    ),
+  );
+
+  $schema['custom_block_revision'] = array(
+    'description' => 'Stores contents of custom-made blocks.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => "The block's {custom_block}.id.",
+      ),
+      // Defaults to NULL in order to avoid a brief period of potential
+      // deadlocks on the index.
+      'revision_id' => array(
+        'description' => 'The current version identifier.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'log' => array(
+        'description' => 'The log entry explaining the changes in this version.',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'big',
+      ),
+      'info' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Block description.',
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the version was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('revision_id'),
+  );
+  return $schema;
+}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorage.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorage.php
deleted file mode 100644
index b7b8711..0000000
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorage.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockStorage.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\ContentEntityDatabaseStorage;
-
-/**
- * Provides storage for the 'custom_block' entity type.
- */
-class CustomBlockStorage extends ContentEntityDatabaseStorage {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['custom_block']['fields']['info']['not null'] = TRUE;
-
-    $schema['custom_block']['unique keys'] += array(
-      'custom_block__info' => array('info'),
-    );
-
-    return $schema;
-  }
-
-}
diff --git a/core/modules/block/custom_block/src/Entity/CustomBlock.php b/core/modules/block/custom_block/src/Entity/CustomBlock.php
index 585026e..71913b7 100644
--- a/core/modules/block/custom_block/src/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/src/Entity/CustomBlock.php
@@ -21,7 +21,6 @@
  *   label = @Translation("Custom Block"),
  *   bundle_label = @Translation("Custom Block type"),
  *   controllers = {
- *     "storage" = "Drupal\custom_block\CustomBlockStorage",
  *     "access" = "Drupal\custom_block\CustomBlockAccessController",
  *     "list_builder" = "Drupal\custom_block\CustomBlockListBuilder",
  *     "view_builder" = "Drupal\custom_block\CustomBlockViewBuilder",
@@ -187,7 +186,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setSetting('target_type', 'custom_block_type')
       ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH);
 
-    $fields['log'] = FieldDefinition::create('string_long')
+    $fields['log'] = FieldDefinition::create('string')
       ->setLabel(t('Revision log message'))
       ->setDescription(t('The revision log message.'))
       ->setRevisionable(TRUE);
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 0c35aae..3bfbcd0 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -32,6 +32,152 @@ function comment_install() {
  * Implements hook_schema().
  */
 function comment_schema() {
+  $schema['comment'] = array(
+    'description' => 'Stores comments and associated data.',
+    'fields' => array(
+      'cid' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique comment ID.',
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'pid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {comment}.cid to which this comment is a reply. If set to 0, this comment is not a reply to an existing comment.',
+      ),
+      'entity_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The entity_id of the entity to which this comment is a reply.',
+      ),
+      'entity_type' => array(
+        'type' => 'varchar',
+        'not null' => TRUE,
+        'default' => 'node',
+        'length' => 255,
+        'description' => 'The entity_type of the entity to which this comment is a reply.',
+      ),
+      'field_id' => array(
+        'type' => 'varchar',
+        'not null' => TRUE,
+        'default' => 'node.comment',
+        'length' => 255,
+        'description' => 'The field_id of the field that was used to add this comment.',
+      ),
+      'uid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {users}.uid who authored the comment. If set to 0, this comment was created by an anonymous user.',
+      ),
+      'subject' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The comment title.',
+      ),
+      'hostname' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "The author's host name.",
+      ),
+      'created' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The time that the comment was created, as a Unix timestamp.',
+      ),
+      'changed' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The time that the comment was last edited, as a Unix timestamp.',
+      ),
+      'status' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 1,
+        'size' => 'tiny',
+        'description' => 'The published status of a comment. (0 = Not Published, 1 = Published)',
+      ),
+      'thread' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'description' => "The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length.",
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 60,
+        'not null' => FALSE,
+        'description' => "The comment author's name. Uses {users}.name if the user is logged in, otherwise uses the value typed into the comment form.",
+      ),
+      'mail' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => FALSE,
+        'description' => "The comment author's e-mail address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.",
+      ),
+      'homepage' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+        'description' => "The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.",
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this comment.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      'comment_status_pid' => array('pid', 'status'),
+      'comment_num_new' => array(
+        'entity_id',
+        array('entity_type', 32),
+        array('field_id', 32),
+        'status',
+        'created',
+        'cid',
+        'thread',
+      ),
+      'comment_uid' => array('uid'),
+      'comment_entity_langcode' => array(
+        'entity_id',
+        array('entity_type', 32),
+        array('field_id', 32),
+        'langcode',
+      ),
+      'comment_created' => array('created'),
+    ),
+    'primary key' => array('cid'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+    'foreign keys' => array(
+      'comment_author' => array(
+        'table' => 'users',
+        'columns' => array('uid' => 'uid'),
+      ),
+    ),
+  );
+
   $schema['comment_entity_statistics'] = array(
     'description' => 'Maintains statistics of entity and comments posts to show "new" and "updated" flags.',
     'fields' => array(
diff --git a/core/modules/comment/src/CommentStorage.php b/core/modules/comment/src/CommentStorage.php
index 5526d2b..88a1bd4 100644
--- a/core/modules/comment/src/CommentStorage.php
+++ b/core/modules/comment/src/CommentStorage.php
@@ -127,50 +127,4 @@ public function getChildCids(array $comments) {
       ->fetchCol();
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['comment']['fields']['pid']['not null'] = TRUE;
-    $schema['comment']['fields']['status']['not null'] = TRUE;
-    $schema['comment']['fields']['entity_id']['not null'] = TRUE;
-    $schema['comment']['fields']['field_id']['not null'] = TRUE;
-    $schema['comment']['fields']['created']['not null'] = TRUE;
-    $schema['comment']['fields']['thread']['not null'] = TRUE;
-
-    unset($schema['comment']['indexes']['field__pid']);
-    unset($schema['comment']['indexes']['field__entity_id']);
-    $schema['comment']['indexes'] += array(
-      'comment__status_pid' => array('pid', 'status'),
-      'comment__num_new' => array(
-        'entity_id',
-        array('entity_type', 32),
-        array('field_id', 32),
-        'status',
-        'created',
-        'cid',
-        'thread',
-      ),
-      'comment__entity_langcode' => array(
-        'entity_id',
-        array('entity_type', 32),
-        array('field_id', 32),
-        'langcode',
-      ),
-      'comment__created' => array('created'),
-    );
-    $schema['comment']['foreign keys'] += array(
-      'comment__author' => array(
-        'table' => 'users',
-        'columns' => array('uid' => 'uid'),
-      ),
-    );
-
-    return $schema;
-  }
-
 }
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index 9b4707f..de10346 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -273,8 +273,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['status'] = FieldDefinition::create('boolean')
       ->setLabel(t('Publishing status'))
-      ->setDescription(t('A boolean indicating whether the comment is published.'))
-      ->setSetting('default_value', TRUE);
+      ->setDescription(t('A boolean indicating whether the comment is published.'));
 
     $fields['thread'] = FieldDefinition::create('string')
       ->setLabel(t('Thread place'))
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index ff8c05a..3fe2a3f 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -47,8 +47,7 @@ public function setUp() {
     $current_user->setAccount($this->createUser(array(), array('access comments')));
 
     // Install tables and config needed to render comments.
-    $this->installSchema('comment', array('comment_entity_statistics'));
-    $this->installEntitySchema('comment');
+    $this->installSchema('comment', array('comment', 'comment_entity_statistics'));
     $this->installConfig(array('system', 'filter'));
 
     // Comment rendering generates links, so build the router.
diff --git a/core/modules/comment/src/Tests/CommentValidationTest.php b/core/modules/comment/src/Tests/CommentValidationTest.php
index b56ed6b..03a0e82 100644
--- a/core/modules/comment/src/Tests/CommentValidationTest.php
+++ b/core/modules/comment/src/Tests/CommentValidationTest.php
@@ -38,8 +38,7 @@ public static function getInfo() {
    */
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('comment');
+    $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision'));
     $this->installSchema('comment', array('comment_entity_statistics'));
   }
 
diff --git a/core/modules/config/src/Tests/ConfigImportRecreateTest.php b/core/modules/config/src/Tests/ConfigImportRecreateTest.php
index 9afec8c..3ce6b12 100644
--- a/core/modules/config/src/Tests/ConfigImportRecreateTest.php
+++ b/core/modules/config/src/Tests/ConfigImportRecreateTest.php
@@ -29,7 +29,7 @@ class ConfigImportRecreateTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'entity', 'field', 'text', 'user', 'node');
+  public static $modules = array('system', 'entity', 'field', 'text', 'node');
 
   public static function getInfo() {
     return array(
@@ -42,7 +42,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('node');
+    $this->installSchema('node', 'node');
 
     $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
 
diff --git a/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php b/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php
index 7e8024a..fbffae7 100644
--- a/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php
+++ b/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php
@@ -32,7 +32,7 @@ class ConfigImportRenameValidationTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'user', 'node', 'field', 'text', 'entity', 'config_test');
+  public static $modules = array('system', 'node', 'field', 'text', 'entity', 'config_test');
 
   /**
    * {@inheritdoc}
@@ -51,8 +51,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('user');
-    $this->installEntitySchema('node');
+    $this->installSchema('node', 'node');
 
     // Set up the ConfigImporter object for testing.
     $storage_comparer = new StorageComparer(
diff --git a/core/modules/contextual/src/ContextualController.php b/core/modules/contextual/src/ContextualController.php
index aeee4a6..fcc47de 100644
--- a/core/modules/contextual/src/ContextualController.php
+++ b/core/modules/contextual/src/ContextualController.php
@@ -44,7 +44,10 @@ public function render(Request $request) {
         '#type' => 'contextual_links',
         '#contextual_links' => _contextual_id_to_links($id),
       );
-      $rendered[$id] = drupal_render($element);
+      // Cast any returned safe markup to string so that it is created
+      // as correct JSON.
+      // @todo Maybe check safeness and checkplain the results?
+      $rendered[$id] = (string) drupal_render($element);
     }
 
     return new JsonResponse($rendered);
diff --git a/core/modules/editor/src/Tests/EditorFileUsageTest.php b/core/modules/editor/src/Tests/EditorFileUsageTest.php
index 184d849..3be50ec 100644
--- a/core/modules/editor/src/Tests/EditorFileUsageTest.php
+++ b/core/modules/editor/src/Tests/EditorFileUsageTest.php
@@ -31,10 +31,8 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('file');
-    $this->installSchema('node', array('node_access'));
-    $this->installSchema('file', array('file_usage'));
+    $this->installSchema('node', array('node', 'node_access', 'node_field_data', 'node_field_revision', 'node_revision'));
+    $this->installSchema('file', array('file_managed', 'file_usage'));
 
     // Add text formats.
     $filtered_html_format = entity_create('filter_format', array(
diff --git a/core/modules/editor/tests/src/EditorXssFilter/StandardTest.php b/core/modules/editor/tests/src/EditorXssFilter/StandardTest.php
index 7927eef..cc3a884 100644
--- a/core/modules/editor/tests/src/EditorXssFilter/StandardTest.php
+++ b/core/modules/editor/tests/src/EditorXssFilter/StandardTest.php
@@ -551,7 +551,7 @@ public function providerTestFilterXss() {
    */
   public function testFilterXss($input, $expected_output) {
     $output = call_user_func($this->editorXssFilterClass . '::filterXss', $input, $this->format);
-    $this->assertSame($expected_output, $output);
+    $this->assertSame($expected_output, (string) $output);
   }
 
 }
diff --git a/core/modules/entity/src/Tests/EntityDisplayTest.php b/core/modules/entity/src/Tests/EntityDisplayTest.php
index 250f5e0..77745f2 100644
--- a/core/modules/entity/src/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/src/Tests/EntityDisplayTest.php
@@ -268,7 +268,7 @@ public function testBaseFieldComponent() {
    */
   public function testRenameDeleteBundle() {
     $this->enableModules(array('field_test', 'node', 'system', 'text'));
-    $this->installEntitySchema('node');
+    $this->installSchema('node', array('node'));
 
     // Create a node bundle, display and form display object.
     entity_create('node_type', array('type' => 'article'))->save();
diff --git a/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php b/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php
index e5ad209..9a5b732 100644
--- a/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php
+++ b/core/modules/entity_reference/src/ConfigurableEntityReferenceItem.php
@@ -90,7 +90,7 @@ public function getSettableOptions(AccountInterface $account = NULL) {
     $return = array();
     foreach ($options as $bundle => $entity_ids) {
       $bundle_label = String::checkPlain($bundles[$bundle]['label']);
-      $return[$bundle_label] = $entity_ids;
+      $return[(string) $bundle_label] = $entity_ids;
     }
 
     return count($return) == 1 ? reset($return) : $return;
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceFieldTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceFieldTest.php
index b3905bc..bde0b2c 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceFieldTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceFieldTest.php
@@ -76,7 +76,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('entity_test_rev');
+    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
 
     // Setup a field and instance.
     entity_reference_create_instance(
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/src/Tests/EntityReferenceItemTest.php
index 0586e7c..8c33d47 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceItemTest.php
+++ b/core/modules/entity_reference/src/Tests/EntityReferenceItemTest.php
@@ -52,7 +52,8 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', 'taxonomy_term_data');
+    $this->installSchema('taxonomy', 'taxonomy_term_hierarchy');
 
     $this->vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index fc6ec9e..507897d 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -9,6 +9,7 @@
 use Drupal\Core\Config\ConfigImporter;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Extension\Extension;
+use Drupal\Core\Template\SafeMarkup;
 use Symfony\Component\HttpFoundation\Request;
 
 /*
@@ -266,7 +267,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 new SafeMarkup(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 ec794fd..3737791 100644
--- a/core/modules/field/src/Plugin/views/field/Field.php
+++ b/core/modules/field/src/Plugin/views/field/Field.php
@@ -7,12 +7,14 @@
 
 namespace Drupal\field\Plugin\views\field;
 
+use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Field\FieldDefinition;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FormatterPluginManager;
@@ -685,11 +687,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 new SafeMarkup(implode(Xss::filterAdmin($this->options['separator']), $items));
       }
       else {
         $item_list = array(
diff --git a/core/modules/field/src/Tests/FieldAttachOtherTest.php b/core/modules/field/src/Tests/FieldAttachOtherTest.php
index 31ba143..a748939 100644
--- a/core/modules/field/src/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/src/Tests/FieldAttachOtherTest.php
@@ -38,7 +38,7 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('entity_test_rev');
+    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
     $this->createFieldWithInstance();
   }
 
diff --git a/core/modules/field/src/Tests/FieldAttachStorageTest.php b/core/modules/field/src/Tests/FieldAttachStorageTest.php
index 129a58f..ec805f2 100644
--- a/core/modules/field/src/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/src/Tests/FieldAttachStorageTest.php
@@ -37,7 +37,7 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('entity_test_rev');
+    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
   }
 
   /**
diff --git a/core/modules/field/src/Tests/FieldUnitTestBase.php b/core/modules/field/src/Tests/FieldUnitTestBase.php
index 457a09f..5717a23 100644
--- a/core/modules/field/src/Tests/FieldUnitTestBase.php
+++ b/core/modules/field/src/Tests/FieldUnitTestBase.php
@@ -36,9 +36,9 @@
    */
   function setUp() {
     parent::setUp();
-    $this->installEntitySchema('entity_test');
-    $this->installEntitySchema('user');
+    $this->installSchema('entity_test', 'entity_test');
     $this->installSchema('system', array('sequences'));
+    $this->installSchema('user', array('users', 'users_roles'));
 
     // Set default storage backend and configure the theme system.
     $this->installConfig(array('field', 'system'));
diff --git a/core/modules/field/src/Tests/TranslationWebTest.php b/core/modules/field/src/Tests/TranslationWebTest.php
index fb52e72..ba00c74 100644
--- a/core/modules/field/src/Tests/TranslationWebTest.php
+++ b/core/modules/field/src/Tests/TranslationWebTest.php
@@ -33,7 +33,7 @@ class TranslationWebTest extends FieldTestBase {
    *
    * @var string
    */
-  protected $entity_type = 'entity_test_mulrev';
+  protected $entity_type = 'entity_test_rev';
 
   /**
    * The field to use in this test.
@@ -78,7 +78,7 @@ function setUp() {
       'bundle' => $this->entity_type,
     );
     entity_create('field_instance_config', $instance)->save();
-    $this->instance = entity_load('field_instance_config', $this->entity_type . '.' . $instance['bundle'] . '.' . $this->field_name);
+    $this->instance = entity_load('field_instance_config', 'entity_test.' . $instance['bundle'] . '.' . $this->field_name);
 
     entity_get_form_display($this->entity_type, $this->entity_type, 'default')
       ->setComponent($this->field_name)
diff --git a/core/modules/field_ui/src/DisplayOverviewBase.php b/core/modules/field_ui/src/DisplayOverviewBase.php
index 2a6f46f..29ea754 100644
--- a/core/modules/field_ui/src/DisplayOverviewBase.php
+++ b/core/modules/field_ui/src/DisplayOverviewBase.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Template\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -398,7 +399,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..91f8d45 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\Core\Template\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/file/file.field.inc b/core/modules/file/file.field.inc
index f9aa9f9..11b071b 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -8,6 +8,7 @@
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Template\SafeMarkup;
 
 /**
  * Returns HTML for an individual file upload widget.
@@ -125,7 +126,7 @@ function theme_file_widget_multiple($variables) {
       $row[] = $display;
     }
     $row[] = $weight;
-    $row[] = $operations;
+    $row[] = new SafeMarkup($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.install b/core/modules/file/file.install
index 8d13c03..6bbed79 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -9,6 +9,105 @@
  * Implements hook_schema().
  */
 function file_schema() {
+  $schema['file_managed'] = array(
+    'description' => 'Stores information for uploaded files.',
+    'fields' => array(
+      'fid' => array(
+        'description' => 'File ID.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'uid' => array(
+        'description' => 'The {users}.uid of the user who is associated with the file.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'filename' => array(
+        'description' => 'Name of the file with no path components. This may differ from the basename of the URI if the file is renamed to avoid overwriting an existing file.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'uri' => array(
+        'description' => 'The URI to access the file (either local or remote).',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'binary' => TRUE,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this file.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'filemime' => array(
+        'description' => "The file's MIME type.",
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'filesize' => array(
+        'description' => 'The size of the file in bytes.',
+        'type' => 'int',
+        'size' => 'big',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'status' => array(
+        'description' => 'A field indicating the status of the file. Two status are defined in core: temporary (0) and permanent (1). Temporary files older than system.file.temporary_maximum_age will be removed during a cron run.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+      ),
+      'created' => array(
+        'description' => 'UNIX timestamp for when the file added.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'changed' => array(
+        'description' => 'UNIX timestamp for when the file was last changed.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'uid' => array('uid'),
+      'status' => array('status'),
+      'changed' => array('changed'),
+    ),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+      'uri' => array('uri'),
+    ),
+    'primary key' => array('fid'),
+    'foreign keys' => array(
+      'file_owner' => array(
+        'table' => 'users',
+        'columns' => array('uid' => 'uid'),
+      ),
+    ),
+  );
+
   $schema['file_usage'] = array(
     'description' => 'Track where a file is used.',
     'fields' => array(
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 47b72ef..4c41b78 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -7,6 +7,7 @@
 
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\file\Entity\File;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\Unicode;
@@ -943,10 +944,10 @@ function file_save_upload($form_field_name, $validators = array(), $destination
           '#theme' => 'item_list',
           '#items' => $errors,
         );
-        $message .= drupal_render($item_list);
+        $message = SafeMarkup::concat($message, drupal_render($item_list));
       }
       else {
-        $message .= ' ' . array_pop($errors);
+        $message = SafeMarkup::concat($message, ' ', array_pop($errors));
       }
       drupal_set_message($message, 'error');
       $files[$i] = FALSE;
diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php
index 8564127..39f27d6 100644
--- a/core/modules/file/src/Entity/File.php
+++ b/core/modules/file/src/Entity/File.php
@@ -258,12 +258,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['filesize'] = FieldDefinition::create('integer')
       ->setLabel(t('File size'))
       ->setDescription(t('The size of the file in bytes.'))
-      ->setSetting('unsigned', TRUE)
-      ->setSetting('size', 'big');
+      ->setSetting('unsigned', TRUE);
 
-    $fields['status'] = FieldDefinition::create('boolean')
+    $fields['status'] = FieldDefinition::create('integer')
       ->setLabel(t('Status'))
-      ->setDescription(t('The status of the file, temporary (FALSE) and permanent (TRUE).'));
+      ->setDescription(t('The status of the file, temporary (0) and permanent (1).'));
 
     $fields['created'] = FieldDefinition::create('created')
       ->setLabel(t('Created'))
diff --git a/core/modules/file/src/FileStorage.php b/core/modules/file/src/FileStorage.php
index 4b30ad6..90d4bb0 100644
--- a/core/modules/file/src/FileStorage.php
+++ b/core/modules/file/src/FileStorage.php
@@ -36,35 +36,6 @@ public function retrieveTemporaryFiles() {
     return $this->database->query('SELECT fid FROM {' . $this->entityType->getBaseTable() . '} WHERE status <> :permanent AND changed < :changed', array(
       ':permanent' => FILE_STATUS_PERMANENT,
       ':changed' => REQUEST_TIME - \Drupal::config('system.file')->get('temporary_maximum_age'),
-    ))->fetchCol();
+    ));
   }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['file_managed']['fields']['status']['not null'] = TRUE;
-    $schema['file_managed']['fields']['changed']['not null'] = TRUE;
-    $schema['file_managed']['fields']['uri']['not null'] = TRUE;
-
-    // @todo There should be a 'binary' field type or setting.
-    $schema['file_managed']['fields']['uri']['binary'] = TRUE;
-    $schema['file_managed']['indexes'] += array(
-      'file__status' => array('status'),
-      'file__changed' => array('changed'),
-    );
-    $schema['file_managed']['unique keys'] += array(
-      // FIXME We have an index size of 255, but the max URI length is 2048 so
-      // this might now always work. Should we replace this with a regular
-      // index?
-      'file__uri' => array(array('uri', 255)),
-    );
-
-    return $schema;
-  }
-
 }
diff --git a/core/modules/file/src/FileStorageInterface.php b/core/modules/file/src/FileStorageInterface.php
index 14bdc62..a856b33 100644
--- a/core/modules/file/src/FileStorageInterface.php
+++ b/core/modules/file/src/FileStorageInterface.php
@@ -35,8 +35,8 @@ public function spaceUsed($uid = NULL, $status = FILE_STATUS_PERMANENT);
    * Get files older than the temporary maximum age,
    * \Drupal::config('system.file')->get('temporary_maximum_age').
    *
-   *  @return int[]
-   *    A list of file IDs of the files to be deleted.
+   *  @return array
+   *    A list of files to be deleted.
    */
   public function retrieveTemporaryFiles();
 
diff --git a/core/modules/file/src/Tests/FileItemTest.php b/core/modules/file/src/Tests/FileItemTest.php
index 57b908a..0f40a14 100644
--- a/core/modules/file/src/Tests/FileItemTest.php
+++ b/core/modules/file/src/Tests/FileItemTest.php
@@ -42,8 +42,8 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('file');
-    $this->installSchema('file', array('file_usage'));
+    $this->installSchema('file', 'file_managed');
+    $this->installSchema('file', 'file_usage');
 
     entity_create('field_config', array(
       'name' => 'file_test',
diff --git a/core/modules/file/src/Tests/FileManagedUnitTestBase.php b/core/modules/file/src/Tests/FileManagedUnitTestBase.php
index 503490f..8c83ac7 100644
--- a/core/modules/file/src/Tests/FileManagedUnitTestBase.php
+++ b/core/modules/file/src/Tests/FileManagedUnitTestBase.php
@@ -29,9 +29,8 @@ function setUp() {
     file_test_reset();
 
     $this->installConfig(array('system'));
-    $this->installEntitySchema('file');
-    $this->installEntitySchema('user');
-    $this->installSchema('file', array('file_usage'));
+    $this->installSchema('file', array('file_managed', 'file_usage'));
+    $this->installSchema('user', array('users', 'users_roles'));
 
     // Make sure that a user with uid 1 exists, self::createFile() relies on
     // it.
diff --git a/core/modules/file/src/Tests/RetrieveTemporaryFilesTest.php b/core/modules/file/src/Tests/RetrieveTemporaryFilesTest.php
index 12da31d..eae640c 100644
--- a/core/modules/file/src/Tests/RetrieveTemporaryFilesTest.php
+++ b/core/modules/file/src/Tests/RetrieveTemporaryFilesTest.php
@@ -15,20 +15,6 @@
 class RetrieveTemporaryFilesTest extends FileManagedUnitTestBase {
 
   /**
-   * The file storage.
-   *
-   * @var \Drupal\file\FileStorageInterface
-   */
-  protected $fileStorage;
-
-  /**
-   * The temporary_maximum_age setting of files.
-   *
-   * @var int
-   */
-  protected $maxAge;
-
-  /**
    * {@inheritdoc}
    */
   public static function getInfo() {
@@ -45,30 +31,27 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->maxAge = $this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age');
-    $this->fileStorage = $this->container->get('entity.manager')->getStorage('file');
+    $max_age = $this->container->get('config.factory')->get('system.file')->get('temporary_maximum_age');
+
     // Create an entry for the user with the date change.
-    $file = $this->fileStorage->create(array('uid' => 2, 'uri' => $this->createUri(), 'status' => 2));
-    $file->save();
+    $file_array = array('uid' => 2, 'uri' => 'public://example1.txt', 'status' => 2, 'changed' => REQUEST_TIME);
+    db_insert('file_managed')->fields($file_array)->execute();
+
+    // Create an entry for the user with an indication of the old date of the
+    // change.
+    $file_array = array('uid' => 2, 'uri' => 'public://example2.txt', 'status' => 2, 'changed' => REQUEST_TIME - ($max_age * 2));
+    db_insert('file_managed')->fields($file_array)->execute();
   }
 
   /**
    * Tests finding stale files.
    */
   function testRetrieveTemporaryFiles() {
-    $this->assertEqual($this->fileStorage->retrieveTemporaryFiles(), [], 'No file is to be deleted.');
+    $file_storage = $this->container->get('entity.manager')->getStorage('file');
 
-    // Create an entry for the user with an indication of the old date of the
-    // change. As the changed field always saves the request time, we do have
-    // update it with a direct db query.
-    $file = $this->fileStorage->create(array('uid' => 2, 'uri' => $this->createUri(), 'status' => 2));
-    $file->save();
-    db_update('file_managed')
-      ->fields(array('changed' => REQUEST_TIME - ($this->maxAge * 2)))
-      ->condition('fid', $file->id())
-      ->execute();
+    $count_files = count($file_storage->retrieveTemporaryFiles()->fetchAssoc());
 
-    $this->assertEqual($this->fileStorage->retrieveTemporaryFiles(), [$file->id()], 'One file is to be deleted.');
+    $this->assertEqual($count_files, 1);
   }
 
 }
diff --git a/core/modules/file/src/Tests/SpaceUsedTest.php b/core/modules/file/src/Tests/SpaceUsedTest.php
index bfedef6..a0c8e33 100644
--- a/core/modules/file/src/Tests/SpaceUsedTest.php
+++ b/core/modules/file/src/Tests/SpaceUsedTest.php
@@ -23,40 +23,20 @@ function setUp() {
     parent::setUp();
 
     // Create records for a couple of users with different sizes.
-    $this->createFileWithSize('public://example1.txt', 50, 2);
-    $this->createFileWithSize('public://example2.txt', 20, 2);
-    $this->createFileWithSize('public://example3.txt', 100, 3);
-    $this->createFileWithSize('public://example4.txt', 200, 3);
+    $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT);
+    db_insert('file_managed')->fields($file)->execute();
+    $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT);
+    db_insert('file_managed')->fields($file)->execute();
+    $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT);
+    db_insert('file_managed')->fields($file)->execute();
+    $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT);
+    db_insert('file_managed')->fields($file)->execute();
 
     // Now create some non-permanent files.
-    $this->createFileWithSize('public://example5.txt', 1, 2, 0);
-    $this->createFileWithSize('public://example6.txt', 3, 3, 0);
-  }
-
-  /**
-   * Creates a file with a given size.
-   *
-   * @param string $uri
-   *   URI of the file to create.
-   * @param int $size
-   *   Size of the file.
-   * @param int $uid
-   *   File owner ID.
-   * @param int $status
-   *   Whether the file should be permanent or temporary.
-   *
-   * @return \Drupal\Core\Entity\EntityInterface
-   *   The file entity.
-   */
-  protected function createFileWithSize($uri, $size, $uid, $status = FILE_STATUS_PERMANENT) {
-    file_put_contents($uri, $this->randomName($size));
-    $file = entity_create('file', array(
-      'uri' => $uri,
-      'uid' => $uid,
-      'status' => $status,
-    ));
-    $file->save();
-    return $file;
+    $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0);
+    db_insert('file_managed')->fields($file)->execute();
+    $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0);
+    db_insert('file_managed')->fields($file)->execute();
   }
 
   /**
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index f25fe79..b2f2c28 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -13,6 +13,7 @@
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\filter\FilterFormatInterface;
 use Drupal\filter\Plugin\FilterInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -414,7 +415,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE,
     \Drupal::cache('filter')->set($cache_id, $text, Cache::PERMANENT, array('filter_format' => $format->id()));
   }
 
-  return $text;
+  return new SafeMarkup($text);
 }
 
 /**
diff --git a/core/modules/filter/src/Plugin/Filter/FilterCaption.php b/core/modules/filter/src/Plugin/Filter/FilterCaption.php
index 08ad4c8..22980fa 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\Core\Template\SafeMarkup;
 use Drupal\filter\Plugin\FilterBase;
 
 /**
@@ -76,7 +77,7 @@ public function process($text, $langcode, $cache, $cache_id) {
         // caption.
         $filter_caption = array(
           '#theme' => 'filter_caption',
-          '#node' => $node->C14N(),
+          '#node' => new SafeMarkup($node->C14N()),
           '#tag' => $node->tagName,
           '#caption' => $caption,
           '#align' => $align,
diff --git a/core/modules/filter/src/Tests/FilterDefaultConfigTest.php b/core/modules/filter/src/Tests/FilterDefaultConfigTest.php
index 7423b70..9d8f3ca 100644
--- a/core/modules/filter/src/Tests/FilterDefaultConfigTest.php
+++ b/core/modules/filter/src/Tests/FilterDefaultConfigTest.php
@@ -30,7 +30,7 @@ function setUp() {
     // filter_permission() calls into url() to output a link in the description.
     $this->installSchema('system', 'url_alias');
 
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users_roles'));
 
     // Install filter_test module, which ships with custom default format.
     $this->installConfig(array('user', 'filter_test'));
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/forum/src/Tests/Views/ForumIntegrationTest.php b/core/modules/forum/src/Tests/Views/ForumIntegrationTest.php
index 6e40370..65d9f1b 100644
--- a/core/modules/forum/src/Tests/Views/ForumIntegrationTest.php
+++ b/core/modules/forum/src/Tests/Views/ForumIntegrationTest.php
@@ -51,7 +51,7 @@ protected function setUp() {
   public function testForumIntegration() {
     // Create a forum.
     $entity_manager = $this->container->get('entity.manager');
-    $term = $entity_manager->getStorage('taxonomy_term')->create(array('vid' => 'forums', 'name' => $this->randomName()));
+    $term = $entity_manager->getStorage('taxonomy_term')->create(array('vid' => 'forums'));
     $term->save();
 
     $comment_storage = $entity_manager->getStorage('comment');
diff --git a/core/modules/hal/src/Tests/EntityTest.php b/core/modules/hal/src/Tests/EntityTest.php
index 8bc3fed..bfbe6f1 100644
--- a/core/modules/hal/src/Tests/EntityTest.php
+++ b/core/modules/hal/src/Tests/EntityTest.php
@@ -38,10 +38,10 @@ function setUp() {
 
     \Drupal::service('router.builder')->rebuild();
     $this->installSchema('system', array('sequences'));
-    $this->installSchema('comment', array('comment_entity_statistics'));
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('comment');
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('node', array('node', 'node_field_data', 'node_revision', 'node_field_revision'));
+    $this->installSchema('comment', array('comment', 'comment_entity_statistics'));
+    $this->installSchema('user', array('users_roles'));
+    $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
   }
 
   /**
diff --git a/core/modules/hal/src/Tests/NormalizerTestBase.php b/core/modules/hal/src/Tests/NormalizerTestBase.php
index c74854f..894cc40 100644
--- a/core/modules/hal/src/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/src/Tests/NormalizerTestBase.php
@@ -62,8 +62,8 @@
   function setUp() {
     parent::setUp();
     $this->installSchema('system', array('url_alias', 'router'));
-    $this->installEntitySchema('user');
-    $this->installEntitySchema('entity_test');
+    $this->installSchema('user', array('users'));
+    $this->installSchema('entity_test', array('entity_test'));
     $this->installConfig(array('field', 'language'));
 
     // Add English as a language.
diff --git a/core/modules/image/src/Tests/ImageItemTest.php b/core/modules/image/src/Tests/ImageItemTest.php
index e17987b..7803a11 100644
--- a/core/modules/image/src/Tests/ImageItemTest.php
+++ b/core/modules/image/src/Tests/ImageItemTest.php
@@ -47,8 +47,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('file');
-    $this->installSchema('file', array('file_usage'));
+    $this->installSchema('file', array('file_managed', 'file_usage'));
 
     entity_create('field_config', array(
       'name' => 'image_test',
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php
index d7fc9a3..d29ad95 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateBookTest.php
@@ -38,7 +38,6 @@ protected function setUp() {
       $entity = entity_create('node', array(
         'type' => 'story',
         'nid' => $i,
-        'status' => TRUE,
       ));
       $entity->enforceIsNew();
       $entity->save();
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php
index dfaa2a3..98c53e9 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateFieldInstanceTest.php
@@ -107,7 +107,6 @@ public function testFieldInstanceSettings() {
       'prefix' => 'pref',
       'suffix' => 'suf',
       'unsigned' => '',
-      'size' => 'normal',
     );
     $this->assertEqual($field->getSettings(), $expected);
 
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateTaxonomyTermTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateTaxonomyTermTest.php
index 5969543..684b626 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateTaxonomyTermTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateTaxonomyTermTest.php
@@ -60,13 +60,11 @@ public function testTaxonomyTerms() {
         'source_vid' => 1,
         'vid' => 'vocabulary_1_i_0_',
         'weight' => 0,
-        'parent' => array(0),
       ),
       '2' => array(
         'source_vid' => 2,
         'vid' => 'vocabulary_2_i_1_',
         'weight' => 3,
-        'parent' => array(0),
       ),
       '3' => array(
         'source_vid' => 2,
@@ -78,7 +76,6 @@ public function testTaxonomyTerms() {
         'source_vid' => 3,
         'vid' => 'vocabulary_3_i_2_',
         'weight' => 6,
-        'parent' => array(0),
       ),
       '5' => array(
         'source_vid' => 3,
@@ -101,8 +98,8 @@ public function testTaxonomyTerms() {
       $this->assertIdentical($term->description->value, "description of term {$tid} of vocabulary {$values['source_vid']}");
       $this->assertEqual($term->vid->target_id, $values['vid']);
       $this->assertEqual($term->weight->value, $values['weight']);
-      if ($values['parent'] === array(0)) {
-        $this->assertEqual($term->parent->value, 0);
+      if (empty($values['parent'])) {
+        $this->assertNull($term->parent->value);
       }
       else {
         $parents = array();
diff --git a/core/modules/node/node.install b/core/modules/node/node.install
index d0d6d1c..7888e2b 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\Core\Template\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 results of t() is safe and so is the results of l(). Preserving
+      // safe object.
+      'description' => new SafeMarkup($description . ' ' . l(t('Rebuild permissions'), 'admin/reports/status/rebuild')),
     );
   }
   return $requirements;
@@ -39,6 +42,311 @@ function node_requirements($phase) {
  * Implements hook_schema().
  */
 function node_schema() {
+  $schema['node'] = array(
+    'description' => 'The base table for nodes.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The primary identifier for a node.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      // Defaults to NULL in order to avoid a brief period of potential
+      // deadlocks on the index.
+      'vid' => array(
+        'description' => 'The current {node_field_revision}.vid version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+      ),
+      'type' => array(
+        'description' => 'The type of this node.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      'node_type' => array(array('type', 4)),
+    ),
+    'unique keys' => array(
+      'vid' => array('vid'),
+      'uuid' => array('uuid'),
+    ),
+    'foreign keys' => array(
+      'node_revision' => array(
+        'table' => 'node_revision',
+        'columns' => array('vid' => 'vid'),
+      ),
+    ),
+    'primary key' => array('nid'),
+  );
+
+  $schema['node_revision'] = array(
+    'description' => 'Stores information about each saved version of a {node}.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The {node} this version belongs to.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'vid' => array(
+        'description' => 'The primary identifier for this version.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'revision_uid' => array(
+        'description' => 'The {users}.uid that created this version.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'log' => array(
+        'description' => 'The log entry explaining the changes in this version.',
+        'type' => 'text',
+        'not null' => FALSE,
+        'size' => 'big',
+      ),
+      'revision_timestamp' => array(
+        'description' => 'The Unix timestamp when the version was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this version.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      'nid' => array('nid'),
+      'revision_uid' => array('revision_uid'),
+      'node_langcode' => array('langcode'),
+    ),
+    'foreign keys' => array(
+      'versioned_node' => array(
+        'table' => 'node',
+        'columns' => array('nid' => 'nid'),
+      ),
+      'version_author' => array(
+        'table' => 'users',
+        'columns' => array('revision_uid' => 'uid'),
+      ),
+    ),
+    'primary key' => array('vid'),
+  );
+
+  // Node field storage.
+  $schema['node_field_data'] = array(
+    'description' => 'Data table for node base fields.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The primary identifier for a node.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'vid' => array(
+        'description' => 'The current {node_field_revision}.vid version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'type' => array(
+        'description' => 'The {node_type}.type of this node.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of these node property values.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the property values are in the {language}.langcode of this node.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'title' => array(
+        'description' => 'The title of this node, always treated as non-markup plain text.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'uid' => array(
+        'description' => 'The {users}.uid that owns this node; initially, this is the user that created it.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'status' => array(
+        'description' => 'Boolean indicating whether the node translation is published (visible to non-administrators).',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'created' => array(
+        'description' => 'The Unix timestamp when the node translation was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the node translation was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'promote' => array(
+        'description' => 'Boolean indicating whether the node translation should be displayed on the front page.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'sticky' => array(
+        'description' => 'Boolean indicating whether the node translation should be displayed at the top of lists in which it appears.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'node_changed' => array('changed'),
+      'node_created' => array('created'),
+      'node_default_langcode' => array('default_langcode'),
+      'node_langcode' => array('langcode'),
+      'node_frontpage' => array('promote', 'status', 'sticky', 'created'),
+      'node_status_type' => array('status', 'type', 'nid'),
+      'node_title_type' => array('title', array('type', 4)),
+      'node_type' => array(array('type', 4)),
+      'vid' => array('vid'),
+      'uid' => array('uid'),
+    ),
+    'foreign keys' => array(
+      'node_base' => array(
+        'table' => 'node',
+        'columns' => array('nid' => 'nid'),
+      ),
+      'node_author' => array(
+        'table' => 'users',
+        'columns' => array('uid' => 'uid'),
+      ),
+    ),
+    'primary key' => array('nid', 'langcode'),
+  );
+
+  $schema['node_field_revision'] = array(
+    'description' => 'Revision table for node base fields.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The {node} this version belongs to.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'vid' => array(
+        'description' => 'The primary identifier for this version.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this version.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the property values of this version are in the {language}.langcode of this node.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'title' => array(
+        'description' => 'The title of this version, always treated as non-markup plain text.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'uid' => array(
+        'description' => 'The {users}.uid that created this node.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'status' => array(
+        'description' => 'Boolean indicating whether the node (at the time of this revision) is published (visible to non-administrators).',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'created' => array(
+        'description' => 'The Unix timestamp when the node was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the version was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'promote' => array(
+        'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed on the front page.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'sticky' => array(
+        'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed at the top of lists in which it appears.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'uid' => array('uid'),
+      'node_default_langcode' => array('default_langcode'),
+      'node_langcode' => array('langcode'),
+    ),
+    'foreign keys' => array(
+      'versioned_node' => array(
+        'table' => 'node',
+        'columns' => array('nid' => 'nid'),
+      ),
+      'node_author' => array(
+        'table' => 'users',
+        'columns' => array('uid' => 'uid'),
+      ),
+    ),
+    'primary key' => array('vid', 'langcode'),
+  );
+
   $schema['node_access'] = array(
     'description' => 'Identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes.',
     'fields' => array(
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 38e047b..2253f71 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -807,10 +807,7 @@ function node_user_cancel($edit, $account, $method) {
       // Anonymize all of the nodes for this old account.
       module_load_include('inc', 'node', 'node.admin');
       $vids = \Drupal::entityManager()->getStorage('node')->userRevisionIds($account);
-      node_mass_update($vids, array(
-        'uid' => 0,
-        'revision_uid' => 0,
-      ), NULL, TRUE, TRUE);
+      node_mass_update($vids, array('uid' => 0), NULL, TRUE, TRUE);
       break;
   }
 }
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 7fb33e6..1d21504 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -65,23 +65,6 @@ class Node extends ContentEntityBase implements NodeInterface {
   /**
    * {@inheritdoc}
    */
-  public function preSave(EntityStorageInterface $storage) {
-    parent::preSave($storage);
-
-    // If no owner has been set explicitly, make the current user the owner.
-    if (!$this->getOwner()) {
-      $this->setOwnerId(\Drupal::currentUser()->id());
-    }
-    // If no revision author has been set explicitly, make the node owner the
-    // revision author.
-    if (!$this->getRevisionAuthor()) {
-      $this->setRevisionAuthorId($this->getOwnerId());
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
     parent::preSaveRevision($storage, $record);
 
@@ -357,7 +340,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Type'))
       ->setDescription(t('The node type.'))
       ->setSetting('target_type', 'node_type')
-      ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH)
       ->setReadOnly(TRUE);
 
     $fields['langcode'] = FieldDefinition::create('language')
@@ -371,8 +353,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setRequired(TRUE)
       ->setTranslatable(TRUE)
       ->setRevisionable(TRUE)
-      ->setSetting('default_value', '')
-      ->setSetting('max_length', 255)
+      ->setSettings(array(
+        'default_value' => '',
+        'max_length' => 255,
+      ))
       ->setDisplayOptions('view', array(
         'label' => 'hidden',
         'type' => 'string',
@@ -388,7 +372,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Author'))
       ->setDescription(t('The user that is the node author.'))
       ->setRevisionable(TRUE)
-      ->setSetting('target_type', 'user')
+      ->setSettings(array(
+        'target_type' => 'user',
+        'default_value' => 0,
+      ))
       ->setTranslatable(TRUE);
 
     $fields['status'] = FieldDefinition::create('boolean')
@@ -421,7 +408,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setRevisionable(TRUE)
       ->setTranslatable(TRUE);
 
-    $fields['revision_timestamp'] = FieldDefinition::create('created')
+    $fields['revision_timestamp'] = FieldDefinition::create('timestamp')
       ->setLabel(t('Revision timestamp'))
       ->setDescription(t('The time that the current revision was created.'))
       ->setQueryable(FALSE)
@@ -430,11 +417,11 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['revision_uid'] = FieldDefinition::create('entity_reference')
       ->setLabel(t('Revision user ID'))
       ->setDescription(t('The user ID of the author of the current revision.'))
-      ->setSetting('target_type', 'user')
+      ->setSettings(array('target_type' => 'user'))
       ->setQueryable(FALSE)
       ->setRevisionable(TRUE);
 
-    $fields['log'] = FieldDefinition::create('string_long')
+    $fields['log'] = FieldDefinition::create('string')
       ->setLabel(t('Log'))
       ->setDescription(t('The log entry explaining the changes in this revision.'))
       ->setRevisionable(TRUE)
@@ -449,30 +436,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
   public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
     $node_type = node_type_load($bundle);
     $fields = array();
-
-    // When deleting a node type the corresponding node displays are deleted as
-    // well. In order to be deleted, they need to be loaded first. Entity
-    // displays, however, fetch the field definitions of the respective entity
-    // type to fill in their defaults. Therefore this function ends up being
-    // called with a non-existing bundle.
-    // @todo Fix this in https://drupal.org/node/2248795
-    if (!$node_type) {
-      return $fields;
-    }
-
     if (isset($node_type->title_label)) {
       $fields['title'] = clone $base_field_definitions['title'];
       $fields['title']->setLabel($node_type->title_label);
     }
-
-    $options = $node_type->getModuleSettings('node')['options'];
-    $fields['status'] = clone $base_field_definitions['status'];
-    $fields['status']->setSetting('default_value', !empty($options['status']) ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
-    $fields['promote'] = clone $base_field_definitions['promote'];
-    $fields['promote']->setSetting('default_value', !empty($options['promote']) ? NODE_PROMOTED : NODE_NOT_PROMOTED);
-    $fields['sticky'] = clone $base_field_definitions['sticky'];
-    $fields['sticky']->setSetting('default_value', !empty($options['sticky']) ? NODE_STICKY : NODE_NOT_STICKY);
-
     return $fields;
   }
 
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index 0c5bbc6..37bb7da 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -36,7 +36,16 @@ protected function prepareEntity() {
     $type = entity_load('node_type', $node->bundle());
     $this->settings = $type->getModuleSettings('node');
 
-    if (!$node->isNew()) {
+    // If this is a new node, fill in the default values.
+    if ($node->isNew()) {
+      foreach (array('status', 'promote', 'sticky') as $key) {
+        // Multistep node forms might have filled in something already.
+        if ($node->$key->isEmpty()) {
+          $node->$key = (int) !empty($this->settings['options'][$key]);
+        }
+      }
+    }
+    else {
       $node->date = format_date($node->getCreatedTime(), 'custom', 'Y-m-d H:i:s O');
       // Remove the log message from the original node entity.
       $node->log = NULL;
diff --git a/core/modules/node/src/NodeStorage.php b/core/modules/node/src/NodeStorage.php
index 8f87df5..8bb17fd 100644
--- a/core/modules/node/src/NodeStorage.php
+++ b/core/modules/node/src/NodeStorage.php
@@ -2,11 +2,12 @@
 
 /**
  * @file
- * Contains \Drupal\node\NodeStorage.
+ * Definition of Drupal\node\NodeStorageController.
  */
 
 namespace Drupal\node;
 
+use Drupal\Core\Database\Database;
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Language\Language;
@@ -58,51 +59,4 @@ public function clearRevisionsLanguage($language) {
       ->condition('langcode', $language->id)
       ->execute();
   }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['node_field_data']['fields']['changed']['not null'] = TRUE;
-    $schema['node_field_data']['fields']['created']['not null'] = TRUE;
-    $schema['node_field_data']['fields']['default_langcode']['not null'] = TRUE;
-    $schema['node_field_data']['fields']['promote']['not null'] = TRUE;
-    $schema['node_field_data']['fields']['status']['not null'] = TRUE;
-    $schema['node_field_data']['fields']['sticky']['not null'] = TRUE;
-    $schema['node_field_data']['fields']['title']['not null'] = TRUE;
-    $schema['node_field_revision']['fields']['default_langcode']['not null'] = TRUE;
-
-    // @todo Revisit index definitions in https://drupal.org/node/2015277.
-    $schema['node_revision']['indexes'] += array(
-      'node__langcode' => array('langcode'),
-    );
-    $schema['node_revision']['foreign keys'] += array(
-      'node__revision_author' => array(
-        'table' => 'users',
-        'columns' => array('revision_uid' => 'uid'),
-      ),
-    );
-
-    $schema['node_field_data']['indexes'] += array(
-      'node__changed' => array('changed'),
-      'node__created' => array('created'),
-      'node__default_langcode' => array('default_langcode'),
-      'node__langcode' => array('langcode'),
-      'node__frontpage' => array('promote', 'status', 'sticky', 'created'),
-      'node__status_type' => array('status', 'type', 'nid'),
-      'node__title_type' => array('title', array('type', 4)),
-    );
-
-    $schema['node_field_revision']['indexes'] += array(
-      'node__default_langcode' => array('default_langcode'),
-      'node__langcode' => array('langcode'),
-    );
-
-    return $schema;
-  }
-
 }
diff --git a/core/modules/node/src/Tests/Condition/NodeConditionTest.php b/core/modules/node/src/Tests/Condition/NodeConditionTest.php
index e429de5..6ba87e6 100644
--- a/core/modules/node/src/Tests/Condition/NodeConditionTest.php
+++ b/core/modules/node/src/Tests/Condition/NodeConditionTest.php
@@ -26,7 +26,7 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('node');
+    $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision'));
 
     // Create the node bundles required for testing.
     $type = entity_create('node_type', array('type' => 'page', 'name' => 'page'));
diff --git a/core/modules/node/src/Tests/Config/NodeImportCreateTest.php b/core/modules/node/src/Tests/Config/NodeImportCreateTest.php
index fadc706..2f6a8db 100644
--- a/core/modules/node/src/Tests/Config/NodeImportCreateTest.php
+++ b/core/modules/node/src/Tests/Config/NodeImportCreateTest.php
@@ -27,7 +27,7 @@ class NodeImportCreateTest extends DrupalUnitTestBase {
    */
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users'));
 
     // Set default storage backend.
     $this->installConfig(array('field'));
diff --git a/core/modules/node/src/Tests/NodeFormButtonsTest.php b/core/modules/node/src/Tests/NodeFormButtonsTest.php
index 9a6831d..1aa7dcd 100644
--- a/core/modules/node/src/Tests/NodeFormButtonsTest.php
+++ b/core/modules/node/src/Tests/NodeFormButtonsTest.php
@@ -108,10 +108,7 @@ function testNodeFormButtons() {
     // Set article content type default to unpublished. This will change the
     // the initial order of buttons and/or status of the node when creating
     // a node.
-    /** @var \Drupal\node\NodeTypeInterface $node_type */
-    $node_type = $this->container->get('entity.manager')->getStorage('node_type')->load('article');
-    $node_type->settings['node']['options']['status'] = FALSE;
-    $node_type->save();
+    \Drupal::config('node.type.article')->set('settings.node.options.status', FALSE)->save();
 
     // Verify the buttons on a node add form for an administrator.
     $this->drupalLogin($this->admin_user);
diff --git a/core/modules/node/src/Tests/NodeLastChangedTest.php b/core/modules/node/src/Tests/NodeLastChangedTest.php
index aa1dbfa..bf23498 100644
--- a/core/modules/node/src/Tests/NodeLastChangedTest.php
+++ b/core/modules/node/src/Tests/NodeLastChangedTest.php
@@ -31,8 +31,11 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('user');
+    $this->installSchema('node', 'node');
+    $this->installSchema('node', 'node_revision');
+    $this->installSchema('node', 'node_field_data');
+    $this->installSchema('node', 'node_field_revision');
+    $this->installSchema('user', array('users'));
   }
 
   /**
diff --git a/core/modules/node/src/Tests/NodeTokenReplaceTest.php b/core/modules/node/src/Tests/NodeTokenReplaceTest.php
index 7f12113..5e9e37a 100644
--- a/core/modules/node/src/Tests/NodeTokenReplaceTest.php
+++ b/core/modules/node/src/Tests/NodeTokenReplaceTest.php
@@ -38,7 +38,7 @@ public static function getInfo() {
    */
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('node');
+    $this->installSchema('node', array('node', 'node_field_revision', 'node_field_data', 'node_revision'));
     $this->installConfig(array('filter'));
 
     $node_type = entity_create('node_type', array('type' => 'article', 'name' => 'Article'));
diff --git a/core/modules/node/src/Tests/NodeValidationTest.php b/core/modules/node/src/Tests/NodeValidationTest.php
index 94c7ca3..e71d75b 100644
--- a/core/modules/node/src/Tests/NodeValidationTest.php
+++ b/core/modules/node/src/Tests/NodeValidationTest.php
@@ -34,7 +34,7 @@ public static function getInfo() {
    */
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('node');
+    $this->installSchema('node', array('node', 'node_field_data', 'node_field_revision', 'node_revision'));
 
     // Create a node type for testing.
     $type = entity_create('node_type', array('type' => 'page', 'name' => 'page'));
diff --git a/core/modules/quickedit/src/Tests/QuickEditTestBase.php b/core/modules/quickedit/src/Tests/QuickEditTestBase.php
index 9fb217c..c72899b 100644
--- a/core/modules/quickedit/src/Tests/QuickEditTestBase.php
+++ b/core/modules/quickedit/src/Tests/QuickEditTestBase.php
@@ -27,7 +27,7 @@
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('entity_test');
+    $this->installSchema('entity_test', array('entity_test', 'entity_test_rev'));
     $this->installConfig(array('field', 'filter'));
   }
 
diff --git a/core/modules/rdf/src/Tests/CommentAttributesTest.php b/core/modules/rdf/src/Tests/CommentAttributesTest.php
index d3cacfd..f152e0b 100644
--- a/core/modules/rdf/src/Tests/CommentAttributesTest.php
+++ b/core/modules/rdf/src/Tests/CommentAttributesTest.php
@@ -163,7 +163,7 @@ public function testCommentRdfaMarkup() {
     $anonymous_user['name'] = $this->randomName();
     $anonymous_user['mail'] = 'tester@simpletest.org';
     $anonymous_user['homepage'] = 'http://example.org/';
-    $comment2 = $this->saveComment($this->node->id(), 0, $anonymous_user);
+    $comment2 = $this->saveComment($this->node->id(), NULL, $anonymous_user);
 
     // Tests comment #2 as anonymous user.
     $parser = new \EasyRdf_Parser_Rdfa();
diff --git a/core/modules/rdf/src/Tests/Field/TaxonomyTermReferenceRdfaTest.php b/core/modules/rdf/src/Tests/Field/TaxonomyTermReferenceRdfaTest.php
index f733014..5086bf1 100644
--- a/core/modules/rdf/src/Tests/Field/TaxonomyTermReferenceRdfaTest.php
+++ b/core/modules/rdf/src/Tests/Field/TaxonomyTermReferenceRdfaTest.php
@@ -50,7 +50,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
 
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module
index 53c182c..fd7341d 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -7,6 +7,7 @@
 
 use Drupal\breakpoint\Entity\Breakpoint;
 use \Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\SafeMarkup;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -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 new SafeMarkup(implode("\n", $output));
   }
 }
 
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 718f5c8..9b3c792 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -133,13 +133,6 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
     // Overwrite the received properties.
     foreach ($entity as $field_name => $field) {
       if (isset($entity->{$field_name})) {
-        // It is not possible to set the language to NULL as it is automatically
-        // re-initialized. As it must not be empty, skip it if it is.
-        // @todo: Use the langcode entity key when available. See
-        //   https://drupal.org/node/2143729.
-        if ($field_name == 'langcode' && $field->isEmpty()) {
-          continue;
-        }
         if ($field->isEmpty() && !$original_entity->get($field_name)->access('delete')) {
           throw new AccessDeniedHttpException(t('Access denied on deleting field @field.', array('@field' => $field_name)));
         }
diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php
index 7afedfa..15ef7f3 100644
--- a/core/modules/rest/src/Tests/UpdateTest.php
+++ b/core/modules/rest/src/Tests/UpdateTest.php
@@ -58,6 +58,7 @@ public function testPatchUpdate() {
     $patch_entity = entity_create($entity_type, $patch_values);
     // We don't want to overwrite the UUID.
     unset($patch_entity->uuid);
+    $patch_entity->save();
     $serialized = $serializer->serialize($patch_entity, $this->defaultFormat);
 
     // Update the entity over the REST API.
diff --git a/core/modules/serialization/src/Tests/EntitySerializationTest.php b/core/modules/serialization/src/Tests/EntitySerializationTest.php
index 1ad843a..865704d 100644
--- a/core/modules/serialization/src/Tests/EntitySerializationTest.php
+++ b/core/modules/serialization/src/Tests/EntitySerializationTest.php
@@ -80,12 +80,18 @@ public function testNormalize() {
       'id' => array(
         array('value' => 1),
       ),
+      'revision_id' => array(
+        array('value' => 1),
+      ),
       'uuid' => array(
         array('value' => $this->entity->uuid()),
       ),
       'langcode' => array(
         array('value' => Language::LANGCODE_NOT_SPECIFIED),
       ),
+      'default_langcode' => array(
+        array('value' => NULL),
+      ),
       'name' => array(
         array('value' => $this->values['name']),
       ),
@@ -95,9 +101,6 @@ public function testNormalize() {
       'user_id' => array(
         array('target_id' => $this->values['user_id']),
       ),
-      'revision_id' => array(
-        array('value' => 1),
-      ),
       'field_test_text' => array(
         array(
           'value' => $this->values['field_test_text']['value'],
@@ -138,12 +141,13 @@ public function testSerialize() {
     // order.
     $expected = array(
       'id' => '<id><value>' . $this->entity->id() . '</value></id>',
+      'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
       'uuid' => '<uuid><value>' . $this->entity->uuid() . '</value></uuid>',
       'langcode' => '<langcode><value>' . Language::LANGCODE_NOT_SPECIFIED . '</value></langcode>',
+      'default_langcode' => '<default_langcode><value/></default_langcode>',
       'name' => '<name><value>' . $this->values['name'] . '</value></name>',
       'type' => '<type><value>entity_test_mulrev</value></type>',
       'user_id' => '<user_id><target_id>' . $this->values['user_id'] . '</target_id></user_id>',
-      'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
       'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format></field_test_text>',
     );
     // Sort it in the same order as normalised.
diff --git a/core/modules/serialization/src/Tests/NormalizerTestBase.php b/core/modules/serialization/src/Tests/NormalizerTestBase.php
index 0c1dc03..3b0cfd6 100644
--- a/core/modules/serialization/src/Tests/NormalizerTestBase.php
+++ b/core/modules/serialization/src/Tests/NormalizerTestBase.php
@@ -21,8 +21,8 @@
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('entity_test_mulrev');
-    $this->installEntitySchema('user');
+    $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_revision', 'entity_test_mulrev_property_data'));
+    $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', array('url_alias'));
     $this->installConfig(array('field'));
 
diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install
index be6f8b2..b7ed49c 100644
--- a/core/modules/shortcut/shortcut.install
+++ b/core/modules/shortcut/shortcut.install
@@ -12,6 +12,96 @@
  * Implements hook_schema().
  */
 function shortcut_schema() {
+  $schema['shortcut'] = array(
+    'description' => 'Stores shortcut items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique shortcut ID.',
+      ),
+      'uuid' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+        'description' => 'Unique Key: Universally unique identifier for this shortcut.',
+      ),
+      'shortcut_set' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The bundle of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of the original variant of this shortcut.',
+      ),
+      'weight' => array(
+        'description' => 'Weight among shortcuts in the same shortcut set.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'route_name' => array(
+        'description' => 'The machine name of a defined Symfony Route this menu item represents.',
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+      'route_parameters' => array(
+        'description' => 'Serialized array of route parameters of this shortcut.',
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+
+  $schema['shortcut_field_data'] = array(
+    'description' => 'Stores shortcut properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {shortcut}.id of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of this variant of this shortcut.',
+      ),
+      'default_langcode' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+      ),
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => FALSE,
+        'description' => 'The title of the shortcut.',
+      ),
+    ),
+    'foreign keys' => array(
+      'shortcut' => array(
+        'table' => 'shortcut',
+        'columns' => array('id' => 'id'),
+      ),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
 
   $schema['shortcut_set_users'] = array(
     'description' => 'Maps users to shortcut sets.',
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 53106e7..03be8c8 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -167,7 +167,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Shortcut set'))
       ->setDescription(t('The bundle of the shortcut.'))
       ->setSetting('target_type', 'shortcut_set')
-      ->setSetting('max_length', EntityTypeInterface::BUNDLE_MAX_LENGTH)
       ->setRequired(TRUE);
 
     $fields['title'] = FieldDefinition::create('string')
@@ -175,8 +174,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setDescription(t('The name of the shortcut.'))
       ->setRequired(TRUE)
       ->setTranslatable(TRUE)
-      ->setSetting('default_value', '')
-      ->setSetting('max_length', 255)
+      ->setSettings(array(
+        'default_value' => '',
+        'max_length' => 255,
+      ))
       ->setDisplayOptions('form', array(
         'type' => 'string',
         'weight' => -10,
@@ -201,6 +202,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setLabel(t('Language code'))
       ->setDescription(t('The language code of the shortcut.'));
 
+    $fields['default_langcode'] = FieldDefinition::create('boolean')
+      ->setLabel(t('Default language'))
+      ->setDescription(t('Flag to indicate whether this is the default language.'));
+
     $fields['path'] = FieldDefinition::create('string')
       ->setLabel(t('Path'))
       ->setDescription(t('The computed shortcut path.'))
diff --git a/core/modules/shortcut/src/Entity/ShortcutSet.php b/core/modules/shortcut/src/Entity/ShortcutSet.php
index b1b0fd7..32c1bb6 100644
--- a/core/modules/shortcut/src/Entity/ShortcutSet.php
+++ b/core/modules/shortcut/src/Entity/ShortcutSet.php
@@ -63,17 +63,15 @@ class ShortcutSet extends ConfigEntityBase implements ShortcutSetInterface {
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
-    if (!$update && !$this->isSyncing()) {
+    // Generate menu-compatible set name.
+    if (!$update && !$this->getOriginalId()) {
       // Save a new shortcut set with links copied from the user's default set.
       $default_set = shortcut_default_set();
-      // This is the default set, do not copy shortcuts.
-      if ($default_set->id() != $this->id()) {
-        foreach ($default_set->getShortcuts() as $shortcut) {
-          $shortcut = $shortcut->createDuplicate();
-          $shortcut->enforceIsNew();
-          $shortcut->shortcut_set->target_id = $this->id();
-          $shortcut->save();
-        }
+      foreach ($default_set->getShortcuts() as $shortcut) {
+        $shortcut = $shortcut->createDuplicate();
+        $shortcut->enforceIsNew();
+        $shortcut->shortcut_set->target_id = $this->id();
+        $shortcut->save();
       }
     }
   }
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index bec96c4..1977a6e 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -465,32 +465,29 @@ function simpletest_test_get_all($module = NULL) {
         $all_data += $listing->scan('profile', TRUE);
         $all_data += $listing->scan('theme', TRUE);
       }
-
-      // Scan all extension folders for class files.
       $classes = array();
       foreach ($all_data as $name => $data) {
+        $extension_dir = DRUPAL_ROOT . '/' . $data->getPath();
 
-        // Build the directory in which simpletest test classes would reside.
-        $tests_dir = DRUPAL_ROOT . '/' . $data->getPath() . '/src/Tests';
-
-        // Check if the directory exists.
-        if (!is_dir($tests_dir)) {
-          // This extension has no directory for simpletest cases.
-          continue;
-        }
-
-        // Scan the directory for class files.
-        $files = file_scan_directory($tests_dir, '/\.php$/');
-        if (empty($files)) {
-          // No class files found.
-          continue;
-        }
+        // Build directories in which the test files would reside.
+        $tests_dirs = array(
+          $extension_dir . '/lib/Drupal/' . $name . '/Tests',
+          $extension_dir . '/src/Tests',
+        );
 
-        // Convert the file names into the namespaced class names.
-        $strlen = strlen($tests_dir) + 1;
         $namespace = 'Drupal\\' . $name . '\Tests\\';
-        foreach ($files as $file) {
-          $classes[] = $namespace . str_replace('/', '\\', substr($file->uri, $strlen, -4));
+        // Scan it for test files if it exists.
+        foreach ($tests_dirs as $tests_dir) {
+          if (is_dir($tests_dir)) {
+            $files = file_scan_directory($tests_dir, '/\.php$/');
+            if (!empty($files)) {
+              $strlen = strlen($tests_dir) + 1;
+              // Convert the file names into the namespaced class names.
+              foreach ($files as $file) {
+                $classes[] = $namespace . str_replace('/', '\\', substr($file->uri, $strlen, -4));
+              }
+            }
+          }
         }
       }
 
@@ -526,7 +523,7 @@ function simpletest_test_get_all($module = NULL) {
             }
           }
 
-          $groups[$info['group']][$class] = $info;
+          $groups[(string) $info['group']][$class] = $info;
         }
       }
 
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index 299f305..2786a3a 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\Core\Template\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[] = new SafeMarkup($assertion->message);
         $row[] = $assertion->message_group;
         $row[] = drupal_basename($assertion->file);
         $row[] = $assertion->line;
diff --git a/core/modules/simpletest/src/Form/SimpletestTestForm.php b/core/modules/simpletest/src/Form/SimpletestTestForm.php
index e3876a5..466ea67 100644
--- a/core/modules/simpletest/src/Form/SimpletestTestForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestTestForm.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\SortArray;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Form\FormBase;
+use Drupal\Core\Template\SafeMarkup;
 
 /**
  * List tests arranged in groups that can be selected and run.
@@ -87,8 +88,8 @@ public function buildForm(array $form, array &$form_state) {
       'data' => array(
         'simpleTest' => array(
           'images' => array(
-            drupal_render($image_collapsed),
-            drupal_render($image_extended),
+            (string) drupal_render($image_collapsed),
+            (string) drupal_render($image_extended),
           ),
         ),
       ),
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index 2606747..1188822 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -7,13 +7,11 @@
 
 namespace Drupal\simpletest;
 
-use Drupal\Component\Utility\String;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -340,39 +338,6 @@ protected function installSchema($module, $tables) {
     )));
   }
 
-
-
-  /**
-   * Installs the tables for a specific entity type.
-   *
-   * @param string $entity_type_id
-   *   The ID of the entity type.
-   */
-  protected function installEntitySchema($entity_type_id) {
-    /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
-    $entity_manager = $this->container->get('entity.manager');
-    /** @var \Drupal\Core\Database\Schema $schema_handler */
-    $schema_handler = $this->container->get('database')->schema();
-
-    $storage = $entity_manager->getStorage($entity_type_id);
-    if ($storage instanceof EntitySchemaProviderInterface) {
-      $schema = $storage->getSchema();
-      foreach ($schema as $table_name => $table_schema) {
-        $schema_handler->createTable($table_name, $table_schema);
-      }
-
-      $this->pass(String::format('Installed entity type tables for the %entity_type entity type: %tables', array(
-        '%entity_type' => $entity_type_id,
-        '%tables' => '{' . implode('}, {', array_keys($schema)) . '}',
-      )));
-    }
-    else {
-      throw new \RuntimeException(String::format('Entity type %entity_type does not support automatic schema installation.', array(
-        '%entity-type' => $entity_type_id,
-      )));
-    }
-  }
-
   /**
    * Enables modules for this test.
    *
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 023cf8e..585c100 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -277,7 +277,7 @@ protected function assert($status, $message = '', $group = 'Other', array $calle
       'test_id' => $this->testId,
       'test_class' => get_class($this),
       'status' => $status,
-      'message' => $message,
+      'message' => (string) $message,
       'message_group' => $group,
       'function' => $caller['function'],
       'line' => $caller['line'],
diff --git a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
index 1537393..0a97a63 100644
--- a/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
+++ b/core/modules/simpletest/src/Tests/KernelTestBaseTest.php
@@ -75,7 +75,7 @@ function testEnableModulesLoad() {
    */
   function testEnableModulesInstall() {
     $module = 'node';
-    $table = 'node_access';
+    $table = 'node';
 
     // Verify that the module does not exist yet.
     $this->assertFalse(\Drupal::moduleHandler()->moduleExists($module), "$module module not found.");
@@ -108,9 +108,9 @@ function testEnableModulesInstall() {
    */
   function testEnableModulesInstallContainer() {
     // Install Node module.
-    $this->enableModules(array('user', 'field', 'node'));
+    $this->enableModules(array('field', 'node'));
 
-    $this->installEntitySchema('node', array('node', 'node_field_data'));
+    $this->installSchema('node', array('node', 'node_field_data'));
     // Perform an entity query against node.
     $query = \Drupal::entityQuery('node');
     // Disable node access checks, since User module is not enabled.
@@ -125,7 +125,7 @@ function testEnableModulesInstallContainer() {
    */
   function testInstallSchema() {
     $module = 'entity_test';
-    $table = 'entity_test_example';
+    $table = 'entity_test';
     // Verify that we can install a table from the module schema.
     $this->installSchema($module, $table);
     $this->assertTrue(db_table_exists($table), "'$table' database table found.");
@@ -172,18 +172,6 @@ function testInstallSchema() {
   }
 
   /**
-   * Tests expected behavior of installEntitySchema().
-   */
-  function testInstallEntitySchema() {
-    $entity = 'entity_test';
-    // The entity_test Entity has a field that depends on the User module.
-    $this->enableModules(array('user'));
-    // Verity that the entity schema is created properly.
-    $this->installEntitySchema($entity);
-    $this->assertTrue(db_table_exists($entity), "'$entity' database table found.");
-  }
-
-  /**
    * Tests expected behavior of installConfig().
    */
   function testInstallConfig() {
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index ec50d4d..f2a229a 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -23,6 +23,7 @@
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\block\Entity\Block;
+use Drupal\Core\Template\SafeMarkup;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\CssSelector\CssSelector;
 
@@ -1629,6 +1630,9 @@ protected function drupalGetAJAX($path, array $options = array(), array $headers
    */
   protected function drupalPostForm($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) {
     $submit_matches = FALSE;
+    if ($submit instanceof SafeMarkup) {
+      $submit = (string) $submit;
+    }
     $ajax = is_array($submit);
     if (isset($path)) {
       $this->drupalGet($path, $options);
@@ -2285,7 +2289,7 @@ protected function buildXPathQuery($xpath, array $args = array()) {
       // XPath 1.0 doesn't support a way to escape single or double quotes in a
       // string literal. We split double quotes out of the string, and encode
       // them separately.
-      if (is_string($value)) {
+      if (is_string($value) || $value instanceof SafeMarkup) {
         // Explode the text at the quote characters.
         $parts = explode('"', $value);
 
@@ -2776,7 +2780,7 @@ protected function assertRaw($raw, $message = '', $group = 'Other') {
     if (!$message) {
       $message = String::format('Raw "@raw" found', array('@raw' => $raw));
     }
-    return $this->assert(strpos($this->drupalGetContent(), $raw) !== FALSE, $message, $group);
+    return $this->assert(strpos($this->drupalGetContent(), (string) $raw) !== FALSE, $message, $group);
   }
 
   /**
@@ -2803,7 +2807,7 @@ protected function assertNoRaw($raw, $message = '', $group = 'Other') {
     if (!$message) {
       $message = String::format('Raw "@raw" not found', array('@raw' => $raw));
     }
-    return $this->assert(strpos($this->drupalGetContent(), $raw) === FALSE, $message, $group);
+    return $this->assert(strpos($this->drupalGetContent(), (string) $raw) === FALSE, $message, $group);
   }
 
   /**
@@ -2887,7 +2891,7 @@ protected function assertTextHelper($text, $message = '', $group, $not_exists) {
     if (!$message) {
       $message = !$not_exists ? String::format('"@text" found', array('@text' => $text)) : String::format('"@text" not found', array('@text' => $text));
     }
-    return $this->assert($not_exists == (strpos($this->plainTextContent, $text) === FALSE), $message, $group);
+    return $this->assert($not_exists == (strpos($this->plainTextContent, (string) $text) === FALSE), $message, $group);
   }
 
   /**
@@ -3701,6 +3705,7 @@ protected function assertMail($name, $value = '', $message = '', $group = 'E-mai
   protected function assertMailString($field_name, $string, $email_depth, $message = '', $group = 'Other') {
     $mails = $this->drupalGetMails();
     $string_found = FALSE;
+    $string = (string) $string;
     for ($i = count($mails) -1; $i >= count($mails) - $email_depth && $i >= 0; $i--) {
       $mail = $mails[$i];
       // Normalize whitespace, as we don't know what the mail system might have
diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php
index d061495..180be27 100644
--- a/core/modules/system/src/Form/ModulesListForm.php
+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -17,6 +17,7 @@
 use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Template\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Access\AccessManager;
 
@@ -162,7 +163,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' => new SafeMarkup('<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..b272148 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\Core\Template\SafeMarkup;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
diff --git a/core/modules/system/src/Tests/Action/ActionUnitTest.php b/core/modules/system/src/Tests/Action/ActionUnitTest.php
index 70fc663..7e2f651 100644
--- a/core/modules/system/src/Tests/Action/ActionUnitTest.php
+++ b/core/modules/system/src/Tests/Action/ActionUnitTest.php
@@ -45,7 +45,7 @@ protected function setUp() {
     parent::setUp();
 
     $this->actionManager = $this->container->get('plugin.manager.action');
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', array('sequences'));
   }
 
diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php
index 8edbb07..1458a0f 100644
--- a/core/modules/system/src/Tests/Common/RenderTest.php
+++ b/core/modules/system/src/Tests/Common/RenderTest.php
@@ -277,7 +277,8 @@ function testDrupalRenderBasics() {
     );
 
     foreach($types as $type) {
-      $this->assertIdentical(drupal_render($type['value']), $type['expected'], '"' . $type['name'] . '" input rendered correctly by drupal_render().');
+      $value = drupal_render($type['value']);
+      $this->assertIdentical((string) $value, $type['expected'], '"' . $type['name'] . '" input rendered correctly by drupal_render().');
     }
   }
 
@@ -394,14 +395,14 @@ function testDrupalRenderThemeArguments() {
       '#theme' => 'common_test_foo',
     );
     // Test that defaults work.
-    $this->assertEqual(drupal_render($element), 'foobar', 'Defaults work');
+    $this->assertEqual((string) drupal_render($element), 'foobar', 'Defaults work');
     $element = array(
       '#theme' => 'common_test_foo',
       '#foo' => $this->randomName(),
       '#bar' => $this->randomName(),
     );
     // Tests that passing arguments to the theme function works.
-    $this->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
+    $this->assertEqual((string) drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
   }
 
   /**
@@ -470,7 +471,7 @@ function testDrupalRenderPostRenderCache() {
     $element = $test_element;
     $element['#markup'] = '<p>#cache disabled</p>';
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
@@ -486,7 +487,7 @@ function testDrupalRenderPostRenderCache() {
     $element['#cache'] = array('cid' => 'post_render_cache_test_GET');
     $element['#markup'] = '<p>#cache enabled, GET</p>';
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -509,7 +510,7 @@ function testDrupalRenderPostRenderCache() {
     $element['#cache'] = array('cid' => 'post_render_cache_test_GET');
     $element['#markup'] = '<p>#cache enabled, GET</p>';
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -526,7 +527,7 @@ function testDrupalRenderPostRenderCache() {
     $element['#cache'] = array('cid' => 'post_render_cache_test_POST');
     $element['#markup'] = '<p>#cache enabled, POST</p>';
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -589,7 +590,7 @@ function testDrupalRenderChildrenPostRenderCache() {
     );
     $element = $test_element;
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -635,7 +636,7 @@ function testDrupalRenderChildrenPostRenderCache() {
     drupal_static_reset('_drupal_add_js');
     $element = $test_element;
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
@@ -647,7 +648,7 @@ function testDrupalRenderChildrenPostRenderCache() {
     unset($test_element['#cache']);
     $element = $test_element;
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $expected_settings = $context_1 + $context_2 + $context_3;
@@ -667,7 +668,7 @@ function testDrupalRenderChildrenPostRenderCache() {
     $element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent');
     $element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertIdentical($element['#markup'], '<p>overridden</p>', '#markup is overridden.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -742,7 +743,7 @@ function testDrupalRenderChildrenPostRenderCache() {
     $element = $test_element;
     $element['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_parent');
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $this->assertIdentical($settings['foo'], 'bar', 'Original JavaScript setting is added to the page.');
@@ -754,7 +755,7 @@ function testDrupalRenderChildrenPostRenderCache() {
     $element['child']['#cache']['keys'] = array('simpletest', 'drupal_render', 'children_post_render_cache', 'nested_cache_child');
     $element = $element['child'];
     $output = drupal_render($element);
-    $this->assertIdentical($output, '<p>overridden</p>', 'Output is overridden.');
+    $this->assertIdentical((string) $output, '<p>overridden</p>', 'Output is overridden.');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $expected_settings = $context_2 + $context_3;
@@ -790,7 +791,7 @@ function testDrupalRenderRenderCachePlaceholder() {
     drupal_static_reset('_drupal_add_js');
     $element = $test_element;
     $output = drupal_render($element);
-    $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+    $this->assertIdentical((string) $output, $expected_output, 'Placeholder was replaced in output');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
 
@@ -803,7 +804,7 @@ function testDrupalRenderRenderCachePlaceholder() {
     $element = $test_element;
     $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
     $output = drupal_render($element);
-    $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+    $this->assertIdentical((string) $output, $expected_output, 'Placeholder was replaced in output');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -840,7 +841,7 @@ function testDrupalRenderRenderCachePlaceholder() {
     $element = $test_element;
     $element['#cache'] = array('cid' => 'render_cache_placeholder_test_GET');
     $output = drupal_render($element);
-    $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+    $this->assertIdentical((string) $output, $expected_output, 'Placeholder was replaced in output');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -880,7 +881,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
     drupal_static_reset('_drupal_add_js');
     $element = $container;
     $output = drupal_render($element);
-    $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+    $this->assertIdentical((string) $output, $expected_output, 'Placeholder was replaced in output');
     $settings = $this->parseDrupalSettings(drupal_get_js());
     $this->assertIdentical($settings['common_test'], $context, '#attached is modified; JavaScript setting is added to page.');
 
@@ -899,7 +900,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
     $element['#children'] = drupal_render($child, TRUE);
     // Eventually, drupal_render() gets called on the root element.
     $output = drupal_render($element);
-    $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+    $this->assertIdentical((string) $output, $expected_output, 'Placeholder was replaced in output');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
@@ -996,7 +997,7 @@ function testDrupalRenderChildElementRenderCachePlaceholder() {
     $child = &$element['test_element'];
     $element['#children'] = drupal_render($child, TRUE);
     $output = drupal_render($element);
-    $this->assertIdentical($output, $expected_output, 'Placeholder was replaced in output');
+    $this->assertIdentical((string) $output, $expected_output, 'Placeholder was replaced in output');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $this->assertIdentical($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
     $settings = $this->parseDrupalSettings(drupal_get_js());
diff --git a/core/modules/system/src/Tests/Common/WriteRecordTest.php b/core/modules/system/src/Tests/Common/WriteRecordTest.php
index fd6ac63..ffe5fdb 100644
--- a/core/modules/system/src/Tests/Common/WriteRecordTest.php
+++ b/core/modules/system/src/Tests/Common/WriteRecordTest.php
@@ -7,19 +7,19 @@
 
 namespace Drupal\system\Tests\Common;
 
-use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\simpletest\WebTestBase;
 
 /**
  * Tests writing of data records with drupal_write_record().
  */
-class WriteRecordTest extends DrupalUnitTestBase {
+class WriteRecordTest extends WebTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('database_test');
+  public static $modules = array('database_test', 'node');
 
   public static function getInfo() {
     return array(
@@ -33,8 +33,6 @@ public static function getInfo() {
    * Tests the drupal_write_record() API function.
    */
   function testDrupalWriteRecord() {
-    $this->installSchema('database_test', array('test', 'test_null', 'test_serialized', 'test_composite_primary'));
-
     // Insert a record with no columns populated.
     $record = array();
     $insert_result = drupal_write_record('test', $record);
@@ -137,14 +135,15 @@ function testDrupalWriteRecord() {
     $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a valid update is run without changing any values.');
 
     // Insert an object record for a table with a multi-field primary key.
-    $composite_primary = new \stdClass();
-    $composite_primary->name = $this->randomName();
-    $composite_primary->age = mt_rand();
-    $insert_result = drupal_write_record('test_composite_primary', $composite_primary);
+    $node_access = new \stdClass();
+    $node_access->nid = mt_rand();
+    $node_access->gid = mt_rand();
+    $node_access->realm = $this->randomName();
+    $insert_result = drupal_write_record('node_access', $node_access);
     $this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.');
 
     // Update the record.
-    $update_result = drupal_write_record('test_composite_primary', $composite_primary, array('name', 'job'));
+    $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm'));
     $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.');
   }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityApiTest.php b/core/modules/system/src/Tests/Entity/EntityApiTest.php
index caa7bb8..4f693c9 100644
--- a/core/modules/system/src/Tests/Entity/EntityApiTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityApiTest.php
@@ -25,10 +25,16 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-
-    $this->installEntitySchema('entity_test_rev');
-    $this->installEntitySchema('entity_test_mul');
-    $this->installEntitySchema('entity_test_mulrev');
+    $this->installSchema('entity_test', array(
+      'entity_test_mul',
+      'entity_test_mul_property_data',
+      'entity_test_rev',
+      'entity_test_rev_revision',
+      'entity_test_mulrev',
+      'entity_test_mulrev_revision',
+      'entity_test_mulrev_property_data',
+      'entity_test_mulrev_property_revision'
+    ));
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php
index fa701a7..1d49a02 100644
--- a/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityCrudHookTest.php
@@ -44,14 +44,9 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('comment');
-
     $this->installSchema('user', array('users_data'));
-    $this->installSchema('file', array('file_usage'));
-    $this->installSchema('node', array('node_access'));
-    $this->installSchema('comment', array('comment_entity_statistics'));
+    $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access'));
+    $this->installSchema('comment', array('comment', 'comment_entity_statistics'));
   }
 
   /**
@@ -224,8 +219,7 @@ public function testCommentHooks() {
    * Tests hook invocations for CRUD operations on files.
    */
   public function testFileHooks() {
-    $this->installEntitySchema('file');
-
+    $this->installSchema('file', array('file_managed', 'file_usage'));
     $url = 'public://entity_crud_hook_test.file';
     file_put_contents($url, 'Test test test');
     $file = entity_create('file', array(
@@ -352,7 +346,7 @@ public function testNodeHooks() {
    * Tests hook invocations for CRUD operations on taxonomy terms.
    */
   public function testTaxonomyTermHooks() {
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
 
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => 'Test vocabulary',
@@ -421,7 +415,7 @@ public function testTaxonomyTermHooks() {
    * Tests hook invocations for CRUD operations on taxonomy vocabularies.
    */
   public function testTaxonomyVocabularyHooks() {
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
 
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => 'Test vocabulary',
diff --git a/core/modules/system/src/Tests/Entity/EntityFieldTest.php b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
index f532b1f..a838d2f 100644
--- a/core/modules/system/src/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFieldTest.php
@@ -41,12 +41,18 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-
-    $this->installEntitySchema('node');
-
-    $this->installEntitySchema('entity_test_rev');
-    $this->installEntitySchema('entity_test_mul');
-    $this->installEntitySchema('entity_test_mulrev');
+    $this->installSchema('user', array('users_data'));
+    $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access'));
+    $this->installSchema('entity_test', array(
+      'entity_test_mul',
+      'entity_test_mul_property_data',
+      'entity_test_rev',
+      'entity_test_rev_revision',
+      'entity_test_mulrev',
+      'entity_test_mulrev_revision',
+      'entity_test_mulrev_property_data',
+      'entity_test_mulrev_property_revision'
+    ));
 
     // Create the test field.
     entity_test_install();
diff --git a/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php
index c76fc97..ada2fbd 100644
--- a/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityLanguageTestBase.php
@@ -50,10 +50,16 @@ function setUp() {
 
     $this->languageManager = $this->container->get('language_manager');
 
-    $this->installEntitySchema('entity_test_rev');
-    $this->installEntitySchema('entity_test_mul');
-    $this->installEntitySchema('entity_test_mulrev');
-
+    $this->installSchema('entity_test', array(
+      'entity_test_mul',
+      'entity_test_mul_property_data',
+      'entity_test_rev',
+      'entity_test_rev_revision',
+      'entity_test_mulrev',
+      'entity_test_mulrev_revision',
+      'entity_test_mulrev_property_data',
+      'entity_test_mulrev_property_revision',
+    ));
     $this->installConfig(array('language'));
 
     // Create the test field.
diff --git a/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php b/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php
index 8d67666..e5d8cf4 100644
--- a/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityQueryRelationshipTest.php
@@ -70,7 +70,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
 
     // We want a taxonomy term reference field. It needs a vocabulary, terms,
     // a field and an instance. First, create the vocabulary.
diff --git a/core/modules/system/src/Tests/Entity/EntityQueryTest.php b/core/modules/system/src/Tests/Entity/EntityQueryTest.php
index b3cb9f7..07ee0e9 100644
--- a/core/modules/system/src/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityQueryTest.php
@@ -57,9 +57,7 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-
-    $this->installEntitySchema('entity_test_mulrev');
-
+    $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision'));
     $this->installConfig(array('language'));
 
     $figures = drupal_strtolower($this->randomName());
diff --git a/core/modules/system/src/Tests/Entity/EntityUUIDTest.php b/core/modules/system/src/Tests/Entity/EntityUUIDTest.php
index a0ec726..ec7134c 100644
--- a/core/modules/system/src/Tests/Entity/EntityUUIDTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityUUIDTest.php
@@ -23,9 +23,16 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('entity_test_rev');
-    $this->installEntitySchema('entity_test_mul');
-    $this->installEntitySchema('entity_test_mulrev');
+    $this->installSchema('entity_test', array(
+      'entity_test_mul',
+      'entity_test_mul_property_data',
+      'entity_test_rev',
+      'entity_test_rev_revision',
+      'entity_test_mulrev',
+      'entity_test_mulrev_revision',
+      'entity_test_mulrev_property_data',
+      'entity_test_mulrev_property_revision',
+    ));
   }
 
   /**
diff --git a/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php
index 24a4de9..8ea9daa 100644
--- a/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityUnitTestBase.php
@@ -42,11 +42,9 @@ public function setUp() {
     $this->entityManager = $this->container->get('entity.manager');
     $this->state = $this->container->get('state');
 
+    $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', 'sequences');
-
-    $this->installEntitySchema('user');
-    $this->installEntitySchema('entity_test');
-
+    $this->installSchema('entity_test', 'entity_test');
     $this->installConfig(array('field'));
   }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityValidationTest.php b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
index dceab23..09e1e54 100644
--- a/core/modules/system/src/Tests/Entity/EntityValidationTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityValidationTest.php
@@ -32,10 +32,17 @@ public static function getInfo() {
    */
   public function setUp() {
     parent::setUp();
-
-    $this->installEntitySchema('entity_test_rev');
-    $this->installEntitySchema('entity_test_mul');
-    $this->installEntitySchema('entity_test_mulrev');
+    $this->installSchema('user', array('users_data'));
+    $this->installSchema('entity_test', array(
+      'entity_test_mul',
+      'entity_test_mul_property_data',
+      'entity_test_rev',
+      'entity_test_rev_revision',
+      'entity_test_mulrev',
+      'entity_test_mulrev_revision',
+      'entity_test_mulrev_property_data',
+      'entity_test_mulrev_property_revision'
+    ));
 
     // Create the test field.
     entity_test_install();
diff --git a/core/modules/system/src/Tests/Entity/FieldAccessTest.php b/core/modules/system/src/Tests/Entity/FieldAccessTest.php
index d72b297..7395dfa 100644
--- a/core/modules/system/src/Tests/Entity/FieldAccessTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldAccessTest.php
@@ -44,9 +44,8 @@ protected function setUp() {
     // Install field configuration.
     $this->installConfig(array('field'));
     // The users table is needed for creating dummy user accounts.
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users'));
     // Register entity_test text field.
-    module_load_install('entity_test');
     entity_test_install();
   }
 
diff --git a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php
index c252acd..a030b70 100644
--- a/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldSqlStorageTest.php
@@ -66,8 +66,7 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-
-    $this->installEntitySchema('entity_test_rev');
+    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
     $entity_type = 'entity_test_rev';
 
     $this->field_name = strtolower($this->randomName());
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
index 4e8620a..a2fa299 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
@@ -57,10 +57,13 @@ public function setUp() {
         'provider' => 'plugin_test',
       ),
     );
-
-    $base_directory = DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/src';
-    $namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory));
-
+    $namespaces = new \ArrayObject(array(
+      'Drupal\plugin_test' => array(
+        // @todo Remove lib/Drupal/$module, once the switch to PSR-4 is complete.
+        DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test',
+        DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/src',
+      ),
+    ));
     $this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/fruit', $namespaces);
     $this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $namespaces);
   }
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php
index 76b0b95..09358cc 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/CustomAnnotationClassDiscoveryTest.php
@@ -41,9 +41,13 @@ protected function setUp() {
         'provider' => 'plugin_test',
       ),
     );
-
-    $base_directory = DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/src';
-    $root_namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory));
+    $root_namespaces = new \ArrayObject(array(
+      'Drupal\plugin_test' => array(
+        // @todo Remove lib/Drupal/$module, once the switch to PSR-4 is complete.
+        DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test',
+        DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/src',
+      ),
+    ));
 
     $this->discovery = new AnnotatedClassDiscovery('Plugin/plugin_test/custom_annotation', $root_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample');
     $this->emptyDiscovery = new AnnotatedClassDiscovery('Plugin/non_existing_module/non_existing_plugin_type', $root_namespaces, 'Drupal\plugin_test\Plugin\Annotation\PluginExample');
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
index f830fc0..1071e77 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
@@ -70,10 +70,21 @@ protected function setUp() {
         'provider' => 'plugin_test',
       ),
     );
-
-    $base_directory = DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/src';
+    // Due to the transition from PSR-0 to PSR-4, plugin classes can be in
+    // either one of
+    // - core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/
+    // - core/modules/system/tests/modules/plugin_test/src/
+    // To avoid false positives with "Drupal\plugin_test\Drupal\plugin_test\..",
+    // only one of them can be registered.
+    // Note: This precaution is only needed if the plugin namespace is identical
+    // with the module namespace. Usually this is not the case, because every
+    // plugin namespace is like "Drupal\$module\Plugin\..".
+    // @todo Clean this up, once the transition to PSR-4 is complete.
+    $extension_dir = DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test';
+    $base_directory = is_dir($extension_dir . '/lib/Drupal/plugin_test')
+      ? $extension_dir . '/lib/Drupal/plugin_test'
+      : $extension_dir . '/src';
     $namespaces = new \ArrayObject(array('Drupal\plugin_test' => $base_directory));
-
     $this->discovery = new AnnotatedClassDiscovery('', $namespaces);
     $empty_namespaces = new \ArrayObject();
     $this->emptyDiscovery = new AnnotatedClassDiscovery('', $empty_namespaces);
diff --git a/core/modules/system/src/Tests/TypedData/TypedDataTest.php b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
index 37c4af9..5893f7b 100644
--- a/core/modules/system/src/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/src/Tests/TypedData/TypedDataTest.php
@@ -44,7 +44,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setup();
 
-    $this->installEntitySchema('file');
+    $this->installSchema('file', array('file_managed', "file_usage"));
     $this->typedDataManager = $this->container->get('typed_data_manager');
   }
 
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 9ba1a1c..67664e9 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\Core\Template\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' => new SafeMarkup($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' => new SafeMarkup('<span class="text"> ' . drupal_render($module['description']) . '</span>'),
       '#attributes' => array('id' => $module['enable']['#id'] . '-description'),
-      '#description' => $description,
+      '#description' => new SafeMarkup($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..dfff949 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\Core\Template\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' => new SafeMarkup(($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' => new SafeMarkup($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/tests/modules/database_test/database_test.install b/core/modules/system/tests/modules/database_test/database_test.install
index 7c74c1c..866d453 100644
--- a/core/modules/system/tests/modules/database_test/database_test.install
+++ b/core/modules/system/tests/modules/database_test/database_test.install
@@ -245,34 +245,5 @@ function database_test_schema() {
     ),
   );
 
-  $schema['test_composite_primary'] = array(
-    'description' => 'Basic test table with a composite primary key',
-    'fields' => array(
-      'name' => array(
-        'description' => "A person's name",
-        'type' => 'varchar',
-        'length' => 50,
-        'not null' => TRUE,
-        'default' => '',
-        'binary' => TRUE,
-      ),
-      'age' => array(
-        'description' => "The person's age",
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'job' => array(
-        'description' => "The person's job",
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => 'Undefined',
-      ),
-    ),
-    'primary key' => array('name', 'age'),
-  );
-
   return $schema;
 }
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install
index 015b067..9e48029 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.install
+++ b/core/modules/system/tests/modules/entity_test/entity_test.install
@@ -42,7 +42,7 @@ function entity_test_install() {
  */
 function entity_test_schema() {
   // Schema for simple entity.
-  $schema['entity_test_example'] = array(
+  $schema['entity_test'] = array(
     'description' => 'Stores entity_test items.',
     'fields' => array(
       'id' => array(
@@ -50,8 +50,392 @@ function entity_test_schema() {
         'not null' => TRUE,
         'description' => 'Primary Key: Unique entity-test item ID.',
       ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'type' => array(
+        'description' => 'The bundle of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of the original variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {users}.uid of the associated user.',
+      ),
     ),
     'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
   );
+
+  // Schema for entity with revisions.
+  $schema['entity_test_rev'] = array(
+    'description' => 'Stores entity_test_rev items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
+      'revision_id' => array(
+        'description' => 'The current {entity_test_rev_property_revision}.revision_id version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'type' => array(
+        'description' => 'The bundle of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {users}.uid of the associated user.',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+  $schema['entity_test_rev_revision'] = array(
+    'description' => 'Stores entity_test_rev item property revisions.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_rev}.id of the test entity.',
+      ),
+      'revision_id' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier for this version.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {users}.uid of the associated user.',
+      ),
+    ),
+    'indexes' => array(
+      'user_id' => array('user_id'),
+    ),
+    'foreign keys' => array(
+      'user_id' => array('users' => 'uid'),
+      'id' => array('entity_test_rev' => 'id'),
+    ),
+    'primary key' => array('revision_id'),
+  );
+
+  // Schema for entity with data table.
+  $schema['entity_test_mul'] = array(
+    'description' => 'Stores entity_test_mul items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'type' => array(
+        'description' => 'The bundle of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of the original variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+  $schema['entity_test_mul_property_data'] = array(
+    'description' => 'Stores entity_test_mul item properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_mul}.id of the test entity.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => FALSE,
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {users}.uid of the associated user.',
+      ),
+    ),
+    'indexes' => array(
+      'user_id' => array('user_id'),
+    ),
+    'foreign keys' => array(
+      'user_id' => array('users' => 'uid'),
+      'id' => array('entity_test_mul' => 'id'),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
+
+  // Schema for entity with data table and revisions.
+  $schema['entity_test_mulrev'] = array(
+    'description' => 'Stores entity_test_mulrev items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique entity-test item ID.',
+      ),
+      'revision_id' => array(
+        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'type' => array(
+        'description' => 'The bundle of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+  $schema['entity_test_mulrev_revision'] = array(
+    'description' => 'Stores entity_test_rev item property revisions.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_rev}.id of the test entity.',
+      ),
+      'revision_id' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier for this version.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'foreign keys' => array(
+      'id' => array('entity_test_rev' => 'id'),
+    ),
+    'primary key' => array('revision_id'),
+  );
+  $schema['entity_test_mulrev_property_data'] = array(
+    'description' => 'Stores entity_test_mulrev item properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_mulrev}.id of the test entity.',
+      ),
+      'revision_id' => array(
+        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {users}.uid of the associated user.',
+      ),
+    ),
+    'indexes' => array(
+      'user_id' => array('user_id'),
+    ),
+    'foreign keys' => array(
+      'user_id' => array('users' => 'uid'),
+      'id' => array('entity_test_mulrev' => 'id'),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
+  $schema['entity_test_mulrev_property_revision'] = array(
+    'description' => 'Stores entity_test_mulrev item property revisions.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {entity_test_mulrev}.id of the test entity.',
+      ),
+      'revision_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The primary identifier for this version.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this variant of this test entity.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'default_langcode' => array(
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'name' => array(
+        'description' => 'The name of the test entity.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'user_id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => FALSE,
+        'default' => NULL,
+        'description' => 'The {users}.uid of the associated user.',
+      ),
+    ),
+    'indexes' => array(
+      'user_id' => array('user_id'),
+    ),
+    'foreign keys' => array(
+      'user_id' => array('users' => 'uid'),
+      'id' => array('entity_test_mulrev' => 'id'),
+    ),
+    'primary key' => array('revision_id', 'langcode'),
+  );
+
   return $schema;
 }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
index 0c0c753..6a8badd 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
@@ -28,6 +28,7 @@
  *   entity_keys = {
  *     "id" = "id",
  *     "uuid" = "uuid",
+ *     "revision" = "revision_id",
  *     "bundle" = "type"
  *   },
  *   links = {
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php
index e881b9a..481711f 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestLabelCallback.php
@@ -15,6 +15,7 @@
  *   label = @Translation("Entity test label callback"),
  *   field_cache = FALSE,
  *   base_table = "entity_test",
+ *   revision_table = "entity_test_revision",
  *   label_callback = "entity_test_label_callback",
  *   fieldable = TRUE,
  *   entity_keys = {
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
index a8abda5..9930262 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
@@ -46,4 +46,17 @@
  */
 class EntityTestMul extends EntityTest {
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields = parent::baseFieldDefinitions($entity_type);
+
+    $fields['default_langcode'] = FieldDefinition::create('boolean')
+      ->setLabel(t('Default language'))
+      ->setDescription(t('Flag to indicate whether this is the default language.'));
+
+    return $fields;
+  }
+
 }
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
index b30cbb1..21f6c54 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
@@ -46,4 +46,23 @@
  */
 class EntityTestMulRev extends EntityTestRev {
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
+    $fields = parent::baseFieldDefinitions($entity_type);
+
+    $fields['revision_id'] = FieldDefinition::create('integer')
+      ->setLabel(t('Revision ID'))
+      ->setDescription(t('The version id of the test entity.'))
+      ->setReadOnly(TRUE)
+      ->setSetting('unsigned', TRUE);
+
+    $fields['default_langcode'] = FieldDefinition::create('boolean')
+      ->setLabel(t('Default language'))
+      ->setDescription(t('Flag to indicate whether this is the default language.'));
+
+    return $fields;
+  }
+
 }
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/README.txt b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/README.txt
index ce8a861..433798b 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/README.txt
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/README.txt
@@ -1,3 +1,3 @@
 The classes in this directory act as a mock plugin type to test annotated class
 discovery. See the corresponding test file:
-/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+/core/modules/system/lib/Drupal/system/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index 8b6996a..510524a 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -11,7 +11,6 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldDefinition;
-use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\taxonomy\TermInterface;
@@ -165,7 +164,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['parent'] = FieldDefinition::create('integer')
       ->setLabel(t('Term Parents'))
       ->setDescription(t('The parents of this term.'))
-      ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED)
       // Save new terms with no parents by default.
       ->setSetting('default_value', 0)
       ->setSetting('unsigned', TRUE)
diff --git a/core/modules/taxonomy/src/TermStorage.php b/core/modules/taxonomy/src/TermStorage.php
index 381b0ab..b7b9201 100644
--- a/core/modules/taxonomy/src/TermStorage.php
+++ b/core/modules/taxonomy/src/TermStorage.php
@@ -8,7 +8,6 @@
 namespace Drupal\taxonomy;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Query\QueryInterface;
 use Drupal\Core\Entity\ContentEntityDatabaseStorage;
 
@@ -152,103 +151,4 @@ public function resetWeights($vid) {
       ->execute();
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['taxonomy_term_data']['fields']['weight']['not null'] = TRUE;
-    $schema['taxonomy_term_data']['fields']['name']['not null'] = TRUE;
-
-    unset($schema['taxonomy_term_data']['indexes']['field__vid']);
-    unset($schema['taxonomy_term_data']['indexes']['field__description__format']);
-    $schema['taxonomy_term_data']['indexes'] += array(
-      'taxonomy_term__tree' => array('vid', 'weight', 'name'),
-      'taxonomy_term__vid_name' => array('vid', 'name'),
-      'taxonomy_term__name' => array('name'),
-    );
-
-    $schema['taxonomy_term_hierarchy'] = array(
-      'description' => 'Stores the hierarchical relationship between terms.',
-      'fields' => array(
-        'tid' => array(
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-          'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.',
-        ),
-        'parent' => array(
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-          'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.",
-        ),
-      ),
-      'indexes' => array(
-        'parent' => array('parent'),
-      ),
-      'foreign keys' => array(
-        'taxonomy_term_data' => array(
-          'table' => 'taxonomy_term_data',
-          'columns' => array('tid' => 'tid'),
-        ),
-      ),
-      'primary key' => array('tid', 'parent'),
-    );
-
-    $schema['taxonomy_index'] = array(
-      'description' => 'Maintains denormalized information about node/term relationships.',
-      'fields' => array(
-        'nid' => array(
-          'description' => 'The {node}.nid this record tracks.',
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-        ),
-        'tid' => array(
-          'description' => 'The term ID.',
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-        ),
-        'sticky' => array(
-          'description' => 'Boolean indicating whether the node is sticky.',
-          'type' => 'int',
-          'not null' => FALSE,
-          'default' => 0,
-          'size' => 'tiny',
-        ),
-        'created' => array(
-          'description' => 'The Unix timestamp when the node was created.',
-          'type' => 'int',
-          'not null' => TRUE,
-          'default'=> 0,
-        ),
-      ),
-      'primary key' => array('nid', 'tid'),
-      'indexes' => array(
-        'term_node' => array('tid', 'sticky', 'created'),
-      ),
-      'foreign keys' => array(
-        'tracked_node' => array(
-          'table' => 'node',
-          'columns' => array('nid' => 'nid'),
-        ),
-        'term' => array(
-          'table' => 'taxonomy_term_data',
-          'columns' => array('tid' => 'tid'),
-        ),
-      ),
-    );
-
-    return $schema;
-  }
-
 }
diff --git a/core/modules/taxonomy/src/Tests/TaxonomyTermReferenceItemTest.php b/core/modules/taxonomy/src/Tests/TaxonomyTermReferenceItemTest.php
index 78c877f..0a67140 100644
--- a/core/modules/taxonomy/src/Tests/TaxonomyTermReferenceItemTest.php
+++ b/core/modules/taxonomy/src/Tests/TaxonomyTermReferenceItemTest.php
@@ -42,7 +42,8 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', 'taxonomy_term_data');
+    $this->installSchema('taxonomy', 'taxonomy_term_hierarchy');
 
     $vocabulary = entity_create('taxonomy_vocabulary', array(
       'name' => $this->randomName(),
diff --git a/core/modules/taxonomy/src/Tests/TermTranslationUITest.php b/core/modules/taxonomy/src/Tests/TermTranslationUITest.php
index c51428c..a027ade 100644
--- a/core/modules/taxonomy/src/Tests/TermTranslationUITest.php
+++ b/core/modules/taxonomy/src/Tests/TermTranslationUITest.php
@@ -121,10 +121,7 @@ function testTranslateLinkVocabularyAdminPage() {
     $this->admin_user = $this->drupalCreateUser(array_merge(parent::getTranslatorPermissions(), array('access administration pages', 'administer taxonomy')));
     $this->drupalLogin($this->admin_user);
 
-    $values = array(
-      'name' => $this->randomName(),
-    );
-    $translatable_tid = $this->createEntity($values, $this->langcodes[0], $this->vocabulary->id());
+    $translatable_tid = $this->createEntity(array(), $this->langcodes[0], $this->vocabulary->id());
 
     // Create an untranslatable vocabulary.
     $untranslatable_vocabulary = entity_create('taxonomy_vocabulary', array(
@@ -136,10 +133,7 @@ function testTranslateLinkVocabularyAdminPage() {
     ));
     $untranslatable_vocabulary->save();
 
-    $values = array(
-      'name' => $this->randomName(),
-    );
-    $untranslatable_tid = $this->createEntity($values, $this->langcodes[0], $untranslatable_vocabulary->id());
+    $untranslatable_tid = $this->createEntity(array(), $this->langcodes[0], $untranslatable_vocabulary->id());
 
     // Verify translation links.
     $this->drupalGet('admin/structure/taxonomy/manage/' .  $this->vocabulary->id() . '/overview');
diff --git a/core/modules/taxonomy/src/Tests/TermValidationTest.php b/core/modules/taxonomy/src/Tests/TermValidationTest.php
index f545403..737f662 100644
--- a/core/modules/taxonomy/src/Tests/TermValidationTest.php
+++ b/core/modules/taxonomy/src/Tests/TermValidationTest.php
@@ -34,7 +34,7 @@ public static function getInfo() {
    */
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', array('taxonomy_term_data'));
   }
 
   /**
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
new file mode 100644
index 0000000..ae5dc7f
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.install
@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the taxonomy module.
+ */
+
+use Drupal\Core\Entity\EntityTypeInterface;
+
+/**
+ * Implements hook_schema().
+ */
+function taxonomy_schema() {
+  $schema['taxonomy_term_data'] = array(
+    'description' => 'Stores term information.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique term ID.',
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'vid' => array(
+        'type' => 'varchar',
+        'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The ID of the vocabulary to which the term is assigned.',
+      ),
+      'langcode' => array(
+        'description' => 'The {language}.langcode of this term.',
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The term name.',
+      ),
+      'description__value' => array(
+        'type' => 'text',
+        'not null' => FALSE,
+        'size' => 'big',
+        'description' => 'A description of the term.',
+      ),
+      'description__format' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+        'description' => 'The filter format ID of the description.',
+      ),
+      'weight' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The weight of this term in relation to other terms.',
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the term was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('tid'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+    'indexes' => array(
+      'taxonomy_tree' => array('vid', 'weight', 'name'),
+      'vid_name' => array('vid', 'name'),
+      'name' => array('name'),
+    ),
+  );
+
+  $schema['taxonomy_term_hierarchy'] = array(
+    'description' => 'Stores the hierarchical relationship between terms.',
+    'fields' => array(
+      'tid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Primary Key: The {taxonomy_term_data}.tid of the term.',
+      ),
+      'parent' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => "Primary Key: The {taxonomy_term_data}.tid of the term's parent. 0 indicates no parent.",
+      ),
+    ),
+    'indexes' => array(
+      'parent' => array('parent'),
+    ),
+    'foreign keys' => array(
+      'taxonomy_term_data' => array(
+        'table' => 'taxonomy_term_data',
+        'columns' => array('tid' => 'tid'),
+      ),
+    ),
+    'primary key' => array('tid', 'parent'),
+  );
+
+  $schema['taxonomy_index'] = array(
+    'description' => 'Maintains denormalized information about node/term relationships.',
+    'fields' => array(
+      'nid' => array(
+        'description' => 'The {node}.nid this record tracks.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'tid' => array(
+         'description' => 'The term ID.',
+         'type' => 'int',
+         'unsigned' => TRUE,
+         'not null' => TRUE,
+         'default' => 0,
+      ),
+      'sticky' => array(
+        'description' => 'Boolean indicating whether the node is sticky.',
+        'type' => 'int',
+        'not null' => FALSE,
+        'default' => 0,
+        'size' => 'tiny',
+      ),
+      'created' => array(
+        'description' => 'The Unix timestamp when the node was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default'=> 0,
+      ),
+    ),
+    'primary key' => array('nid', 'tid'),
+    'indexes' => array(
+      'term_node' => array('tid', 'sticky', 'created'),
+    ),
+    'foreign keys' => array(
+      'tracked_node' => array(
+        'table' => 'node',
+        'columns' => array('nid' => 'nid'),
+      ),
+      'term' => array(
+        'table' => 'taxonomy_term_data',
+        'columns' => array('tid' => 'tid'),
+      ),
+    ),
+  );
+
+  return $schema;
+}
diff --git a/core/modules/text/src/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/src/Tests/Formatter/TextPlainUnitTest.php
index 3357d1d..1c35b24 100644
--- a/core/modules/text/src/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/src/Tests/Formatter/TextPlainUnitTest.php
@@ -48,7 +48,7 @@ function setUp() {
 
     // Configure the theme system.
     $this->installConfig(array('system', 'field'));
-    $this->installEntitySchema('entity_test');
+    $this->installSchema('entity_test', 'entity_test');
 
     // @todo Add helper methods for all of the following.
 
diff --git a/core/modules/text/src/Tests/TextWithSummaryItemTest.php b/core/modules/text/src/Tests/TextWithSummaryItemTest.php
index c98245e..68a6179 100644
--- a/core/modules/text/src/Tests/TextWithSummaryItemTest.php
+++ b/core/modules/text/src/Tests/TextWithSummaryItemTest.php
@@ -50,7 +50,7 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('entity_test_rev');
+    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
 
     // Create the necessary formats.
     $this->installConfig(array('filter'));
diff --git a/core/modules/user/src/AccountForm.php b/core/modules/user/src/AccountForm.php
index 5ac1c00..42a2292 100644
--- a/core/modules/user/src/AccountForm.php
+++ b/core/modules/user/src/AccountForm.php
@@ -378,12 +378,9 @@ public function validate(array $form, array &$form_state) {
       // Move text value for user signature into 'signature'.
       $form_state['values']['signature'] = $form_state['values']['signature']['value'];
 
-      // @todo Make the user signature field use a widget to benefit from
-      //   automatic typed data validation in https://drupal.org/node/2227381.
-      $field_definitions = $this->entityManager->getFieldDefinitions('user', $this->getEntity()->bundle());
-      $max_length = $field_definitions['signature']->getSetting('max_length');
-      if (drupal_strlen($form_state['values']['signature']) > $max_length) {
-        $this->setFormError('signature', $form_state, $this->t('The signature is too long: it must be %max characters or less.', array('%max' => $max_length)));
+      $user_schema = drupal_get_schema('users');
+      if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
+        $this->setFormError('signature', $form_state, $this->t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
       }
     }
   }
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 6c0fb85..25eb641 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -18,9 +18,6 @@
 /**
  * Defines the user entity class.
  *
- * The base table name here is plural, despite Drupal table naming standards,
- * because "user" is a reserved word in many databases.
- *
  * @ContentEntityType(
  *   id = "user",
  *   label = @Translation("User"),
@@ -503,11 +500,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['status'] = FieldDefinition::create('boolean')
       ->setLabel(t('User status'))
-      ->setDescription(t('Whether the user is active or blocked.'))
-      // @todo As the status has access implications users should be created as
-      //   blocked by default and activated explicitly if needed. See
-      //   https://drupal.org/node/2248969.
-      ->setSetting('default_value', TRUE);
+      ->setDescription(t('Whether the user is active (1) or blocked (0).'))
+      ->setSetting('default_value', 1);
 
     $fields['created'] = FieldDefinition::create('created')
       ->setLabel(t('Created'))
@@ -531,7 +525,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     // @todo Convert this to entity_reference_field, see
     // https://drupal.org/node/2044859.
     $fields['roles'] = FieldDefinition::create('string')
-      ->setCustomStorage(TRUE)
       ->setLabel(t('Roles'))
       ->setCardinality(FieldDefinitionInterface::CARDINALITY_UNLIMITED)
       ->setDescription(t('The roles the user has.'));
diff --git a/core/modules/user/src/Tests/UserAdminTest.php b/core/modules/user/src/Tests/UserAdminTest.php
index f71d1e6..d8d384d 100644
--- a/core/modules/user/src/Tests/UserAdminTest.php
+++ b/core/modules/user/src/Tests/UserAdminTest.php
@@ -34,22 +34,13 @@ public static function getInfo() {
    */
   function testUserAdmin() {
     $user_a = $this->drupalCreateUser();
-    $user_a->name = 'User A';
     $user_a->mail = $this->randomName() . '@example.com';
     $user_a->save();
     $user_b = $this->drupalCreateUser(array('administer taxonomy'));
-    $user_b->name = 'User B';
-    $user_b->save();
     $user_c = $this->drupalCreateUser(array('administer taxonomy'));
-    $user_c->name = 'User C';
-    $user_c->save();
 
     // Create admin user to delete registered user.
     $admin_user = $this->drupalCreateUser(array('administer users'));
-    // Use a predictable name so that we can reliably order the user admin page
-    // by name.
-    $admin_user->name = 'Admin user';
-    $admin_user->save();
     $this->drupalLogin($admin_user);
     $this->drupalGet('admin/people');
     $this->assertText($user_a->getUsername(), 'Found user A on admin users page');
@@ -95,12 +86,8 @@ function testUserAdmin() {
     $this->assertTrue($account->isActive(), 'User C not blocked');
     $edit = array();
     $edit['action'] = 'user_block_user_action';
-    $edit['user_bulk_form[4]'] = TRUE;
-    $this->drupalPostForm('admin/people', $edit, t('Apply'), array(
-      // Sort the table by username so that we know reliably which user will be
-      // targeted with the blocking action.
-      'query' => array('order' => 'name', 'sort' => 'asc')
-    ));
+    $edit['user_bulk_form[1]'] = TRUE;
+    $this->drupalPostForm('admin/people', $edit, t('Apply'));
     $account = user_load($user_c->id(), TRUE);
     $this->assertTrue($account->isBlocked(), 'User C blocked');
 
@@ -113,12 +100,8 @@ function testUserAdmin() {
     // Test unblocking of a user from /admin/people page and sending of activation mail
     $editunblock = array();
     $editunblock['action'] = 'user_unblock_user_action';
-    $editunblock['user_bulk_form[4]'] = TRUE;
-    $this->drupalPostForm('admin/people', $editunblock, t('Apply'), array(
-      // Sort the table by username so that we know reliably which user will be
-      // targeted with the blocking action.
-      'query' => array('order' => 'name', 'sort' => 'asc')
-    ));
+    $editunblock['user_bulk_form[1]'] = TRUE;
+    $this->drupalPostForm('admin/people', $editunblock, t('Apply'));
     $account = user_load($user_c->id(), TRUE);
     $this->assertTrue($account->isActive(), 'User C unblocked');
     $this->assertMail("to", $account->getEmail(), "Activation mail sent to user C");
diff --git a/core/modules/user/src/Tests/UserInstallTest.php b/core/modules/user/src/Tests/UserInstallTest.php
index 9fdb8db..7014a03 100644
--- a/core/modules/user/src/Tests/UserInstallTest.php
+++ b/core/modules/user/src/Tests/UserInstallTest.php
@@ -37,9 +37,7 @@ public static function getInfo() {
    */
   protected function setUp() {
     parent::setUp();
-    $this->container->get('module_handler')->loadInclude('user', 'install');
-    $this->installEntitySchema('user');
-    user_install();
+    $this->installSchema('user', array('users'));
   }
 
 
@@ -47,6 +45,7 @@ protected function setUp() {
    * Test that the initial users have correct values.
    */
   public function testUserInstall() {
+    user_install();
     $anon = db_query('SELECT * FROM {users} WHERE uid = 0')->fetchObject();
     $admin = db_query('SELECT * FROM {users} WHERE uid = 1')->fetchObject();
     $this->assertFalse(empty($anon->uuid), 'Anon user has a UUID');
diff --git a/core/modules/user/src/Tests/UserValidationTest.php b/core/modules/user/src/Tests/UserValidationTest.php
index 06d42b9..499877e 100644
--- a/core/modules/user/src/Tests/UserValidationTest.php
+++ b/core/modules/user/src/Tests/UserValidationTest.php
@@ -36,7 +36,7 @@ public static function getInfo() {
    */
   public function setUp() {
     parent::setUp();
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users'));
     $this->installSchema('system', array('sequences'));
   }
 
diff --git a/core/modules/user/src/Tests/Views/UserUnitTestBase.php b/core/modules/user/src/Tests/Views/UserUnitTestBase.php
index 16ba123..eafc620 100644
--- a/core/modules/user/src/Tests/Views/UserUnitTestBase.php
+++ b/core/modules/user/src/Tests/Views/UserUnitTestBase.php
@@ -48,7 +48,7 @@ protected function setUp() {
 
     ViewTestData::createTestViews(get_class($this), array('user_test_views'));
 
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', 'sequences');
 
     $entity_manager = $this->container->get('entity.manager');
diff --git a/core/modules/user/src/UserStorage.php b/core/modules/user/src/UserStorage.php
index 44d323f..d5d91ca 100644
--- a/core/modules/user/src/UserStorage.php
+++ b/core/modules/user/src/UserStorage.php
@@ -47,13 +47,15 @@ class UserStorage extends ContentEntityDatabaseStorage implements UserStorageInt
    *   The database connection to be used.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
+   * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
+   *   The UUID Service.
    * @param \Drupal\Core\Password\PasswordInterface $password
    *   The password hashing service.
    * @param \Drupal\user\UserDataInterface $user_data
    *   The user data service.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $database, $entity_manager);
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityManagerInterface $entity_manager, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_type, $database, $entity_manager, $uuid_service);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -67,6 +69,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_type,
       $container->get('database'),
       $container->get('entity.manager'),
+      $container->get('uuid'),
       $container->get('password'),
       $container->get('user.data')
     );
@@ -151,59 +154,4 @@ public function updateLastLoginTimestamp(UserInterface $account) {
       ->execute();
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getSchema() {
-    $schema = parent::getSchema();
-
-    // Marking the respective fields as NOT NULL makes the indexes more
-    // performant.
-    $schema['users']['fields']['access']['not null'] = TRUE;
-    $schema['users']['fields']['created']['not null'] = TRUE;
-    $schema['users']['fields']['name']['not null'] = TRUE;
-
-    // The "users" table does not use serial identifiers.
-    $schema['users']['fields']['uid']['type'] = 'int';
-    $schema['users']['indexes'] += array(
-      'user__access' => array('access'),
-      'user__created' => array('created'),
-      'user__mail' => array('mail'),
-    );
-    $schema['users']['unique keys'] += array(
-      'user__name' => array('name'),
-    );
-
-    $schema['users_roles'] = array(
-      'description' => 'Maps users to roles.',
-      'fields' => array(
-        'uid' => array(
-          'type' => 'int',
-          'unsigned' => TRUE,
-          'not null' => TRUE,
-          'default' => 0,
-          'description' => 'Primary Key: {users}.uid for user.',
-        ),
-        'rid' => array(
-          'type' => 'varchar',
-          'length' => 64,
-          'not null' => TRUE,
-          'description' => 'Primary Key: ID for the role.',
-        ),
-      ),
-      'primary key' => array('uid', 'rid'),
-      'indexes' => array(
-        'rid' => array('rid'),
-      ),
-      'foreign keys' => array(
-        'user' => array(
-          'table' => 'users',
-          'columns' => array('uid' => 'uid'),
-        ),
-      ),
-    );
-
-    return $schema;
-  }
-
 }
diff --git a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml
index 8b62fc7..b16cb3e 100644
--- a/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml
+++ b/core/modules/user/tests/modules/user_test_views/test_views/views.view.test_filter_permission.yml
@@ -115,20 +115,7 @@ display:
             group_items: {  }
           reduce_duplicates: '1'
           plugin_id: user_permissions
-      sorts:
-        uid:
-          id: uid
-          table: users
-          field: uid
-          relationship: none
-          group_type: group
-          admin_label: ''
-          order: ASC
-          exposed: false
-          expose:
-            label: ''
-          plugin_id: standard
-          provider: views
+      sorts: {  }
 human_name: test_filter_permission
 module: views
 id: test_filter_permission
diff --git a/core/modules/user/user.info.yml b/core/modules/user/user.info.yml
index dbdb520..45a421a 100644
--- a/core/modules/user/user.info.yml
+++ b/core/modules/user/user.info.yml
@@ -6,5 +6,3 @@ version: VERSION
 core: 8.x
 required: true
 configure: user.admin_index
-dependencies:
-  - entity
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index e0658c5..086a5a0 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -9,6 +9,130 @@
  * Implements hook_schema().
  */
 function user_schema() {
+  // The table name here is plural, despite Drupal table naming standards,
+  // because "user" is a reserved word in many databases.
+  $schema['users'] = array(
+    'description' => 'Stores user data.',
+    'fields' => array(
+      'uid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique user ID.',
+        'default' => 0,
+      ),
+      'uuid' => array(
+        'description' => 'Unique Key: Universally unique identifier for this entity.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+      ),
+      'name' => array(
+        'type' => 'varchar',
+        'length' => 60,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Unique user name.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "The {language}.langcode of the user's profile.",
+      ),
+      'pass' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "User's password (hashed).",
+      ),
+      'mail' => array(
+        'type' => 'varchar',
+        'length' => 254,
+        'not null' => FALSE,
+        'default' => '',
+        'description' => "User's e-mail address.",
+      ),
+      'signature' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "User's signature.",
+      ),
+      'signature_format' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+        'description' => 'The filter format ID of the signature.',
+      ),
+      'created' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Timestamp for when user was created.',
+      ),
+      'access' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Timestamp for previous time user accessed the site.',
+      ),
+      'login' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => "Timestamp for user's last login.",
+      ),
+      'status' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'size' => 'tiny',
+        'description' => 'Whether the user is active(1) or blocked(0).',
+      ),
+      'timezone' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => FALSE,
+        'description' => "User's time zone.",
+      ),
+      'preferred_langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode that the user prefers for receiving emails and viewing the site.',
+      ),
+      'preferred_admin_langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode that the user prefers for viewing administration pages.',
+      ),
+      'init' => array(
+        'type' => 'varchar',
+        'length' => 254,
+        'not null' => FALSE,
+        'default' => '',
+        'description' => 'E-mail address used for initial account creation.',
+      ),
+    ),
+    'indexes' => array(
+      'access' => array('access'),
+      'created' => array('created'),
+      'mail' => array('mail'),
+    ),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+      'name' => array('name'),
+    ),
+    'primary key' => array('uid'),
+  );
+
   $schema['users_data'] = array(
     'description' => 'Stores module data as key/value pairs per user.',
     'fields' => array(
@@ -57,6 +181,35 @@ function user_schema() {
     ),
   );
 
+  $schema['users_roles'] = array(
+    'description' => 'Maps users to roles.',
+    'fields' => array(
+      'uid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Primary Key: {users}.uid for user.',
+      ),
+      'rid' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'description' => 'Primary Key: ID for the role.',
+      ),
+    ),
+    'primary key' => array('uid', 'rid'),
+    'indexes' => array(
+      'rid' => array('rid'),
+    ),
+    'foreign keys' => array(
+      'user' => array(
+        'table' => 'users',
+        'columns' => array('uid' => 'uid'),
+      ),
+    ),
+  );
+
   return $schema;
 }
 
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 4b28c81..8936c27 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -6,6 +6,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Session\AnonymousUserSession;
 use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\Core\Url;
 use Drupal\file\Entity\File;
 use Drupal\user\Entity\Role;
@@ -692,7 +693,7 @@ function theme_username($variables) {
     // We have a link path, so we should generate a link using l().
     // Additional classes may be added as array elements like
     // $variables['link_options']['attributes']['class'][] = 'myclass';
-    $output = l($variables['name'] . $variables['extra'], $variables['link_path'], $variables['link_options']);
+    $output = l(new SafeMarkup($variables['name'] . $variables['extra']), $variables['link_path'], $variables['link_options']);
   }
   else {
     // Modules may have added important attributes so they must be included
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index 87a5674..c3e19aa 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -275,10 +275,7 @@ public function calculateDependencies() {
     // Ensure that the view is dependent on the module that provides the schema
     // for the base table.
     $schema = $this->drupalGetSchema($this->base_table);
-    // @todo Entity base tables are no longer registered in hook_schema(). Once
-    //   we automate the views data for entity types add the entity type
-    //   type provider as a dependency. See https://drupal.org/node/1740492.
-    if ($schema && $this->module != $schema['module']) {
+    if ($this->module != $schema['module']) {
       $this->addDependency('module', $schema['module']);
     }
 
@@ -286,6 +283,7 @@ public function calculateDependencies() {
     foreach (Views::getHandlerTypes() as $type) {
       $handler_types[] = $type['plural'];
     }
+
     foreach ($this->get('display') as $display) {
       // Collect all dependencies of all handlers.
       foreach ($handler_types as $handler_type) {
@@ -298,10 +296,10 @@ public function calculateDependencies() {
             // Add the additional dependencies from the handler configuration.
             if (!empty($handler['dependencies'])) {
               $this->addDependencies($handler['dependencies']);
+            }
           }
         }
       }
-    }
 
       // Collect all dependencies of plugins.
       foreach (Views::getPluginTypes('plugin') as $plugin_type) {
diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php
index cbff847..da8ddb1 100644
--- a/core/modules/views/src/Plugin/views/HandlerBase.php
+++ b/core/modules/views/src/Plugin/views/HandlerBase.php
@@ -236,7 +236,7 @@ public function getField($field = NULL) {
    * @param $type
    *   The type of sanitization needed. If not provided, String::checkPlain() is used.
    *
-   * @return string
+   * @return \Drupal\Core\Template\SafeMarkup
    *   Returns the safe value.
    */
   public function sanitizeValue($value, $type = NULL) {
diff --git a/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php b/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
index 6f0add5..9af6b70 100644
--- a/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
+++ b/core/modules/views/src/Plugin/views/area/TokenizeAreaPluginBase.php
@@ -51,13 +51,13 @@ public function tokenForm(&$form, &$form_state) {
     // Get a list of the available fields and arguments for token replacement.
     $options = array();
     foreach ($this->view->display_handler->getHandlers('field') as $field => $handler) {
-      $options[t('Fields')]["[$field]"] = $handler->adminLabel();
+      $options[(string) t('Fields')]["[$field]"] = $handler->adminLabel();
     }
 
     $count = 0; // This lets us prepare the key as we want it printed.
     foreach ($this->view->display_handler->getHandlers('argument') as $handler) {
-      $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
-      $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
+      $options[(string) t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
+      $options[(string) t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
     }
 
     if (!empty($options)) {
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index b960d5b..c40a3c7 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -1732,8 +1732,8 @@ public function buildOptionsForm(&$form, &$form_state) {
         $options = array();
         $count = 0; // This lets us prepare the key as we want it printed.
         foreach ($this->view->display_handler->getHandlers('argument') as $handler) {
-          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
-          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
+          $options[(string) t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
+          $options[(string) t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
         }
 
         // Default text.
diff --git a/core/modules/views/src/Plugin/views/field/FieldPluginBase.php b/core/modules/views/src/Plugin/views/field/FieldPluginBase.php
index 5eb5765..b5581ea 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\Core\Template\SafeMarkup;
 use Drupal\views\Plugin\views\HandlerBase;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
 use Drupal\views\ResultRow;
@@ -850,18 +851,18 @@ public function buildOptionsForm(&$form, &$form_state) {
       // Setup the tokens for fields.
       $previous = $this->getPreviousFieldLabels();
       foreach ($previous as $id => $label) {
-        $options[t('Fields')]["[$id]"] = $label;
+        $options[(string) t('Fields')]["[$id]"] = $label;
       }
       // Add the field to the list of options.
-      $options[t('Fields')]["[{$this->options['id']}]"] = $this->label();
+      $options[(string) t('Fields')]["[{$this->options['id']}]"] = $this->label();
 
       $count = 0; // This lets us prepare the key as we want it printed.
       foreach ($this->view->display_handler->getHandlers('argument') as $arg => $handler) {
-        $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
-        $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
+        $options[(string) t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->adminLabel()));
+        $options[(string) t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->adminLabel()));
       }
 
-      $this->documentSelfTokens($options[t('Fields')]);
+      $this->documentSelfTokens($options[(string) t('Fields')]);
 
       // Default text.
       $output = '<p>' . t('You must add some additional fields to this display before using this field. These fields may be marked as <em>Exclude from display</em> if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.') . '</p>';
@@ -1172,6 +1173,10 @@ public function advancedRender(ResultRow $values) {
         $this->last_render = $this->renderText($alter);
       }
     }
+    // @TODO: this is very dicey!
+    if ($this->last_render && is_string($this->last_render)) {
+      $this->last_render = new SafeMarkup($this->last_render);
+    }
 
     return $this->last_render;
   }
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index 902b543..42b8d0b 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -1159,8 +1159,7 @@ protected function prepareFilterSelectOptions(&$options) {
         $this->prepareFilterSelectOptions($options[$value]);
       }
       // FAPI has some special value to allow hierarchy.
-      // @see _form_options_flatten
-      elseif (is_object($label)) {
+      elseif (is_object($label) && isset($options[$value]->option)) {
         $this->prepareFilterSelectOptions($options[$value]->option);
       }
       else {
diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php
index 5b51afd..175e7f4 100644
--- a/core/modules/views/src/Plugin/views/query/Sql.php
+++ b/core/modules/views/src/Plugin/views/query/Sql.php
@@ -1425,6 +1425,7 @@ function execute(ViewExecutable $view) {
 
         $result = $query->execute();
         $result->setFetchMode(\PDO::FETCH_CLASS, 'Drupal\views\ResultRow');
+
         $view->result = iterator_to_array($result);
 
         $view->pager->postExecute($view->result);
diff --git a/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php b/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php
index 7a85bbc..673869b 100644
--- a/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php
+++ b/core/modules/views/src/Plugin/views/relationship/GroupwiseMax.php
@@ -130,10 +130,10 @@ public function buildOptionsForm(&$form, &$form_state) {
         // TODO: check the field is the correct sort?
         // or let users hang themselves at this stage and check later?
         if ($view->type == 'Default') {
-          $views[t('Default Views')][$view->storage->id()] = $view->storage->id();
+          $views[(string) t('Default Views')][$view->storage->id()] = $view->storage->id();
         }
         else {
-          $views[t('Existing Views')][$view->storage->id()] = $view->storage->id();
+          $views[(string) t('Existing Views')][$view->storage->id()] = $view->storage->id();
         }
       }
     }
diff --git a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
index 35c8c61..869ad88 100644
--- a/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
+++ b/core/modules/views/src/Tests/Entity/RowEntityRenderersTest.php
@@ -56,9 +56,8 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('user');
-    $this->installSchema('node', array('node_access'));
+    $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access'));
+    $this->installSchema('user', array('users'));
     $this->installConfig(array('node', 'language'));
 
     // The node.view route must exist when nodes are rendered.
diff --git a/core/modules/views/src/Tests/Handler/AreaTextTest.php b/core/modules/views/src/Tests/Handler/AreaTextTest.php
index 3ab29d7..2ac78f8 100644
--- a/core/modules/views/src/Tests/Handler/AreaTextTest.php
+++ b/core/modules/views/src/Tests/Handler/AreaTextTest.php
@@ -38,7 +38,7 @@ protected function setUp() {
     parent::setUp();
 
     $this->installConfig(array('system', 'filter'));
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users'));
   }
 
   public function testAreaText() {
diff --git a/core/modules/views/src/Tests/Handler/HandlerAliasTest.php b/core/modules/views/src/Tests/Handler/HandlerAliasTest.php
index ac9b920..caec4c7 100644
--- a/core/modules/views/src/Tests/Handler/HandlerAliasTest.php
+++ b/core/modules/views/src/Tests/Handler/HandlerAliasTest.php
@@ -35,7 +35,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('user');
+    $this->installSchema('user', 'users');
   }
 
   /**
diff --git a/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php
index 5542619..fae3515 100644
--- a/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php
+++ b/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php
@@ -26,7 +26,7 @@
    * Overrides \Drupal\views\Tests\ViewUnitTestBase::setUpFixtures().
    */
   protected function setUpFixtures() {
-    $this->installEntitySchema('user');
+    $this->installSchema('user', array('users', 'users_roles'));
     $this->installConfig(array('user'));
     parent::setUpFixtures();
 
diff --git a/core/modules/views/src/Tests/Plugin/RowEntityTest.php b/core/modules/views/src/Tests/Plugin/RowEntityTest.php
index 8cecbd1..0b406f5 100644
--- a/core/modules/views/src/Tests/Plugin/RowEntityTest.php
+++ b/core/modules/views/src/Tests/Plugin/RowEntityTest.php
@@ -53,7 +53,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('taxonomy_term');
+    $this->installSchema('taxonomy', array('taxonomy_term_data', 'taxonomy_term_hierarchy'));
     $this->installConfig(array('taxonomy'));
     \Drupal::service('router.builder')->rebuild();
   }
diff --git a/core/modules/views/src/Tests/QueryGroupByTest.php b/core/modules/views/src/Tests/QueryGroupByTest.php
index ff0c225..7b7d0b7 100644
--- a/core/modules/views/src/Tests/QueryGroupByTest.php
+++ b/core/modules/views/src/Tests/QueryGroupByTest.php
@@ -49,7 +49,7 @@ public static function getInfo() {
   protected function setUp() {
     parent::setUp();
 
-    $this->installEntitySchema('entity_test');
+    $this->installSchema('entity_test', array('entity_test'));
 
     $this->storage = $this->container->get('entity.manager')->getStorage('entity_test');
   }
diff --git a/core/modules/views/src/Tests/ViewExecutableTest.php b/core/modules/views/src/Tests/ViewExecutableTest.php
index 183e1af..74787f1 100644
--- a/core/modules/views/src/Tests/ViewExecutableTest.php
+++ b/core/modules/views/src/Tests/ViewExecutableTest.php
@@ -84,12 +84,10 @@ public static function getInfo() {
   }
 
   protected function setUpFixtures() {
-    $this->installEntitySchema('user');
-    $this->installEntitySchema('node');
-    $this->installEntitySchema('comment');
-    $this->installSchema('comment', array('comment_entity_statistics'));
+    $this->installSchema('user', array('users'));
+    $this->installSchema('node', array('node', 'node_field_data'));
+    $this->installSchema('comment', array('comment', 'comment_entity_statistics'));
     $this->installConfig(array('field'));
-
     entity_create('node_type', array(
       'type' => 'page',
       'name' => 'Page',
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 216bc93..5a93d2e 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -14,6 +14,7 @@
 use Drupal\Core\Database\Query\AlterableInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\views\Plugin\Derivative\ViewsLocalTask;
 use Drupal\Core\Template\AttributeArray;
 use Drupal\views\ViewExecutable;
@@ -903,7 +904,7 @@ function views_pre_render_views_form_views_form($element) {
   }
 
   // Apply substitutions to the rendered output.
-  $element['output']['#markup'] = str_replace($search, $replace, $element['output']['#markup']);
+  $element['output']['#markup'] = SafeMarkup::strReplace($search, $replace, $element['output']['#markup']);
 
   // Sort, render and add remaining form fields.
   $children = Element::children($element, TRUE);
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 86e3e85..28155ea 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -9,6 +9,7 @@
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Template\SafeMarkup;
 use Drupal\views\Form\ViewsForm;
 use Drupal\views\ViewExecutable;
 
@@ -525,6 +526,7 @@ function template_preprocess_views_view_table(&$variables) {
 
     // Render the header labels.
     if ($field == $column && empty($fields[$field]->options['exclude'])) {
+      $safe = TRUE;
       $label = String::checkPlain(!empty($fields[$field]) ? $fields[$field]->label() : '');
       if (empty($options['info'][$field]['sortable']) || !$fields[$field]->clickSortable()) {
         $variables['header'][$field]['content'] = $label;
@@ -542,7 +544,10 @@ function template_preprocess_views_view_table(&$variables) {
             '#theme' => 'tablesort_indicator',
             '#style' => $initial,
           );
-          $label .= drupal_render($tablesort_indicator);
+          $markup = drupal_render($tablesort_indicator);
+          // $label is safe.
+          $safe = $markup instanceof SafeMarkup;
+          $label .= $markup;
         }
 
         $query['order'] = $field;
@@ -552,7 +557,7 @@ function template_preprocess_views_view_table(&$variables) {
           'attributes' => array('title' => $title),
           'query' => $query,
         );
-        $variables['header'][$field]['content'] = l($label, current_path(), $link_options);
+        $variables['header'][$field]['content'] = l($safe ? new SafeMarkup($label) : $label, current_path(), $link_options);
       }
 
       // Set up the header label class.
@@ -633,7 +638,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::concat(new SafeMarkup('<' . $element_type . '>'), $field_output, new SafeMarkup('</' . $element_type . '>'));
         }
 
         // Only bother with separators and stuff if the field shows up.
@@ -641,13 +646,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::concat($column_reference['content'], Xss::filterAdmin($options['info'][$column]['separator']));
             }
           }
           else {
             $column_reference['content'] = '';
           }
-          $column_reference['content'] .= $field_output;
+          $column_reference['content'] = SafeMarkup::concat($column_reference['content'], $field_output);
         }
       }
       $column_reference['attributes'] = new Attribute($column_reference['attributes']);
diff --git a/core/modules/views_ui/admin.inc b/core/modules/views_ui/admin.inc
index e90ef35..3fe9789 100644
--- a/core/modules/views_ui/admin.inc
+++ b/core/modules/views_ui/admin.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Tags;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Views;
@@ -89,16 +90,18 @@ function views_ui_add_ajax_trigger(&$wrapping_element, $trigger_key, $refresh_pa
   // always give the button a unique #value, rather than playing around with
   // #name.
   $button_title = !empty($triggering_element['#title']) ? $triggering_element['#title'] : $trigger_key;
-  if (empty($seen_buttons[$button_title])) {
-    $wrapping_element[$button_key]['#value'] = t('Update "@title" choice', array(
+  $button_title_string = (string) $button_title;
+  if (empty($seen_buttons[$button_title_string])) {
+    // This code relies on check_plain()'ing the string because of the quotes.
+    $wrapping_element[$button_key]['#value'] = (string) t('Update "@title" choice', array(
       '@title' => $button_title,
     ));
-    $seen_buttons[$button_title] = 1;
+    $seen_buttons[$button_title_string] = 1;
   }
   else {
-    $wrapping_element[$button_key]['#value'] = t('Update "@title" choice (@number)', array(
+    $wrapping_element[$button_key]['#value'] = (string) t('Update "@title" choice (@number)', array(
       '@title' => $button_title,
-      '@number' => ++$seen_buttons[$button_title],
+      '@number' => ++$seen_buttons[$button_title_string],
     ));
   }
 
diff --git a/core/modules/views_ui/src/Controller/ViewsUIController.php b/core/modules/views_ui/src/Controller/ViewsUIController.php
index a09b063..735c95d 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\Core\Template\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/Tests/HandlerTest.php b/core/modules/views_ui/src/Tests/HandlerTest.php
index 5a00b03..200937f 100644
--- a/core/modules/views_ui/src/Tests/HandlerTest.php
+++ b/core/modules/views_ui/src/Tests/HandlerTest.php
@@ -163,7 +163,7 @@ public function testBrokenHandlers() {
 
       $this->drupalGet($href);
       $result = $this->xpath('//h1');
-      $this->assertTrue(strpos((string) $result[0], $text) !== FALSE, 'Ensure the broken handler text was found.');
+      $this->assertTrue(strpos((string) $result[0], (string) $text) !== FALSE, 'Ensure the broken handler text was found.');
 
       $description_args = array(
         '@module' => 'views',
diff --git a/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php
index 68ad5b0..a468589 100644
--- a/core/modules/views_ui/src/ViewListBuilder.php
+++ b/core/modules/views_ui/src/ViewListBuilder.php
@@ -230,7 +230,7 @@ protected function getDisplaysList(EntityInterface $view) {
     foreach ($view->get('display') as $display) {
       $definition = $this->displayManager->getDefinition($display['display_plugin']);
       if (!empty($definition['admin'])) {
-        $displays[$definition['admin']] = TRUE;
+        $displays[(string) $definition['admin']] = TRUE;
       }
     }
 
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index 7b8e87c..32b3384 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -18,6 +18,8 @@
       <!-- Exclude Drush tests. -->
       <exclude>./drush/tests</exclude>
       <!-- Exclude special-case files from config's test modules. -->
+      <!-- @todo Remove /lib/Drupal/config_test after the transition to PSR-4. -->
+      <exclude>./modules/config/tests/config_test/lib/Drupal/config_test</exclude>
       <exclude>./modules/config/tests/config_test/src</exclude>
     </testsuite>
   </testsuites>
diff --git a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
index cc77d6d..6e60480 100644
--- a/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/ContentEntityDatabaseStorageTest.php
@@ -23,34 +23,6 @@
 class ContentEntityDatabaseStorageTest extends UnitTestCase {
 
   /**
-   * The content entity database storage used in this test.
-   *
-   * @var \Drupal\Core\Entity\ContentEntityDatabaseStorage|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityStorage;
-
-  /**
-   * The mocked entity type used in this test.
-   *
-   * @var \Drupal\Core\Entity\ContentEntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityType;
-
-  /**
-   * An array of field definitions used for this test, keyed by field name.
-   *
-   * @var \Drupal\Core\Field\FieldDefinition[]|\PHPUnit_Framework_MockObject_MockObject[]
-   */
-  protected $fieldDefinitions = array();
-
-  /**
-   * The mocked entity manager used in this test.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityManager;
-
-  /**
    * {@inheritdoc}
    */
   public static function getInfo() {
@@ -62,875 +34,27 @@ public static function getInfo() {
   }
 
   /**
-   * {@inheritdoc}
-   */
-  public function setUp() {
-    $this->entityType = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface');
-    $this->entityType->expects($this->any())
-      ->method('id')
-      ->will($this->returnValue('entity_test'));
-
-    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
-  }
-
-  /**
-   * Tests ContentEntityDatabaseStorage::getBaseTable().
-   *
-   * @param string $base_table
-   *   The base table to be returned by the mocked entity type.
-   * @param string $expected
-   *   The expected return value of
-   *   ContentEntityDatabaseStorage::getBaseTable().
-   *
-   * @covers ::__construct()
-   * @covers ::getBaseTable()
-   *
-   * @dataProvider providerTestGetBaseTable
-   */
-  public function testGetBaseTable($base_table, $expected) {
-    $this->entityType->expects($this->once())
-      ->method('getBaseTable')
-      ->will($this->returnValue('entity_test'));
-
-    $this->setUpEntityStorage();
-
-    $this->assertSame($expected, $this->entityStorage->getBaseTable());
-  }
-
-  /**
-   * Provides test data for testGetBaseTable().
-   *
-   * @return array[]
-   *   An nested array where each inner array has the base table to be returned
-   *   by the mocked entity type as the first value and the expected return
-   *   value of ContentEntityDatabaseStorage::getBaseTable() as the second
-   *   value.
-   */
-  public function providerTestGetBaseTable() {
-    return array(
-      // Test that the entity type's base table is used, if provided.
-      array('entity_test', 'entity_test'),
-      // Test that the storage falls back to the entity type ID.
-      array(NULL, 'entity_test'),
-    );
-  }
-
-  /**
-   * Tests ContentEntityDatabaseStorage::getRevisionTable().
-   *
-   * @param string $revision_table
-   *   The revision table to be returned by the mocked entity type.
-   * @param string $expected
-   *   The expected return value of
-   *   ContentEntityDatabaseStorage::getRevisionTable().
-   *
-   * @cover ::__construct()
-   * @covers ::getRevisionTable()
-   *
-   * @dataProvider providerTestGetRevisionTable
-   */
-  public function testGetRevisionTable($revision_table, $expected) {
-    $this->entityType->expects($this->once())
-      ->method('isRevisionable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->once())
-      ->method('getRevisionTable')
-      ->will($this->returnValue($revision_table));
-
-    $this->setUpEntityStorage();
-
-    $this->assertSame($expected, $this->entityStorage->getRevisionTable());
-  }
-
-  /**
-   * Provides test data for testGetRevisionTable().
-   *
-   * @return array[]
-   *   An nested array where each inner array has the revision table to be
-   *   returned by the mocked entity type as the first value and the expected
-   *   return value of ContentEntityDatabaseStorage::getRevisionTable() as the
-   *   second value.
-   */
-  public function providerTestGetRevisionTable() {
-    return array(
-      // Test that the entity type's revision table is used, if provided.
-      array('entity_test_revision', 'entity_test_revision'),
-      // Test that the storage falls back to the entity type ID with a
-      // '_revision' suffix.
-      array(NULL, 'entity_test_revision'),
-    );
-  }
-
-  /**
-   * Tests ContentEntityDatabaseStorage::getDataTable().
-   *
-   * @cover ::__construct()
-   * @covers ::getDataTable()
-   */
-  public function testGetDataTable() {
-    $this->entityType->expects($this->once())
-      ->method('isTranslatable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->exactly(2))
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-
-    $this->setUpEntityStorage();
-
-    $this->assertSame('entity_test_field_data', $this->entityStorage->getDataTable());
-  }
-
-  /**
-   * Tests ContentEntityDatabaseStorage::getRevisionDataTable().
-   *
-   * @param string $revision_data_table
-   *   The revision data table to be returned by the mocked entity type.
-   * @param string $expected
-   *   The expected return value of
-   *   ContentEntityDatabaseStorage::getRevisionDataTable().
-   *
-   * @cover ::__construct()
-   * @covers ::getRevisionDataTable()
-   *
-   * @dataProvider providerTestGetRevisionDataTable
-   */
-  public function testGetRevisionDataTable($revision_data_table, $expected) {
-    $this->entityType->expects($this->once())
-      ->method('isRevisionable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->once())
-      ->method('isTranslatable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->exactly(2))
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-    $this->entityType->expects($this->once())
-      ->method('getRevisionDataTable')
-      ->will($this->returnValue($revision_data_table));
-
-    $this->setUpEntityStorage();
-
-    $actual = $this->entityStorage->getRevisionDataTable();
-    $this->assertSame($expected, $actual);
-  }
-
-  /**
-   * Provides test data for testGetRevisionDataTable().
-   *
-   * @return array[]
-   *   An nested array where each inner array has the revision data table to be
-   *   returned by the mocked entity type as the first value and the expected
-   *   return value of ContentEntityDatabaseStorage::getRevisionDataTable() as
-   *   the second value.
-   */
-  public function providerTestGetRevisionDataTable() {
-    return array(
-      // Test that the entity type's revision data table is used, if provided.
-      array('entity_test_field_revision', 'entity_test_field_revision'),
-      // Test that the storage falls back to the entity type ID with a
-      // '_field_revision' suffix.
-      array(NULL, 'entity_test_field_revision'),
-    );
-  }
-
-  /**
-   * Tests ContentEntityDatabaseStorage::getSchema().
-   *
-   * @covers ::__construct()
-   * @covers ::getSchema()
-   * @covers ::schemaHandler()
-   * @covers ::getTableMapping()
-   */
-  public function testGetSchema() {
-    $columns = array(
-      'value' => array(
-        'type' => 'int',
-      ),
-    );
-
-    $this->fieldDefinitions['id'] = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $this->fieldDefinitions['id']->expects($this->once())
-      ->method('getColumns')
-      ->will($this->returnValue($columns));
-    $this->fieldDefinitions['id']->expects($this->once())
-      ->method('getSchema')
-      ->will($this->returnValue(array('columns' => $columns)));
-
-    $this->entityType->expects($this->once())
-      ->method('getKeys')
-      ->will($this->returnValue(array('id' => 'id')));
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        // EntityStorageBase::__construct()
-        array('id', 'id'),
-        // ContentEntityStorageBase::__construct()
-        array('uuid', NULL),
-        array('bundle', NULL),
-        // ContentEntitySchemaHandler::initializeBaseTable()
-        array('id' => 'id'),
-        // ContentEntitySchemaHandler::processBaseTable()
-        array('id' => 'id'),
-      )));
-
-    $this->entityManager->expects($this->once())
-      ->method('getFieldStorageDefinitions')
-      ->with($this->entityType->id())
-      ->will($this->returnValue($this->fieldDefinitions));
-
-    $this->setUpEntityStorage();
-
-    $expected = array(
-      'entity_test' => array(
-        'description' => 'The base table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'type' => 'serial',
-            'description' => NULL,
-            'not null' => TRUE,
-          ),
-        ),
-        'primary key' => array('id'),
-        'indexes' => array(),
-        'foreign keys' => array(),
-      ),
-    );
-    $this->assertEquals($expected, $this->entityStorage->getSchema());
-
-    // Test that repeated calls do not result in repeatedly instantiating
-    // ContentEntitySchemaHandler as getFieldStorageDefinitions() is only
-    // expected to be called once.
-    $this->assertEquals($expected, $this->entityStorage->getSchema());
-  }
-
-  /**
-   * Tests getTableMapping() with an empty entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   */
-  public function testGetTableMappingEmpty() {
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-    $this->assertSame(array('entity_test'), $mapping->getTableNames());
-    $this->assertSame(array(), $mapping->getFieldNames('entity_test'));
-    $this->assertSame(array(), $mapping->getExtraColumns('entity_test'));
-  }
-
-  /**
-   * Tests getTableMapping() with a simple entity type.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingSimple(array $entity_keys) {
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        array('id', $entity_keys['id']),
-        array('uuid', $entity_keys['uuid']),
-        array('bundle', $entity_keys['bundle']),
-      )));
-
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-
-    $this->assertEquals(array('entity_test'), $mapping->getTableNames());
-
-    $expected = array_values(array_filter($entity_keys));
-    $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
-
-    $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
-  }
-
-  /**
-   * Tests getTableMapping() with a simple entity type with some base fields.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingSimpleWithFields(array $entity_keys) {
-    $base_field_names = array('title', 'description', 'owner');
-    $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
-
-    $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $this->fieldDefinitions = array_fill_keys($field_names, $definition);
-
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        array('id', $entity_keys['id']),
-        array('uuid', $entity_keys['uuid']),
-        array('bundle', $entity_keys['bundle']),
-      )));
-
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-    $this->assertEquals(array('entity_test'), $mapping->getTableNames());
-    $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
-    $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
-  }
-
-  /**
-   * Provides test data for testGetTableMappingSimple().
-   *
-   * @return array[]
-   *   A nested array, where each inner array has a single value being a  map of
-   *   entity keys to use for the mocked entity type.
-   */
-  public function providerTestGetTableMappingSimple() {
-    return array(
-      array(array(
-        'id' => 'test_id',
-        'bundle' => NULL,
-        'uuid' => NULL,
-      )),
-      array(array(
-        'id' => 'test_id',
-        'bundle' => 'test_bundle',
-        'uuid' => NULL,
-      )),
-      array(array(
-        'id' => 'test_id',
-        'bundle' => NULL,
-        'uuid' => 'test_uuid',
-      )),
-      array(array(
-        'id' => 'test_id',
-        'bundle' => 'test_bundle',
-        'uuid' => 'test_uuid',
-      )),
-    );
-  }
-
-  /**
-   * Tests getTableMapping() with a revisionable, non-translatable entity type.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingRevisionable(array $entity_keys) {
-    // This allows to re-use the data provider.
-    $entity_keys = array(
-      'id' => $entity_keys['id'],
-      'revision' => 'test_revision',
-      'bundle' => $entity_keys['bundle'],
-      'uuid' => $entity_keys['uuid'],
-    );
-
-    $this->entityType->expects($this->exactly(2))
-      ->method('isRevisionable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        array('id', $entity_keys['id']),
-        array('uuid', $entity_keys['uuid']),
-        array('bundle', $entity_keys['bundle']),
-        array('revision', $entity_keys['revision']),
-      )));
-
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-
-    $expected = array('entity_test', 'entity_test_revision');
-    $this->assertEquals($expected, $mapping->getTableNames());
-
-    $expected = array_values(array_filter($entity_keys));
-    $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
-    $expected = array($entity_keys['id'], $entity_keys['revision']);
-    $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
-
-    $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
-    $this->assertEquals(array(), $mapping->getExtraColumns('entity_test_revision'));
-  }
-
-  /**
-   * Tests getTableMapping() with a revisionable entity type with fields.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
-    // This allows to re-use the data provider.
-    $entity_keys = array(
-      'id' => $entity_keys['id'],
-      'revision' => 'test_revision',
-      'bundle' => $entity_keys['bundle'],
-      'uuid' => $entity_keys['uuid'],
-    );
-
-    // PHPUnit does not allow for multiple data providers.
-    $test_cases = array(
-      array(),
-      array('revision_timestamp'),
-      array('revision_uid'),
-      array('log'),
-      array('revision_timestamp', 'revision_uid'),
-      array('revision_timestamp', 'log'),
-      array('revision_uid', 'log'),
-      array('revision_timestamp', 'revision_uid', 'log'),
-    );
-    foreach ($test_cases as $revision_metadata_field_names) {
-      $this->setUp();
-
-      $base_field_names = array('title');
-      $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
-
-      $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-      $this->fieldDefinitions = array_fill_keys($field_names, $definition);
-
-      $revisionable_field_names = array('description', 'owner');
-      $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-      // isRevisionable() is only called once, but we re-use the same definition
-      // for all revisionable fields.
-      $definition->expects($this->any())
-        ->method('isRevisionable')
-        ->will($this->returnValue(TRUE));
-      $field_names = array_merge(
-        $field_names,
-        $revisionable_field_names
-      );
-      $this->fieldDefinitions += array_fill_keys(
-        array_merge($revisionable_field_names, $revision_metadata_field_names),
-        $definition
-      );
-
-      $this->entityType->expects($this->exactly(2))
-        ->method('isRevisionable')
-        ->will($this->returnValue(TRUE));
-      $this->entityType->expects($this->any())
-        ->method('getKey')
-        ->will($this->returnValueMap(array(
-          array('id', $entity_keys['id']),
-          array('uuid', $entity_keys['uuid']),
-          array('bundle', $entity_keys['bundle']),
-          array('revision', $entity_keys['revision']),
-        )));
-
-      $this->setUpEntityStorage();
-
-      $mapping = $this->entityStorage->getTableMapping();
-
-      $expected = array('entity_test', 'entity_test_revision');
-      $this->assertEquals($expected, $mapping->getTableNames());
-
-      $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
-      $expected = array_merge(
-        array($entity_keys['id'], $entity_keys['revision']),
-        $revisionable_field_names,
-        $revision_metadata_field_names
-      );
-      $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
-
-      $this->assertEquals(array(), $mapping->getExtraColumns('entity_test'));
-      $this->assertEquals(array(), $mapping->getExtraColumns('entity_test_revision'));
-    }
-  }
-
-  /**
-   * Tests getTableMapping() with a non-revisionable, translatable entity type.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingTranslatable(array $entity_keys) {
-    // This allows to re-use the data provider.
-    $entity_keys['langcode'] = 'langcode';
-
-    $this->entityType->expects($this->exactly(2))
-      ->method('isTranslatable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->exactly(3))
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        array('id', $entity_keys['id']),
-        array('uuid', $entity_keys['uuid']),
-        array('bundle', $entity_keys['bundle']),
-      )));
-
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-
-    $expected = array('entity_test', 'entity_test_field_data');
-    $this->assertEquals($expected, $mapping->getTableNames());
-
-    $expected = array_values(array_filter($entity_keys));
-    $actual = $mapping->getFieldNames('entity_test');
-    $this->assertEquals($expected, $actual);
-    // The UUID is not stored on the data table.
-    $expected = array_values(array_filter(array(
-      $entity_keys['id'],
-      $entity_keys['bundle'],
-      $entity_keys['langcode'],
-    )));
-    $actual = $mapping->getFieldNames('entity_test_field_data');
-    $this->assertEquals($expected, $actual);
-
-    $expected = array();
-    $actual = $mapping->getExtraColumns('entity_test');
-    $this->assertEquals($expected, $actual);
-    $expected = array('default_langcode');
-    $actual = $mapping->getExtraColumns('entity_test_field_data');
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Tests getTableMapping() with a translatable entity type with fields.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingTranslatableWithFields(array $entity_keys) {
-    // This allows to re-use the data provider.
-    $entity_keys['langcode'] = 'langcode';
-
-    $base_field_names = array('title', 'description', 'owner');
-    $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
-
-    $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $this->fieldDefinitions = array_fill_keys($field_names, $definition);
-
-    $this->entityType->expects($this->exactly(2))
-      ->method('isTranslatable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->exactly(3))
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        array('id', $entity_keys['id']),
-        array('uuid', $entity_keys['uuid']),
-        array('bundle', $entity_keys['bundle']),
-      )));
-
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-
-    $expected = array('entity_test', 'entity_test_field_data');
-    $this->assertEquals($expected, $mapping->getTableNames());
-
-    $expected = array_values(array_filter($entity_keys));
-    $actual = $mapping->getFieldNames('entity_test');
-    $this->assertEquals($expected, $actual);
-    // The UUID is not stored on the data table.
-    $expected = array_merge(array_filter(array(
-      $entity_keys['id'],
-      $entity_keys['bundle'],
-      $entity_keys['langcode'],
-    )), $base_field_names);
-    $actual = $mapping->getFieldNames('entity_test_field_data');
-    $this->assertEquals($expected, $actual);
-
-    $expected = array();
-    $actual = $mapping->getExtraColumns('entity_test');
-    $this->assertEquals($expected, $actual);
-    $expected = array('default_langcode');
-    $actual = $mapping->getExtraColumns('entity_test_field_data');
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Tests getTableMapping() with a revisionable, translatable entity type.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingRevisionableTranslatable(array $entity_keys) {
-    // This allows to re-use the data provider.
-    $entity_keys = array(
-      'id' => $entity_keys['id'],
-      'revision' => 'test_revision',
-      'bundle' => $entity_keys['bundle'],
-      'uuid' => $entity_keys['uuid'],
-      'langcode' => 'langcode',
-    );
-
-    $this->entityType->expects($this->exactly(2))
-      ->method('isRevisionable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->exactly(2))
-      ->method('isTranslatable')
-      ->will($this->returnValue(TRUE));
-    $this->entityType->expects($this->exactly(3))
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-    $this->entityType->expects($this->any())
-      ->method('getKey')
-      ->will($this->returnValueMap(array(
-        array('id', $entity_keys['id']),
-        array('uuid', $entity_keys['uuid']),
-        array('bundle', $entity_keys['bundle']),
-        array('revision', $entity_keys['revision']),
-      )));
-
-    $this->setUpEntityStorage();
-
-    $mapping = $this->entityStorage->getTableMapping();
-
-    $expected = array(
-      'entity_test',
-      'entity_test_field_data',
-      'entity_test_revision',
-      'entity_test_field_revision',
-    );
-    $this->assertEquals($expected, $mapping->getTableNames());
-
-    // The language code is not stored on the base table, but on the revision
-    // table.
-    $expected = array_values(array_filter(array(
-      $entity_keys['id'],
-      $entity_keys['revision'],
-      $entity_keys['bundle'],
-      $entity_keys['uuid'],
-    )));
-    $actual = $mapping->getFieldNames('entity_test');
-    $this->assertEquals($expected, $actual);
-    // The revision table on the other hand does not store the bundle and the
-    // UUID.
-    $expected = array_values(array_filter(array(
-      $entity_keys['id'],
-      $entity_keys['revision'],
-      $entity_keys['langcode'],
-    )));
-    $actual = $mapping->getFieldNames('entity_test_revision');
-    $this->assertEquals($expected, $actual);
-    // The UUID is not stored on the data table.
-    $expected = array_values(array_filter(array(
-      $entity_keys['id'],
-      $entity_keys['revision'],
-      $entity_keys['bundle'],
-      $entity_keys['langcode'],
-    )));
-    $actual = $mapping->getFieldNames('entity_test_field_data');
-    $this->assertEquals($expected, $actual);
-    // The data revision also does not store the bundle.
-    $expected = array_values(array_filter(array(
-      $entity_keys['id'],
-      $entity_keys['revision'],
-      $entity_keys['langcode'],
-    )));
-    $actual = $mapping->getFieldNames('entity_test_field_revision');
-    $this->assertEquals($expected, $actual);
-
-    $expected = array();
-    $actual = $mapping->getExtraColumns('entity_test');
-    $this->assertEquals($expected, $actual);
-    $actual = $mapping->getExtraColumns('entity_test_revision');
-    $this->assertEquals($expected, $actual);
-    $expected = array('default_langcode');
-    $actual = $mapping->getExtraColumns('entity_test_field_data');
-    $this->assertEquals($expected, $actual);
-    $actual = $mapping->getExtraColumns('entity_test_field_revision');
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Tests getTableMapping() with a complex entity type with fields.
-   *
-   * @param string[] $entity_keys
-   *   A map of entity keys to use for the mocked entity type.
-   *
-   * @covers ::__construct()
-   * @covers ::getTableMapping()
-   *
-   * @dataProvider providerTestGetTableMappingSimple()
-   */
-  public function testGetTableMappingRevisionableTranslatableWithFields(array $entity_keys) {
-    // This allows to re-use the data provider.
-    $entity_keys = array(
-      'id' => $entity_keys['id'],
-      'revision' => 'test_revision',
-      'bundle' => $entity_keys['bundle'],
-      'uuid' => $entity_keys['uuid'],
-      'langcode' => 'langcode',
-    );
-
-    // PHPUnit does not allow for multiple data providers.
-    $test_cases = array(
-      array(),
-      array('revision_timestamp'),
-      array('revision_uid'),
-      array('log'),
-      array('revision_timestamp', 'revision_uid'),
-      array('revision_timestamp', 'log'),
-      array('revision_uid', 'log'),
-      array('revision_timestamp', 'revision_uid', 'log'),
-    );
-    foreach ($test_cases as $revision_metadata_field_names) {
-      $this->setUp();
-
-      $base_field_names = array('title');
-      $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
-
-      $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-      $this->fieldDefinitions = array_fill_keys($field_names, $definition);
-
-      $revisionable_field_names = array('description', 'owner');
-      $definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-      // isRevisionable() is only called once, but we re-use the same definition
-      // for all revisionable fields.
-      $definition->expects($this->any())
-        ->method('isRevisionable')
-        ->will($this->returnValue(TRUE));
-      $this->fieldDefinitions += array_fill_keys(
-        array_merge($revisionable_field_names, $revision_metadata_field_names),
-        $definition
-      );
-
-      $this->entityType->expects($this->exactly(2))
-        ->method('isRevisionable')
-        ->will($this->returnValue(TRUE));
-      $this->entityType->expects($this->exactly(2))
-        ->method('isTranslatable')
-        ->will($this->returnValue(TRUE));
-      $this->entityType->expects($this->exactly(3))
-        ->method('getDataTable')
-        ->will($this->returnValue('entity_test_field_data'));
-      $this->entityType->expects($this->any())
-        ->method('getKey')
-        ->will($this->returnValueMap(array(
-          array('id', $entity_keys['id']),
-          array('uuid', $entity_keys['uuid']),
-          array('bundle', $entity_keys['bundle']),
-          array('revision', $entity_keys['revision']),
-        )));
-
-      $this->setUpEntityStorage();
-
-      $mapping = $this->entityStorage->getTableMapping();
-
-      $expected = array(
-        'entity_test',
-        'entity_test_field_data',
-        'entity_test_revision',
-        'entity_test_field_revision',
-      );
-      $this->assertEquals($expected, $mapping->getTableNames());
-
-      $expected = array(
-        'entity_test',
-        'entity_test_field_data',
-        'entity_test_revision',
-        'entity_test_field_revision',
-      );
-      $this->assertEquals($expected, $mapping->getTableNames());
-
-      // The language code is not stored on the base table, but on the revision
-      // table.
-      $expected = array_values(array_filter(array(
-        $entity_keys['id'],
-        $entity_keys['revision'],
-        $entity_keys['bundle'],
-        $entity_keys['uuid'],
-      )));
-      $actual = $mapping->getFieldNames('entity_test');
-      $this->assertEquals($expected, $actual);
-      // The revision table on the other hand does not store the bundle and the
-      // UUID.
-      $expected = array_merge(array_filter(array(
-        $entity_keys['id'],
-        $entity_keys['revision'],
-        $entity_keys['langcode'],
-      )), $revision_metadata_field_names);
-      $actual = $mapping->getFieldNames('entity_test_revision');
-      $this->assertEquals($expected, $actual);
-      // The UUID is not stored on the data table.
-      $expected = array_merge(array_filter(array(
-        $entity_keys['id'],
-        $entity_keys['revision'],
-        $entity_keys['bundle'],
-        $entity_keys['langcode'],
-      )), $base_field_names, $revisionable_field_names);
-      $actual = $mapping->getFieldNames('entity_test_field_data');
-      $this->assertEquals($expected, $actual);
-      // The data revision also does not store the bundle.
-      $expected = array_merge(array_filter(array(
-        $entity_keys['id'],
-        $entity_keys['revision'],
-        $entity_keys['langcode'],
-      )), $revisionable_field_names);
-      $actual = $mapping->getFieldNames('entity_test_field_revision');
-      $this->assertEquals($expected, $actual);
-
-      $expected = array();
-      $actual = $mapping->getExtraColumns('entity_test');
-      $this->assertEquals($expected, $actual);
-      $actual = $mapping->getExtraColumns('entity_test_revision');
-      $this->assertEquals($expected, $actual);
-      $expected = array('default_langcode');
-      $actual = $mapping->getExtraColumns('entity_test_field_data');
-      $this->assertEquals($expected, $actual);
-      $actual = $mapping->getExtraColumns('entity_test_field_revision');
-      $this->assertEquals($expected, $actual);
-    }
-  }
-
-  /**
    * Tests field SQL schema generation for an entity with a string identifier.
    *
    * @covers ::_fieldSqlSchema()
    */
   public function testFieldSqlSchemaForEntityWithStringIdentifier() {
     $field_type_manager = $this->getMock('Drupal\Core\Field\FieldTypePluginManagerInterface');
+    $entity_manager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
 
     $container = new ContainerBuilder();
     $container->set('plugin.manager.field.field_type', $field_type_manager);
-    $container->set('entity.manager', $this->entityManager);
+    $container->set('entity.manager', $entity_manager);
     \Drupal::setContainer($container);
 
-    $this->entityType->expects($this->any())
+    $definition = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $definition->expects($this->any())
       ->method('getKey')
       ->will($this->returnValueMap(array(
         array('id', 'id'),
         array('revision', 'revision'),
       )));
-    $this->entityType->expects($this->once())
+    $definition->expects($this->once())
       ->method('hasKey')
       ->with('revision')
       ->will($this->returnValue(TRUE));
@@ -942,18 +66,19 @@ public function testFieldSqlSchemaForEntityWithStringIdentifier() {
       ->method('getDefaultInstanceSettings')
       ->will($this->returnValue(array()));
 
-    $this->fieldDefinitions['id'] = FieldDefinition::create('string')
+    $fields['id'] = FieldDefinition::create('string')
       ->setName('id');
-    $this->fieldDefinitions['revision'] = FieldDefinition::create('string')
+    $fields['revision'] = FieldDefinition::create('string')
       ->setName('revision');
 
-    $this->entityManager->expects($this->any())
+    $entity_manager->expects($this->any())
       ->method('getDefinition')
       ->with('test_entity')
-      ->will($this->returnValue($this->entityType));
-    $this->entityManager->expects($this->once())
+      ->will($this->returnValue($definition));
+    $entity_manager->expects($this->any())
       ->method('getBaseFieldDefinitions')
-      ->will($this->returnValue($this->fieldDefinitions));
+      ->with('test_entity')
+      ->will($this->returnValue($fields));
 
     // Define a field definition for a test_field field.
     $field = $this->getMock('\Drupal\field\FieldConfigInterface');
@@ -993,42 +118,43 @@ public function testFieldSqlSchemaForEntityWithStringIdentifier() {
   public function testCreate() {
     $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+    $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+    // @todo Add field definitions to test default values of fields.
+    $entity_manager->expects($this->atLeastOnce())
+      ->method('getFieldDefinitions')
+      ->will($this->returnValue(array()));
 
     $container = new ContainerBuilder();
     $container->set('language_manager', $language_manager);
-    $container->set('entity.manager', $this->entityManager);
+    $container->set('entity.manager', $entity_manager);
     $container->set('module_handler', $module_handler);
     \Drupal::setContainer($container);
 
-    $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
-      ->disableOriginalConstructor()
-      ->setMethods(array('id'))
-      ->getMockForAbstractClass();
-
-    $this->entityType->expects($this->atLeastOnce())
+    $entity = $this->getMockForAbstractClass('Drupal\Core\Entity\ContentEntityBase', array(), '', FALSE, TRUE, TRUE, array('id'));
+    $entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $entity_type->expects($this->atLeastOnce())
+      ->method('id')
+      ->will($this->returnValue('test_entity_type'));
+    $entity_type->expects($this->atLeastOnce())
       ->method('getClass')
       ->will($this->returnValue(get_class($entity)));
-    $this->entityType->expects($this->atLeastOnce())
+    $entity_type->expects($this->atLeastOnce())
       ->method('getKeys')
       ->will($this->returnValue(array('id' => 'id')));
-
-    // ContentEntityStorageBase iterates over the entity which calls this method
-    // internally in ContentEntityBase::getProperties().
-    $this->entityManager->expects($this->once())
-      ->method('getFieldDefinitions')
-      ->will($this->returnValue(array()));
-
-    $this->entityType->expects($this->atLeastOnce())
+    $entity_type->expects($this->atLeastOnce())
       ->method('isRevisionable')
       ->will($this->returnValue(FALSE));
-    $this->entityManager->expects($this->atLeastOnce())
+    $entity_manager->expects($this->atLeastOnce())
       ->method('getDefinition')
-      ->with($this->entityType->id())
-      ->will($this->returnValue($this->entityType));
+      ->with('test_entity_type')
+      ->will($this->returnValue($entity_type));
 
-    $this->setUpEntityStorage();
+    $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $entity_storage = new ContentEntityDatabaseStorage($entity_type, $connection, $entity_manager);
 
-    $entity = $this->entityStorage->create();
+    $entity = $entity_storage->create();
     $entity->expects($this->atLeastOnce())
       ->method('id')
       ->will($this->returnValue('foo'));
@@ -1038,19 +164,4 @@ public function testCreate() {
     $this->assertTrue($entity->isNew());
   }
 
-  /**
-   * Sets up the content entity database storage.
-   */
-  protected function setUpEntityStorage() {
-    $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->entityManager->expects($this->once())
-      ->method('getBaseFieldDefinitions')
-      ->will($this->returnValue($this->fieldDefinitions));
-
-    $this->entityStorage = new ContentEntityDatabaseStorage($this->entityType, $connection, $this->entityManager);
-  }
-
 }
diff --git a/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php b/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php
deleted file mode 100644
index b39fbcb..0000000
--- a/core/tests/Drupal/Tests/Core/Entity/Schema/ContentEntitySchemaHandlerTest.php
+++ /dev/null
@@ -1,770 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\Core\Entity\Schema\ContentEntitySchemaHandlerTest.
- */
-
-namespace Drupal\Tests\Core\Entity\Schema;
-
-use Drupal\Core\Entity\ContentEntityType;
-use Drupal\Core\Entity\Schema\ContentEntitySchemaHandler;
-use Drupal\Core\Entity\Sql\DefaultTableMapping;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * Tests the content entity schema handler.
- *
- * @coversDefaultClass \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler
- *
- * @group Drupal
- * @group Entity
- */
-class ContentEntitySchemaHandlerTest extends UnitTestCase {
-
-  /**
-   * The mocked entity manager used in this test.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityManager;
-
-  /**
-   * The mocked entity type used in this test.
-   *
-   * @var \Drupal\Core\Entity\ContentEntityTypeInterface
-   */
-  protected $entityType;
-
-  /**
-   * The mocked SQL storage used in this test.
-   *
-   * @var \Drupal\Core\Entity\ContentEntityDatabaseStorage|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $storage;
-
-  /**
-   * The mocked field definitions used in this test.
-   *
-   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]|\PHPUnit_Framework_MockObject_MockObject[]
-   */
-  protected $storageDefinitions;
-
-  /**
-   * The content entity schema handler used in this test.
-   *
-   * @var \Drupal\Core\Entity\Schema\ContentEntitySchemaHandler.
-   */
-  protected $schemaHandler;
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Content entity schema handler',
-      'description' => 'Tests the schema generation for content entities.',
-      'group' => 'Entity',
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setUp() {
-    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
-    $this->storage = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityDatabaseStorage')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $this->storage->expects($this->any())
-      ->method('getBaseTable')
-      ->will($this->returnValue('entity_test'));
-
-    // Add an ID field. This also acts as a test for a simple, single-column
-    // field.
-    $this->setUpStorageDefinition('id', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'int',
-        ),
-      ),
-    ));
-  }
-
-  /**
-   * Tests the schema for non-revisionable, non-translatable entities.
-   *
-   * @param bool $uuid_key
-   *   Whether or not the tested entity type should have a UUID key.
-   *
-   * @covers ::__construct()
-   * @covers ::getSchema()
-   * @covers ::getTables()
-   * @covers ::initializeBaseTable()
-   * @covers ::getEntityIndexName()
-   * @covers ::addFieldSchema()
-   * @covers ::getFieldIndexes()
-   * @covers ::getFieldUniqueKeys()
-   * @covers ::getFieldForeignKeys()
-   * @covers ::getFieldSchemaData()
-   * @covers ::addDefaultLangcodeSchema()
-   * @covers ::processBaseTable()
-   * @covers ::processIdentifierSchema()
-   *
-   * @dataProvider providerTestGetSchemaLayoutBase
-   */
-  public function testGetSchemaBase($uuid_key) {
-    $this->entityType = new ContentEntityType(array(
-      'id' => 'entity_test',
-      'entity_keys' => array(
-        'id' => 'id',
-        'uuid' => $uuid_key ? 'uuid' : NULL,
-      ),
-    ));
-
-    // Add a field with a 'length' constraint.
-    $this->setUpStorageDefinition('name', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'varchar',
-          'length' => 255,
-        ),
-      ),
-    ));
-    if ($uuid_key) {
-      $this->setUpStorageDefinition('uuid', array(
-        'columns' => array(
-          'value' => array(
-            'type' => 'varchar',
-            'length' => 128,
-          ),
-        ),
-      ));
-    }
-    // Add a multi-column field.
-    $this->setUpStorageDefinition('description', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'text',
-          'description' => 'The text value',
-        ),
-        'format' => array(
-          'type' => 'varchar',
-          'description' => 'The text description',
-        ),
-      ),
-    ));
-    // Add a field with an index.
-    $this->setUpStorageDefinition('owner', array(
-      'columns' => array(
-        'target_id' => array(
-          'description' => 'The ID of the target entity.',
-          'type' => 'int',
-        ),
-      ),
-      'indexes' => array(
-        'target_id' => array('target_id'),
-      ),
-    ));
-    // Add a field with an index, specified as column name and length.
-    $this->setUpStorageDefinition('translator', array(
-      'columns' => array(
-        'target_id' => array(
-          'description' => 'The ID of the target entity.',
-          'type' => 'int',
-        ),
-      ),
-      'indexes' => array(
-        'target_id' => array(array('target_id', 10)),
-      ),
-    ));
-    // Add a field with a multi-column index.
-    $this->setUpStorageDefinition('location', array(
-      'columns' => array(
-        'country' => array(
-          'type' => 'varchar',
-        ),
-        'state' => array(
-          'type' => 'varchar',
-        ),
-        'city' => array(
-          'type' => 'varchar',
-        )
-      ),
-      'indexes' => array(
-        'country_state_city' => array('country', 'state', array('city', 10)),
-      ),
-    ));
-    // Add a field with a foreign key.
-    $this->setUpStorageDefinition('editor', array(
-      'columns' => array(
-        'target_id' => array(
-          'type' => 'int',
-        ),
-      ),
-      'foreign keys' => array(
-        'user_id' => array(
-          'table' => 'users',
-          'columns' => array('target_id' => 'uid'),
-        ),
-      ),
-    ));
-    // Add a multi-column field with a foreign key.
-    $this->setUpStorageDefinition('editor_revision', array(
-      'columns' => array(
-        'target_id' => array(
-          'type' => 'int',
-        ),
-        'target_revision_id' => array(
-          'type' => 'int',
-        ),
-      ),
-      'foreign keys' => array(
-        'user_id' => array(
-          'table' => 'users',
-          'columns' => array('target_id' => 'uid'),
-        ),
-      ),
-    ));
-
-    $this->setUpSchemaHandler();
-
-    $table_mapping = new DefaultTableMapping($this->storageDefinitions);
-    $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
-    $table_mapping->setExtraColumns('entity_test', array('default_langcode'));
-
-    $this->storage->expects($this->once())
-      ->method('getTableMapping')
-      ->will($this->returnValue($table_mapping));
-
-    $expected = array(
-      'entity_test' => array(
-        'description' => 'The base table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'serial',
-            'not null' => TRUE,
-          ),
-          'name' => array(
-            'description' => 'The name field.',
-            'type' => 'varchar',
-            'length' => 255,
-          ),
-          'description__value' => array(
-            'description' => 'The description field.',
-            'type' => 'text',
-          ),
-          'description__format' => array(
-            'description' => 'The description field.',
-            'type' => 'varchar',
-          ),
-          'owner' => array(
-            'description' => 'The owner field.',
-            'type' => 'int',
-          ),
-          'translator' => array(
-            'description' => 'The translator field.',
-            'type' => 'int',
-          ),
-          'location__country' => array(
-            'description' => 'The location field.',
-            'type' => 'varchar',
-          ),
-          'location__state' => array(
-            'description' => 'The location field.',
-            'type' => 'varchar',
-          ),
-          'location__city' => array(
-            'description' => 'The location field.',
-            'type' => 'varchar',
-          ),
-          'editor' => array(
-            'description' => 'The editor field.',
-            'type' => 'int',
-          ),
-          'editor_revision__target_id' => array(
-            'description' => 'The editor_revision field.',
-            'type' => 'int',
-          ),
-          'editor_revision__target_revision_id' => array(
-            'description' => 'The editor_revision field.',
-            'type' => 'int',
-          ),
-          'default_langcode' => array(
-            'description' => 'Boolean indicating whether field values are in the default entity language.',
-            'type' => 'int',
-            'size' => 'tiny',
-            'not null' => TRUE,
-            'default' => 1,
-          ),
-        ),
-        'primary key' => array('id'),
-        'indexes' => array(
-          'entity_test_field__owner__target_id' => array('owner'),
-          'entity_test_field__translator__target_id' => array(
-            array('translator', 10),
-          ),
-          'entity_test_field__location__country_state_city' => array(
-            'location__country',
-            'location__state',
-            array('location__city', 10),
-          ),
-        ),
-        'foreign keys' => array(
-          'entity_test_field__editor__user_id' => array(
-            'table' => 'users',
-            'columns' => array('editor' => 'uid'),
-          ),
-          'entity_test_field__editor_revision__user_id' => array(
-            'table' => 'users',
-            'columns' => array('editor_revision__target_id' => 'uid'),
-          ),
-        ),
-      ),
-    );
-    if ($uuid_key) {
-      $expected['entity_test']['fields']['uuid'] = array(
-        'type' => 'varchar',
-        'length' => 128,
-        'description' => 'The uuid field.',
-        'not null' => TRUE,
-      );
-      $expected['entity_test']['unique keys']['entity_test__uuid'] = array('uuid');
-    }
-    $actual = $this->schemaHandler->getSchema();
-
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Provides data for testGetSchemaLayoutBase().
-   *
-   * @return array
-   *   Returns a nested array where each inner array returns a boolean,
-   *   indicating whether or not the tested entity type should include a UUID
-   *   key.
-   */
-  public function providerTestGetSchemaLayoutBase() {
-    return array(
-      array(FALSE),
-      array(TRUE),
-    );
-  }
-
-  /**
-   * Tests the schema for revisionable, non-translatable entities.
-   *
-   * @covers ::__construct()
-   * @covers ::getSchema()
-   * @covers ::getTables()
-   * @covers ::initializeBaseTable()
-   * @covers ::initializeRevisionTable()
-   * @covers ::getEntityIndexName()
-   * @covers ::processRevisionTable()
-   * @covers ::processIdentifierSchema()
-   */
-  public function testGetSchemaRevisionable() {
-    $this->entityType = new ContentEntityType(array(
-      'id' => 'entity_test',
-      'entity_keys' => array(
-        'id' => 'id',
-        'revision' => 'revision_id',
-      ),
-    ));
-
-    $this->storage->expects($this->exactly(2))
-      ->method('getRevisionTable')
-      ->will($this->returnValue('entity_test_revision'));
-
-    $this->setUpStorageDefinition('revision_id', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'int',
-        ),
-      ),
-    ));
-
-    $this->setUpSchemaHandler();
-
-    $table_mapping = new DefaultTableMapping($this->storageDefinitions);
-    $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
-    $table_mapping->setFieldNames('entity_test_revision', array_keys($this->storageDefinitions));
-
-    $this->storage->expects($this->once())
-      ->method('getTableMapping')
-      ->will($this->returnValue($table_mapping));
-
-    $expected = array(
-      'entity_test' => array(
-        'description' => 'The base table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'serial',
-            'not null' => TRUE,
-          ),
-          'revision_id' => array(
-            'description' => 'The revision_id field.',
-            'type' => 'int',
-          )
-        ),
-        'primary key' => array('id'),
-        'indexes' => array(),
-        'foreign keys' => array(
-          'entity_test__revision' => array(
-            'table' => 'entity_test_revision',
-            'columns' => array('revision_id' => 'revision_id'),
-          )
-        ),
-        'unique keys' => array(
-          'entity_test__revision_id' => array('revision_id'),
-        ),
-      ),
-      'entity_test_revision' => array(
-        'description' => 'The revision table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'int',
-            'not null' => TRUE,
-          ),
-          'revision_id' => array(
-            'description' => 'The revision_id field.',
-            'type' => 'serial',
-          ),
-        ),
-        'primary key' => array('revision_id'),
-        'indexes' => array(
-          'entity_test__id' => array('id'),
-        ),
-        'foreign keys' => array(
-          'entity_test__revisioned' => array(
-            'table' => 'entity_test',
-            'columns' => array('id' => 'id'),
-          ),
-        ),
-      ),
-    );
-
-    $actual = $this->schemaHandler->getSchema();
-
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Tests the schema for non-revisionable, translatable entities.
-   *
-   * @covers ::__construct()
-   * @covers ::getSchema()
-   * @covers ::getTables()
-   * @covers ::initializeDataTable()
-   * @covers ::getEntityIndexName()
-   * @covers ::processDataTable()
-   */
-  public function testGetSchemaTranslatable() {
-    $this->entityType = new ContentEntityType(array(
-      'id' => 'entity_test',
-      'entity_keys' => array(
-        'id' => 'id',
-      ),
-    ));
-
-    $this->storage->expects($this->once())
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-
-    $this->setUpStorageDefinition('langcode', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'varchar',
-        ),
-      ),
-    ));
-
-    $this->setUpSchemaHandler();
-
-    $table_mapping = new DefaultTableMapping($this->storageDefinitions);
-    $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
-    $table_mapping->setFieldNames('entity_test_field_data', array_keys($this->storageDefinitions));
-
-    $this->storage->expects($this->once())
-      ->method('getTableMapping')
-      ->will($this->returnValue($table_mapping));
-
-    $expected = array(
-      'entity_test' => array(
-        'description' => 'The base table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'serial',
-            'not null' => TRUE,
-          ),
-          'langcode' => array(
-            'description' => 'The langcode field.',
-            'type' => 'varchar',
-            'not null' => TRUE,
-          )
-        ),
-        'primary key' => array('id'),
-        'indexes' => array(),
-        'foreign keys' => array(),
-      ),
-      'entity_test_field_data' => array(
-        'description' => 'The data table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'int',
-            'not null' => TRUE,
-          ),
-          'langcode' => array(
-            'description' => 'The langcode field.',
-            'type' => 'varchar',
-            'not null' => TRUE,
-          ),
-        ),
-        'primary key' => array('id', 'langcode'),
-        'indexes' => array(),
-        'foreign keys' => array(
-          'entity_test' => array(
-            'table' => 'entity_test',
-            'columns' => array('id' => 'id'),
-          ),
-        ),
-      ),
-    );
-
-    $actual = $this->schemaHandler->getSchema();
-
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Tests the schema for revisionable, translatable entities.
-   *
-   * @covers ::__construct()
-   * @covers ::getSchema()
-   * @covers ::getTables()
-   * @covers ::initializeDataTable()
-   * @covers ::getEntityIndexName()
-   * @covers ::initializeRevisionDataTable()
-   * @covers ::processRevisionDataTable()
-   */
-  public function testGetSchemaRevisionableTranslatable() {
-    $this->entityType = new ContentEntityType(array(
-      'id' => 'entity_test',
-      'entity_keys' => array(
-        'id' => 'id',
-        'revision' => 'revision_id',
-      ),
-    ));
-
-    $this->storage->expects($this->exactly(3))
-      ->method('getRevisionTable')
-      ->will($this->returnValue('entity_test_revision'));
-    $this->storage->expects($this->once())
-      ->method('getDataTable')
-      ->will($this->returnValue('entity_test_field_data'));
-    $this->storage->expects($this->once())
-      ->method('getRevisionDataTable')
-      ->will($this->returnValue('entity_test_revision_field_data'));
-
-    $this->setUpStorageDefinition('revision_id', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'int',
-        ),
-      ),
-    ));
-    $this->setUpStorageDefinition('langcode', array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'varchar',
-        ),
-      ),
-    ));
-
-    $this->setUpSchemaHandler();
-
-    $table_mapping = new DefaultTableMapping($this->storageDefinitions);
-    $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
-    $table_mapping->setFieldNames('entity_test_revision', array_keys($this->storageDefinitions));
-    $table_mapping->setFieldNames('entity_test_field_data', array_keys($this->storageDefinitions));
-    $table_mapping->setFieldNames('entity_test_revision_field_data', array_keys($this->storageDefinitions));
-
-    $this->storage->expects($this->once())
-      ->method('getTableMapping')
-      ->will($this->returnValue($table_mapping));
-
-    $expected = array(
-      'entity_test' => array(
-        'description' => 'The base table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'serial',
-            'not null' => TRUE,
-          ),
-          'revision_id' => array(
-            'description' => 'The revision_id field.',
-            'type' => 'int',
-          ),
-          'langcode' => array(
-            'description' => 'The langcode field.',
-            'type' => 'varchar',
-            'not null' => TRUE,
-          )
-        ),
-        'primary key' => array('id'),
-        'indexes' => array(),
-        'unique keys' => array(
-          'entity_test__revision_id' => array('revision_id'),
-        ),
-        'foreign keys' => array(
-          'entity_test__revision' => array(
-            'table' => 'entity_test_revision',
-            'columns' => array('revision_id' => 'revision_id'),
-          ),
-        ),
-      ),
-      'entity_test_revision' => array(
-        'description' => 'The revision table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'int',
-            'not null' => TRUE,
-          ),
-          'revision_id' => array(
-            'description' => 'The revision_id field.',
-            'type' => 'serial',
-          ),
-          'langcode' => array(
-            'description' => 'The langcode field.',
-            'type' => 'varchar',
-            'not null' => TRUE,
-          ),
-        ),
-        'primary key' => array('revision_id'),
-        'indexes' => array(
-          'entity_test__id' => array('id'),
-        ),
-        'foreign keys' => array(
-          'entity_test__revisioned' => array(
-            'table' => 'entity_test',
-            'columns' => array('id' => 'id'),
-          ),
-        ),
-      ),
-      'entity_test_field_data' => array(
-        'description' => 'The data table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'int',
-            'not null' => TRUE,
-          ),
-          'revision_id' => array(
-            'description' => 'The revision_id field.',
-            'type' => 'int',
-          ),
-          'langcode' => array(
-            'description' => 'The langcode field.',
-            'type' => 'varchar',
-            'not null' => TRUE,
-          ),
-        ),
-        'primary key' => array('id', 'langcode'),
-        'indexes' => array(
-          'entity_test__revision_id' => array('revision_id'),
-        ),
-        'foreign keys' => array(
-          'entity_test' => array(
-            'table' => 'entity_test',
-            'columns' => array('id' => 'id'),
-          ),
-        ),
-      ),
-      'entity_test_revision_field_data' => array(
-        'description' => 'The revision data table for entity_test entities.',
-        'fields' => array(
-          'id' => array(
-            'description' => 'The id field.',
-            'type' => 'int',
-            'not null' => TRUE,
-          ),
-          'revision_id' => array(
-            'description' => 'The revision_id field.',
-            'type' => 'int',
-          ),
-          'langcode' => array(
-            'description' => 'The langcode field.',
-            'type' => 'varchar',
-            'not null' => TRUE,
-          ),
-        ),
-        'primary key' => array('revision_id', 'langcode'),
-        'indexes' => array(),
-        'foreign keys' => array(
-          'entity_test' => array(
-            'table' => 'entity_test',
-            'columns' => array('id' => 'id'),
-          ),
-          'entity_test__revision' => array(
-            'table' => 'entity_test_revision',
-            'columns' => array('revision_id' => 'revision_id'),
-          ),
-        ),
-      ),
-    );
-
-    $actual = $this->schemaHandler->getSchema();
-
-    $this->assertEquals($expected, $actual);
-  }
-
-  /**
-   * Sets up the schema handler.
-   *
-   * This uses the field definitions set in $this->fieldDefinitions.
-   */
-  protected function setUpSchemaHandler() {
-    $this->entityManager->expects($this->once())
-      ->method('getFieldStorageDefinitions')
-      ->with($this->entityType->id())
-      ->will($this->returnValue($this->storageDefinitions));
-    $this->schemaHandler = new ContentEntitySchemaHandler(
-      $this->entityManager,
-      $this->entityType,
-      $this->storage
-    );
-  }
-
-  /**
-   * Sets up a field definition.
-   *
-   * @param string $field_name
-   *   The field name.
-   * @param array $schema
-   *   The schema array of the field definition, as returned from
-   *   FieldDefinitionInterface::schema().
-   */
-  public function setUpStorageDefinition($field_name, array $schema) {
-    $this->storageDefinitions[$field_name] = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
-    // getDescription() is called once for each table.
-    $this->storageDefinitions[$field_name]->expects($this->any())
-      ->method('getDescription')
-      ->will($this->returnValue("The $field_name field."));
-    // getSchema() is called once for each table.
-    $this->storageDefinitions[$field_name]->expects($this->any())
-      ->method('getSchema')
-      ->will($this->returnValue($schema));
-    $this->storageDefinitions[$field_name]->expects($this->once())
-      ->method('getColumns')
-      ->will($this->returnValue($schema['columns']));
-  }
-
-}
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/DefaultTableMappingTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/DefaultTableMappingTest.php
deleted file mode 100644
index 196ff77..0000000
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/DefaultTableMappingTest.php
+++ /dev/null
@@ -1,268 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\Core\Entity\Sql\DefaultTableMappingTest.
- */
-
-namespace Drupal\Tests\Core\Entity\Sql;
-use Drupal\Core\Entity\Sql\DefaultTableMapping;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * Tests the default table mapping class.
- *
- * @coversDefaultClass \Drupal\Core\Entity\Sql\DefaultTableMapping
- *
- * @group Drupal
- * @group Entity
- */
-class DefaultTableMappingTest extends UnitTestCase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getInfo() {
-    return [
-      'name' => 'Default table mapping',
-      'description' => 'Check that the default table mapping works.',
-      'group' => 'Entity',
-    ];
-  }
-
-  /**
-   * Tests DefaultTableMapping::getTableNames().
-   *
-   * @covers ::getTableNames()
-   */
-  public function testGetTableNames() {
-    // The storage definitions are only used in getColumnNames() so we do not
-    // need to provide any here.
-    $table_mapping = new DefaultTableMapping([]);
-    $this->assertSame([], $table_mapping->getTableNames());
-
-    $table_mapping->setFieldNames('foo', []);
-    $this->assertSame(['foo'], $table_mapping->getTableNames());
-
-    $table_mapping->setFieldNames('bar', []);
-    $this->assertSame(['foo', 'bar'], $table_mapping->getTableNames());
-
-    $table_mapping->setExtraColumns('baz', []);
-    $this->assertSame(['foo', 'bar', 'baz'], $table_mapping->getTableNames());
-
-    // Test that table names are not duplicated.
-    $table_mapping->setExtraColumns('foo', []);
-    $this->assertSame(['foo', 'bar', 'baz'], $table_mapping->getTableNames());
-  }
-
-  /**
-   * Tests DefaultTableMapping::getAllColumns().
-   *
-   * @covers ::__construct()
-   * @covers ::getAllColumns()
-   * @covers ::getFieldNames()
-   * @covers ::getColumnNames()
-   * @covers ::setFieldNames()
-   * @covers ::getExtraColumns()
-   * @covers ::setExtraColumns()
-   */
-  public function testGetAllColumns() {
-    // Set up single-column and multi-column definitions.
-    $definitions['id'] = $this->setUpDefinition(['value']);
-    $definitions['name'] = $this->setUpDefinition(['value']);
-    $definitions['type'] = $this->setUpDefinition(['value']);
-    $definitions['description'] = $this->setUpDefinition(['value', 'format']);
-    $definitions['owner'] = $this->setUpDefinition([
-      'target_id',
-      'target_revision_id',
-    ]);
-
-    $table_mapping = new DefaultTableMapping($definitions);
-    $expected = [];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    // Test adding field columns.
-    $table_mapping->setFieldNames('test', ['id']);
-    $expected = ['id'];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    $table_mapping->setFieldNames('test', ['id', 'name']);
-    $expected = ['id', 'name'];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    $table_mapping->setFieldNames('test', ['id', 'name', 'type']);
-    $expected = ['id', 'name', 'type'];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    $table_mapping->setFieldNames('test', [
-      'id',
-      'name',
-      'type',
-      'description',
-    ]);
-    $expected = [
-      'id',
-      'name',
-      'type',
-      'description__value',
-      'description__format',
-    ];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    $table_mapping->setFieldNames('test', [
-      'id',
-      'name',
-      'type',
-      'description',
-      'owner',
-    ]);
-    $expected = [
-      'id',
-      'name',
-      'type',
-      'description__value',
-      'description__format',
-      'owner__target_id',
-      'owner__target_revision_id',
-    ];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    // Test adding extra columns.
-    $table_mapping->setFieldNames('test', []);
-    $table_mapping->setExtraColumns('test', ['default_langcode']);
-    $expected = ['default_langcode'];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    $table_mapping->setExtraColumns('test', [
-      'default_langcode',
-      'default_revision',
-    ]);
-    $expected = ['default_langcode', 'default_revision'];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-
-    // Test adding both field and extra columns.
-    $table_mapping->setFieldNames('test', [
-      'id',
-      'name',
-      'type',
-      'description',
-      'owner',
-    ]);
-    $table_mapping->setExtraColumns('test', [
-      'default_langcode',
-      'default_revision',
-    ]);
-    $expected = [
-      'id',
-      'name',
-      'type',
-      'description__value',
-      'description__format',
-      'owner__target_id',
-      'owner__target_revision_id',
-      'default_langcode',
-      'default_revision',
-    ];
-    $this->assertSame($expected, $table_mapping->getAllColumns('test'));
-  }
-
-  /**
-   * Tests DefaultTableMapping::getFieldNames().
-   *
-   * @covers ::getFieldNames()
-   * @covers ::setFieldNames()
-   */
-  public function testGetFieldNames() {
-    // The storage definitions are only used in getColumnNames() so we do not
-    // need to provide any here.
-    $table_mapping = new DefaultTableMapping([]);
-
-    // Test that requesting the list of field names for a table for which no
-    // fields have been added does not fail.
-    $this->assertSame([], $table_mapping->getFieldNames('foo'));
-
-    $return = $table_mapping->setFieldNames('foo', ['id', 'name', 'type']);
-    $this->assertSame($table_mapping, $return);
-    $expected = ['id', 'name', 'type'];
-    $this->assertSame($expected, $table_mapping->getFieldNames('foo'));
-    $this->assertSame([], $table_mapping->getFieldNames('bar'));
-
-    $return = $table_mapping->setFieldNames('bar', ['description', 'owner']);
-    $this->assertSame($table_mapping, $return);
-    $expected = ['description', 'owner'];
-    $this->assertSame($expected, $table_mapping->getFieldNames('bar'));
-    // Test that the previously added field names are unaffected.
-    $expected = ['id', 'name', 'type'];
-    $this->assertSame($expected, $table_mapping->getFieldNames('foo'));
-  }
-
-  /**
-   * Tests DefaultTableMapping::getColumnNames().
-   *
-   * @covers ::__construct()
-   * @covers ::getColumnNames()
-   */
-  public function testGetColumnNames() {
-    $definitions['test'] = $this->setUpDefinition([]);
-    $table_mapping = new DefaultTableMapping($definitions);
-    $expected = [];
-    $this->assertSame($expected, $table_mapping->getColumnNames('test'));
-
-    $definitions['test'] = $this->setUpDefinition(['value']);
-    $table_mapping = new DefaultTableMapping($definitions);
-    $expected = ['value' => 'test'];
-    $this->assertSame($expected, $table_mapping->getColumnNames('test'));
-
-    $definitions['test'] = $this->setUpDefinition(['value', 'format']);
-    $table_mapping = new DefaultTableMapping($definitions);
-    $expected = ['value' => 'test__value', 'format' => 'test__format'];
-    $this->assertSame($expected, $table_mapping->getColumnNames('test'));
-  }
-
-  /**
-   * Tests DefaultTableMapping::getExtraColumns().
-   *
-   * @covers ::getExtraColumns()
-   * @covers ::setExtraColumns()
-   */
-  public function testGetExtraColumns() {
-    // The storage definitions are only used in getColumnNames() so we do not
-    // need to provide any here.
-    $table_mapping = new DefaultTableMapping([]);
-
-    // Test that requesting the list of field names for a table for which no
-    // fields have been added does not fail.
-    $this->assertSame([], $table_mapping->getExtraColumns('foo'));
-
-    $return = $table_mapping->setExtraColumns('foo', ['id', 'name', 'type']);
-    $this->assertSame($table_mapping, $return);
-    $expected = ['id', 'name', 'type'];
-    $this->assertSame($expected, $table_mapping->getExtraColumns('foo'));
-    $this->assertSame([], $table_mapping->getExtraColumns('bar'));
-
-    $return = $table_mapping->setExtraColumns('bar', ['description', 'owner']);
-    $this->assertSame($table_mapping, $return);
-    $expected = ['description', 'owner'];
-    $this->assertSame($expected, $table_mapping->getExtraColumns('bar'));
-    // Test that the previously added field names are unaffected.
-    $expected = ['id', 'name', 'type'];
-    $this->assertSame($expected, $table_mapping->getExtraColumns('foo'));
-  }
-
-  /**
-   * Sets up a field storage definition for the test.
-   *
-   * @param array $column_names
-   *   An array of column names for the storage definition.
-   *
-   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected function setUpDefinition(array $column_names) {
-    $definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
-    $definition->expects($this->any())
-      ->method('getColumns')
-      ->will($this->returnValue(array_fill_keys($column_names, [])));
-    return $definition;
-  }
-
-}
diff --git a/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php b/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php
index da65a2f..5b033f9 100644
--- a/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Field/FieldDefinitionTestBase.php
@@ -32,8 +32,17 @@ public function setUp() {
     // getModuleAndPath() returns an array of the module name and directory.
     list($module_name, $module_dir) = $this->getModuleAndPath();
 
-    $namespaces = new \ArrayObject();
-    $namespaces["Drupal\\$module_name"] = $module_dir . '/src';
+    $namespaces = new \ArrayObject(
+      array(
+        "Drupal\\$module_name" => array(
+          // Suppport both PSR-0 and PSR-4 directory layouts.
+          $module_dir . '/src',
+          // @todo Remove this when PSR-0 support ends.
+          // @see https://drupal.org/node/2247287
+          $module_dir . '/lib/Drupal/' . $module_name,
+        ),
+      )
+    );
 
     $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->once())
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 86cb5cc..2953f92 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -64,8 +64,7 @@ protected function setUp() {
       ),
     );
 
-    $this->namespaces = new \ArrayObject();
-    $this->namespaces['Drupal\plugin_test'] = DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/src';
+    $this->namespaces = new \ArrayObject(array('Drupal\plugin_test' => DRUPAL_ROOT . '/core/modules/system/tests/modules/plugin_test/lib'));
   }
 
   /**
diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php
index 72c5d5b..af5746c 100644
--- a/core/tests/bootstrap.php
+++ b/core/tests/bootstrap.php
@@ -56,12 +56,21 @@ function drupal_phpunit_contrib_extension_directory_roots() {
  */
 function drupal_phpunit_register_extension_dirs(Composer\Autoload\ClassLoader $loader, $dirs) {
   foreach ($dirs as $extension => $dir) {
+    // Register PSR-0 test directories.
+    // @todo Remove this, when the transition to PSR-4 is complete.
+    $lib_path = $dir . '/lib';
+    if (is_dir($lib_path)) {
+      $loader->add('Drupal\\' . $extension, $lib_path);
+    }
+    $tests_path = $dir . '/tests';
+    if (is_dir($tests_path)) {
+      $loader->add('Drupal\\' . $extension, $tests_path);
+    }
+    // Register PSR-4 test directories.
     if (is_dir($dir . '/src')) {
-      // Register the PSR-4 directory for module-provided classes.
       $loader->addPsr4('Drupal\\' . $extension . '\\', $dir . '/src');
     }
     if (is_dir($dir . '/tests/src')) {
-      // Register the PSR-4 directory for PHPUnit test classes.
       $loader->addPsr4('Drupal\\' . $extension . '\Tests\\', $dir . '/tests/src');
     }
   }
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..47376dc 100644
--- a/core/themes/engines/twig/twig.engine
+++ b/core/themes/engines/twig/twig.engine
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Extension\Extension;
+use Drupal\Core\Template\SafeMarkup;
 
 /**
  * Implements hook_theme().
@@ -93,7 +94,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 new SafeMarkup(implode('', $output));
 }
 
 /**
diff --git a/core/vendor/twig/twig/lib/Twig/Extension/Core.php b/core/vendor/twig/twig/lib/Twig/Extension/Core.php
index 4e80c67..396a9f3 100644
--- a/core/vendor/twig/twig/lib/Twig/Extension/Core.php
+++ b/core/vendor/twig/twig/lib/Twig/Extension/Core.php
@@ -922,7 +922,12 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
 
     if (!is_string($string)) {
         if (is_object($string) && method_exists($string, '__toString')) {
-            $string = (string) $string;
+            // @todo Move to twig engine. This is necessary because of
+            // RenderWrapper.
+            $string = $string->__toString();
+            if ($autoescape && $string instanceof Twig_Markup) {
+              return $string;
+            }
         } else {
             return $string;
         }
