diff --git a/core/core.services.yml b/core/core.services.yml
index 882cf9f..bc08b5d 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -170,6 +170,9 @@ services:
     arguments: [ '%container.namespaces%' ]
     tags:
       - { name: persist }
+  theme_handler:
+    class: Drupal\Core\Theme\ThemeHandler
+    arguments: ['@config.factory', '@module_handler', '@cache.cache', '@info_parser', '@router.builder']
   entity.manager:
     class: Drupal\Core\Entity\EntityManager
     arguments: ['@container.namespaces', '@service_container', '@module_handler', '@cache.cache', '@language_manager', '@string_translation']
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 9159f71..95abf64 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -477,7 +477,15 @@ function install_begin_request(&$install_state) {
 
     // Register the info parser.
     $container->register('info_parser', 'Drupal\Core\Extension\InfoParser');
+
+    $container->register('theme_handler', 'Drupal\Core\Theme\ThemeHandler')
+      ->addArgument(new Reference('config.factory'))
+      ->addArgument(new Reference('module_handler'))
+      ->addArgument(new Reference('cache.cache'))
+      ->addArgument(new Reference('info_parser'))
+    ;
   }
+
   // Set the request in the kernel to the new created Request above
   // so it is available to the rest of the installation process.
   $container->set('request', $request);
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index b1ca7c0..a2cedee 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -695,56 +695,15 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) {
  *     declare this theme as their base theme.
  */
 function list_themes($refresh = FALSE) {
-  $list = &drupal_static(__FUNCTION__, array());
+  /** @var \Drupal\Core\Theme\ThemeHandler $theme_handler */
+  $theme_handler = \Drupal::service('theme_handler');
 
   if ($refresh) {
-    $list = array();
+    $theme_handler->reset();
     system_list_reset();
   }
 
-  if (empty($list)) {
-    $list = array();
-    // Extract from the database only when it is available.
-    // Also check that the site is not in the middle of an install or update.
-    if (!defined('MAINTENANCE_MODE')) {
-      try {
-        $themes = system_list('theme');
-      }
-      catch (Exception $e) {
-        // If the database is not available, rebuild the theme data.
-        $themes = _system_rebuild_theme_data();
-      }
-    }
-    else {
-      // Scan the installation when the database should not be read.
-      $themes = _system_rebuild_theme_data();
-    }
-
-    foreach ($themes as $theme) {
-      foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
-        foreach ($stylesheets as $stylesheet => $path) {
-          $theme->stylesheets[$media][$stylesheet] = $path;
-        }
-      }
-      foreach ($theme->info['scripts'] as $script => $path) {
-        $theme->scripts[$script] = $path;
-      }
-      if (isset($theme->info['engine'])) {
-        $theme->engine = $theme->info['engine'];
-      }
-      if (isset($theme->info['base theme'])) {
-        $theme->base_theme = $theme->info['base theme'];
-      }
-      // Status is normally retrieved from the database. Add zero values when
-      // read from the installation directory to prevent notices.
-      if (!isset($theme->status)) {
-        $theme->status = 0;
-      }
-      $list[$theme->name] = $theme;
-    }
-  }
-
-  return $list;
+  return $theme_handler->listInfo();
 }
 
 /**
@@ -766,29 +725,7 @@ function list_themes($refresh = FALSE) {
  *   will be NULL if an error occurred.
  */
 function drupal_find_base_themes($themes, $key, $used_keys = array()) {
-  $base_key = $themes[$key]->info['base theme'];
-  // Does the base theme exist?
-  if (!isset($themes[$base_key])) {
-    return array($base_key => NULL);
-  }
-
-  $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
-
-  // Is the base theme itself a child of another theme?
-  if (isset($themes[$base_key]->info['base theme'])) {
-    // Do we already know the base themes of this theme?
-    if (isset($themes[$base_key]->base_themes)) {
-      return $themes[$base_key]->base_themes + $current_base_theme;
-    }
-    // Prevent loops.
-    if (!empty($used_keys[$base_key])) {
-      return array($base_key => NULL);
-    }
-    $used_keys[$base_key] = TRUE;
-    return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
-  }
-  // If we get here, then this is our parent theme.
-  return $current_base_theme;
+  return \Drupal::service('theme_handler')->getBaseThemes($key);
 }
 
 /**
@@ -1478,36 +1415,7 @@ function theme_settings_convert_to_config(array $theme_settings, Config $config)
  *   An array of theme names.
  */
 function theme_enable($theme_list) {
-  drupal_clear_css_cache();
-  $theme_config = \Drupal::config('system.theme');
-  $disabled_themes = \Drupal::config('system.theme.disabled');
-  foreach ($theme_list as $key) {
-    // Throw an exception if the theme name is too long.
-    if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) {
-      throw new ExtensionNameLengthException(format_string('Theme name %name is over the maximum allowed length of @max characters.', array(
-        '%name' => $key,
-        '@max' => DRUPAL_EXTENSION_NAME_MAX_LENGTH,
-      )));
-    }
-
-    // The value is not used; the weight is ignored for themes currently.
-    $theme_config->set("enabled.$key", 0)->save();
-    $disabled_themes->clear($key)->save();
-
-    // Refresh the theme list as config_install_default_config() needs an
-    // updated list to work.
-    list_themes(TRUE);
-    // Install default configuration of the theme.
-    config_install_default_config('theme', $key);
-  }
-
-  \Drupal::service('router.builder')->rebuild();
-  menu_router_rebuild();
-  \Drupal::cache('cache')->deleteTags(array('local_task' => 1));
-  drupal_theme_rebuild();
-
-  // Invoke hook_themes_enabled() after the themes have been enabled.
-  \Drupal::moduleHandler()->invokeAll('themes_enabled', array($theme_list));
+  \Drupal::service('theme_handler')->enable($theme_list);
 }
 
 /**
@@ -1517,34 +1425,7 @@ function theme_enable($theme_list) {
  *   An array of theme names.
  */
 function theme_disable($theme_list) {
-  // Don't disable the default theme.
-  if ($pos = array_search(\Drupal::config('system.theme')->get('default'), $theme_list) !== FALSE) {
-    unset($theme_list[$pos]);
-    if (empty($theme_list)) {
-      return;
-    }
-  }
-
-  drupal_clear_css_cache();
-
-  $theme_config = \Drupal::config('system.theme');
-  $disabled_themes = \Drupal::config('system.theme.disabled');
-  foreach ($theme_list as $key) {
-    // The value is not used; the weight is ignored for themes currently.
-    $theme_config->clear("enabled.$key");
-    $disabled_themes->set($key, 0);
-  }
-  $theme_config->save();
-  $disabled_themes->save();
-
-  list_themes(TRUE);
-  \Drupal::service('router.builder')->rebuild();
-  menu_router_rebuild();
-  \Drupal::cache('cache')->deleteTags(array('local_task' => 1));
-  drupal_theme_rebuild();
-
-  // Invoke hook_themes_disabled after the themes have been disabled.
-  \Drupal::moduleHandler()->invokeAll('themes_disabled', array($theme_list));
+  \Drupal::service('theme_handler')->disable($theme_list);
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Theme/ThemeHandler.php b/core/lib/Drupal/Core/Theme/ThemeHandler.php
new file mode 100644
index 0000000..d8f798d
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeHandler.php
@@ -0,0 +1,377 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\ThemeHandler.
+ */
+
+namespace Drupal\Core\Theme;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Routing\RouteBuilder;
+use Drupal\Core\SystemListingInfo;
+use Drupal\Core\Extension\InfoParserInterface;
+
+/**
+ * Manages the list of available themes as well as enable/disable them.
+ */
+class ThemeHandler implements ThemeHandlerInterface {
+
+  protected $defaultFeatures = array(
+    'logo',
+    'favicon',
+    'name',
+    'slogan',
+    'node_user_picture',
+    'comment_user_picture',
+    'comment_user_verification',
+    'main_menu',
+    'secondary_menu',
+  );
+
+  protected $list = array();
+
+  public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, InfoParserInterface $info_parser, RouteBuilder $route_builder = NULL) {
+    $this->configFactory = $config_factory;
+    $this->moduleHandler = $module_handler;
+    $this->cacheBackend = $cache_backend;
+    $this->infoParser = $info_parser;
+    $this->routeBuilder = $route_builder;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function enable(array $theme_list) {
+    drupal_clear_css_cache();
+    $theme_config = $this->configFactory->get('system.theme');
+    $disabled_themes = $this->configFactory->get('system.theme.disabled');
+    foreach ($theme_list as $key) {
+      // Throw an exception if the theme name is too long.
+      if (strlen($key) > DRUPAL_EXTENSION_NAME_MAX_LENGTH) {
+        throw new ExtensionNameLengthException(format_string('Theme name %name is over the maximum allowed length of @max characters.', array(
+          '%name' => $key,
+          '@max' => DRUPAL_EXTENSION_NAME_MAX_LENGTH,
+        )));
+      }
+
+      // The value is not used; the weight is ignored for themes currently.
+      $theme_config->set("enabled.$key", 0)->save();
+      $disabled_themes->clear($key)->save();
+
+      // Refresh the theme list as config_install_default_config() needs an
+      // updated list to work.
+      $this->reset();
+      // Install default configuration of the theme.
+      $this->configInstallDefaultConfig($key);
+    }
+
+    $this->resetSystem();
+
+    // Invoke hook_themes_enabled() after the themes have been enabled.
+    $this->moduleHandler->invokeAll('themes_enabled', array($theme_list));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function disable(array $theme_list) {
+    // Don't disable the default theme.
+    if ($pos = array_search($this->configFactory->get('system.theme')->get('default'), $theme_list) !== FALSE) {
+      unset($theme_list[$pos]);
+      if (empty($theme_list)) {
+        return;
+      }
+    }
+
+    $this->clearCssCache();
+
+    $theme_config = $this->configFactory->get('system.theme');
+    $disabled_themes = $this->configFactory->get('system.theme.disabled');
+    foreach ($theme_list as $key) {
+      // The value is not used; the weight is ignored for themes currently.
+      $theme_config->clear("enabled.$key");
+      $disabled_themes->set($key, 0);
+    }
+    $theme_config->save();
+    $disabled_themes->save();
+
+    $this->reset();
+    $this->resetSystem();
+    $this->themeRegistryRebuild();
+
+    // Invoke hook_themes_disabled after the themes have been disabled.
+    $this->moduleHandler->invokeAll('themes_disabled', array($theme_list));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listInfo() {
+    if (empty($this->list)) {
+      $this->list = array();
+      // Extract from the database only when it is available.
+      // Also check that the site is not in the middle of an install or update.
+      if (!defined('MAINTENANCE_MODE')) {
+        try {
+          $themes = system_list('theme');
+        }
+        catch (\Exception $e) {
+          // If the database is not available, rebuild the theme data.
+          $themes = $this->rebuildThemeData();
+        }
+      }
+      else {
+        // Scan the installation when the database should not be read.
+        $themes = $this->rebuildThemeData();
+      }
+
+      foreach ($themes as $theme) {
+        foreach ($theme->info['stylesheets'] as $media => $stylesheets) {
+          foreach ($stylesheets as $stylesheet => $path) {
+            $theme->stylesheets[$media][$stylesheet] = $path;
+          }
+        }
+        foreach ($theme->info['scripts'] as $script => $path) {
+          $theme->scripts[$script] = $path;
+        }
+        if (isset($theme->info['engine'])) {
+          $theme->engine = $theme->info['engine'];
+        }
+        if (isset($theme->info['base theme'])) {
+          $theme->base_theme = $theme->info['base theme'];
+        }
+        // Status is normally retrieved from the database. Add zero values when
+        // read from the installation directory to prevent notices.
+        if (!isset($theme->status)) {
+          $theme->status = 0;
+        }
+        $this->list[$theme->name] = $theme;
+      }
+    }
+    return $this->list;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    $this->list = array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function rebuildThemeData() {
+    // Find themes.
+    $listing = new SystemListingInfo();
+    $themes = $listing->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes', 'name', 1);
+    // Allow modules to add further themes.
+    if ($module_themes = $this->moduleHandler->invokeAll('system_theme_info')) {
+      foreach ($module_themes as $name => $uri) {
+        // @see file_scan_directory()
+        $themes[$name] = (object) array(
+          'uri' => $uri,
+          'filename' => pathinfo($uri, PATHINFO_FILENAME),
+          'name' => $name,
+        );
+      }
+    }
+
+    // Find theme engines
+    $listing = new SystemListingInfo();
+    $engines = $listing->scan('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines', 'name', 1);
+
+    // Set defaults for theme info.
+    $defaults = array(
+      'engine' => 'twig',
+      'regions' => array(
+        'sidebar_first' => 'Left sidebar',
+        'sidebar_second' => 'Right sidebar',
+        'content' => 'Content',
+        'header' => 'Header',
+        'footer' => 'Footer',
+        'highlighted' => 'Highlighted',
+        'help' => 'Help',
+        'page_top' => 'Page top',
+        'page_bottom' => 'Page bottom',
+      ),
+      'description' => '',
+      'features' => $this->defaultFeatures,
+      'screenshot' => 'screenshot.png',
+      'php' => DRUPAL_MINIMUM_PHP,
+      'stylesheets' => array(),
+      'scripts' => array(),
+    );
+
+    $sub_themes = array();
+    // Read info files for each theme
+    foreach ($themes as $key => $theme) {
+      $themes[$key]->filename = $theme->uri;
+      $themes[$key]->info = $this->infoParser->parse($theme->uri) + $defaults;
+
+      // Skip this extension if its type is not theme.
+      if (!isset($themes[$key]->info['type']) || $themes[$key]->info['type'] != 'theme') {
+        unset($themes[$key]);
+        continue;
+      }
+
+      // Add the info file modification time, so it becomes available for
+      // contributed modules to use for ordering theme lists.
+      $themes[$key]->info['mtime'] = filemtime($theme->uri);
+
+      // Invoke hook_system_info_alter() to give installed modules a chance to
+      // modify the data in the .info.yml files if necessary.
+      $type = 'theme';
+      drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type);
+
+      if (!empty($themes[$key]->info['base theme'])) {
+        $sub_themes[] = $key;
+      }
+
+      $engine = $themes[$key]->info['engine'];
+      if (isset($engines[$engine])) {
+        $themes[$key]->owner = $engines[$engine]->uri;
+        $themes[$key]->prefix = $engines[$engine]->name;
+        $themes[$key]->template = TRUE;
+      }
+
+      // Prefix stylesheets and scripts with module path.
+      $path = dirname($theme->uri);
+      $theme->info['stylesheets'] = $this->themeInfoPrefixPath($theme->info['stylesheets'], $path);
+      $theme->info['scripts'] = $this->themeInfoPrefixPath($theme->info['scripts'], $path);
+
+      // Give the screenshot proper path information.
+      if (!empty($themes[$key]->info['screenshot'])) {
+        $themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot'];
+      }
+    }
+
+    // Now that we've established all our master themes, go back and fill in data
+    // for subthemes.
+    foreach ($sub_themes as $key) {
+      $themes[$key]->base_themes = $this->doGetBaseThemes($themes, $key);
+      // Don't proceed if there was a problem with the root base theme.
+      if (!current($themes[$key]->base_themes)) {
+        continue;
+      }
+      $base_key = key($themes[$key]->base_themes);
+      foreach (array_keys($themes[$key]->base_themes) as $base_theme) {
+        $themes[$base_theme]->sub_themes[$key] = $themes[$key]->info['name'];
+      }
+      // Copy the 'owner' and 'engine' over if the top level theme uses a theme
+      // engine.
+      if (isset($themes[$base_key]->owner)) {
+        if (isset($themes[$base_key]->info['engine'])) {
+          $themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
+          $themes[$key]->owner = $themes[$base_key]->owner;
+          $themes[$key]->prefix = $themes[$base_key]->prefix;
+        }
+        else {
+          $themes[$key]->prefix = $key;
+        }
+      }
+    }
+
+    return $themes;
+  }
+
+  /**
+   * Prefixes all values in an .info.yml file array with a given path.
+   *
+   * This helper function is mainly used to prefix all array values of an
+   * .info.yml file property with a single given path (to the module or theme);
+   * e.g., to prefix all values of the 'stylesheets' or 'scripts' properties with
+   * the file path to the defining module/theme.
+   *
+   * @param $info
+   *   A nested array of data of an .info.yml file to be processed.
+   * @param $path
+   *   A file path to prepend to each value in $info.
+   *
+   * @return
+   *   The $info array with prefixed values.
+   *
+   * @see _system_rebuild_module_data()
+   * @see _system_rebuild_theme_data()
+   */
+  protected function themeInfoPrefixPath($info, $path) {
+    foreach ($info as $key => $value) {
+      // Recurse into nested values until we reach the deepest level.
+      if (is_array($value)) {
+        $info[$key] = $this->themeInfoPrefixPath($info[$key], $path);
+      }
+      // Unset the original value's key and set the new value with prefix, using
+      // the original value as key, so original values can still be looked up.
+      else {
+        unset($info[$key]);
+        $info[$value] = $path . '/' . $value;
+      }
+    }
+    return $info;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBaseThemes($theme) {
+    $themes = $this->listInfo();
+
+    return $this->doGetBaseThemes($themes, $theme);
+  }
+
+  protected function doGetBaseThemes($themes, $theme, $used_themes = array()) {
+    $base_key = $themes[$theme]->info['base theme'];
+    // Does the base theme exist?
+    if (!isset($themes[$base_key])) {
+      return array($base_key => NULL);
+    }
+
+    $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
+
+    // Is the base theme itself a child of another theme?
+    if (isset($themes[$base_key]->info['base theme'])) {
+      // Do we already know the base themes of this theme?
+      if (isset($themes[$base_key]->base_themes)) {
+        return $themes[$base_key]->base_themes + $current_base_theme;
+      }
+      // Prevent loops.
+      if (!empty($used_themes[$base_key])) {
+        return array($base_key => NULL);
+      }
+      $used_themes[$base_key] = TRUE;
+      return $this->doGetBaseThemes($themes, $base_key, $used_themes) + $current_base_theme;
+    }
+    // If we get here, then this is our parent theme.
+    return $current_base_theme;
+  }
+
+  /**
+   * Installs the default theme config.
+   *
+   * @param string $theme
+   */
+  protected function configInstallDefaultConfig($theme) {
+    config_install_default_config('theme', $theme);
+  }
+
+  protected function resetSystem() {
+    if ($this->routeBuilder) {
+      $this->routeBuilder->rebuild();
+    }
+
+    $this->cacheBackend->deleteTags(array('local_task' => 1));
+    $this->themeRegistryRebuild();
+  }
+
+  protected function clearCssCache() {
+    drupal_clear_css_cache();
+  }
+
+  protected function themeRegistryRebuild() {
+    drupal_theme_rebuild();
+  }
+
+} 
diff --git a/core/lib/Drupal/Core/Theme/ThemeHandlerInterface.php b/core/lib/Drupal/Core/Theme/ThemeHandlerInterface.php
new file mode 100644
index 0000000..a024e68
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeHandlerInterface.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Theme\ThemeHandlerInterface.
+ */
+
+namespace Drupal\Core\Theme;
+
+/**
+ * Manages the list of available themes as well as enable/disable them.
+ */
+interface ThemeHandlerInterface {
+
+  /**
+   * Enables a given list of themes.
+   *
+   * @param array $theme_list
+   *   An array of theme names.
+   *
+   * @throws ExtensionNameLengthException
+   */
+  public function enable(array $theme_list);
+
+  /**
+   * Disables a given list of themes.
+   *
+   * @param array $theme_list
+   *   An array of theme names.
+   */
+  public function disable(array $theme_list);
+
+  /**
+   * Returns a list of all currently available themes.
+   *
+   * Retrieved from the database, if available and the site is not in maintenance
+   * mode; otherwise compiled freshly from the filesystem.
+   *
+   * @return array
+   *   An associative array of the currently available themes. The keys are the
+   *   themes' machine names and the values are objects having the following
+   *   properties:
+   *   - filename: The filepath and name of the .info.yml file.
+   *   - name: The machine name of the theme.
+   *   - status: 1 for enabled, 0 for disabled themes.
+   *   - info: The contents of the .info.yml file.
+   *   - stylesheets: A two dimensional array, using the first key for the
+   *     media attribute (e.g. 'all'), the second for the name of the file
+   *     (e.g. style.css). The value is a complete filepath (e.g.
+   *     themes/bartik/style.css). Not set if no stylesheets are defined in the
+   *     .info.yml file.
+   *   - scripts: An associative array of JavaScripts, using the filename as key
+   *     and the complete filepath as value. Not set if no scripts are defined in
+   *     the .info.yml file.
+   *   - prefix: The base theme engine prefix.
+   *   - engine: The machine name of the theme engine.
+   *   - base_theme: If this is a sub-theme, the machine name of the base theme
+   *     defined in the .info.yml file. Otherwise, the element is not set.
+   *   - base_themes: If this is a sub-theme, an associative array of the
+   *     base-theme ancestors of this theme, starting with this theme's base
+   *     theme, then the base theme's own base theme, etc. Each entry has an
+   *     array key equal to the theme's machine name, and a value equal to the
+   *     human-readable theme name; if a theme with matching machine name does
+   *     not exist in the system, the value will instead be NULL (and since the
+   *     system would not know whether that theme itself has a base theme, that
+   *     will end the array of base themes). This is not set if the theme is not
+   *     a sub-theme.
+   *   - sub_themes: An associative array of themes on the system that are
+   *     either direct sub-themes (that is, they declare this theme to be
+   *     their base theme), direct sub-themes of sub-themes, etc. The keys are
+   *     the themes' machine names, and the values are the themes' human-readable
+   *     names. This element is not set if there are no themes on the system that
+   *     declare this theme as their base theme.
+   */
+  public function listInfo();
+
+  /**
+   * Resets the internal state of the theme handler.
+   */
+  public function reset();
+
+  /**
+   * Helper function to scan and collect theme .info.yml data and their engines.
+   *
+   * @return array
+   *   An associative array of themes information.
+   */
+  public function rebuildThemeData();
+
+  /**
+   * Finds all the base themes for the specified theme.
+   *
+   * Themes can inherit templates and function implementations from earlier
+   * themes.
+   *
+   * @param string $theme
+   *
+   * @return array
+   *   Returns an array of all of the theme's ancestors; the first element's value
+   *   will be NULL if an error occurred.
+   */
+  public function getBaseThemes($theme);
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index cf057b2..b66a8a5 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2483,115 +2483,7 @@ function system_rebuild_module_data() {
  *   An associative array of themes information.
  */
 function _system_rebuild_theme_data() {
-  // Find themes
-  $themes = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes');
-  // Allow modules to add further themes.
-  if ($module_themes = \Drupal::moduleHandler()->invokeAll('system_theme_info')) {
-    foreach ($module_themes as $name => $uri) {
-      // @see file_scan_directory()
-      $themes[$name] = (object) array(
-        'uri' => $uri,
-        'filename' => pathinfo($uri, PATHINFO_FILENAME),
-        'name' => $name,
-      );
-    }
-  }
-
-  // Find theme engines
-  $engines = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.engine$/', 'themes/engines');
-
-  // Set defaults for theme info.
-  $defaults = array(
-    'engine' => 'twig',
-    'regions' => array(
-      'sidebar_first' => 'Left sidebar',
-      'sidebar_second' => 'Right sidebar',
-      'content' => 'Content',
-      'header' => 'Header',
-      'footer' => 'Footer',
-      'highlighted' => 'Highlighted',
-      'help' => 'Help',
-      'page_top' => 'Page top',
-      'page_bottom' => 'Page bottom',
-    ),
-    'description' => '',
-    'features' => _system_default_theme_features(),
-    'screenshot' => 'screenshot.png',
-    'php' => DRUPAL_MINIMUM_PHP,
-    'stylesheets' => array(),
-    'scripts' => array(),
-  );
-
-  $sub_themes = array();
-  // Read info files for each theme
-  foreach ($themes as $key => $theme) {
-    $themes[$key]->filename = $theme->uri;
-    $themes[$key]->info = \Drupal::service('info_parser')->parse($theme->uri) + $defaults;
-
-    // Skip this extension if its type is not theme.
-    if (!isset($themes[$key]->info['type']) || $themes[$key]->info['type'] != 'theme') {
-      unset($themes[$key]);
-      continue;
-    }
-
-    // Add the info file modification time, so it becomes available for
-    // contributed modules to use for ordering theme lists.
-    $themes[$key]->info['mtime'] = filemtime($theme->uri);
-
-    // Invoke hook_system_info_alter() to give installed modules a chance to
-    // modify the data in the .info.yml files if necessary.
-    $type = 'theme';
-    drupal_alter('system_info', $themes[$key]->info, $themes[$key], $type);
-
-    if (!empty($themes[$key]->info['base theme'])) {
-      $sub_themes[] = $key;
-    }
-
-    $engine = $themes[$key]->info['engine'];
-    if (isset($engines[$engine])) {
-      $themes[$key]->owner = $engines[$engine]->uri;
-      $themes[$key]->prefix = $engines[$engine]->name;
-      $themes[$key]->template = TRUE;
-    }
-
-    // Prefix stylesheets and scripts with module path.
-    $path = dirname($theme->uri);
-    $theme->info['stylesheets'] = _system_info_add_path($theme->info['stylesheets'], $path);
-    $theme->info['scripts'] = _system_info_add_path($theme->info['scripts'], $path);
-
-    // Give the screenshot proper path information.
-    if (!empty($themes[$key]->info['screenshot'])) {
-      $themes[$key]->info['screenshot'] = $path . '/' . $themes[$key]->info['screenshot'];
-    }
-  }
-
-  // Now that we've established all our master themes, go back and fill in data
-  // for subthemes.
-  foreach ($sub_themes as $key) {
-    $themes[$key]->base_themes = drupal_find_base_themes($themes, $key);
-    // Don't proceed if there was a problem with the root base theme.
-    if (!current($themes[$key]->base_themes)) {
-      continue;
-    }
-    $base_key = key($themes[$key]->base_themes);
-    foreach (array_keys($themes[$key]->base_themes) as $base_theme) {
-      $themes[$base_theme]->sub_themes[$key] = $themes[$key]->info['name'];
-    }
-    // Copy the 'owner' and 'engine' over if the top level theme uses a theme
-    // engine.
-    if (isset($themes[$base_key]->owner)) {
-      if (isset($themes[$base_key]->info['engine'])) {
-        $themes[$key]->info['engine'] = $themes[$base_key]->info['engine'];
-        $themes[$key]->owner = $themes[$base_key]->owner;
-        $themes[$key]->prefix = $themes[$base_key]->prefix;
-      }
-      else {
-        $themes[$key]->prefix = $key;
-      }
-    }
-  }
-
-  return $themes;
+  return \Drupal::service('theme_handler')->rebuildThemeData();
 }
 
 /**
@@ -2628,41 +2520,6 @@ function system_rebuild_theme_data() {
 }
 
 /**
- * Prefixes all values in an .info.yml file array with a given path.
- *
- * This helper function is mainly used to prefix all array values of an
- * .info.yml file property with a single given path (to the module or theme);
- * e.g., to prefix all values of the 'stylesheets' or 'scripts' properties with
- * the file path to the defining module/theme.
- *
- * @param $info
- *   A nested array of data of an .info.yml file to be processed.
- * @param $path
- *   A file path to prepend to each value in $info.
- *
- * @return
- *   The $info array with prefixed values.
- *
- * @see _system_rebuild_module_data()
- * @see _system_rebuild_theme_data()
- */
-function _system_info_add_path($info, $path) {
-  foreach ($info as $key => $value) {
-    // Recurse into nested values until we reach the deepest level.
-    if (is_array($value)) {
-      $info[$key] = _system_info_add_path($info[$key], $path);
-    }
-    // Unset the original value's key and set the new value with prefix, using
-    // the original value as key, so original values can still be looked up.
-    else {
-      unset($info[$key]);
-      $info[$value] = $path . '/' . $value;
-    }
-  }
-  return $info;
-}
-
-/**
  * Returns an array of default theme features.
  */
 function _system_default_theme_features() {
