diff --git a/core/core.services.yml b/core/core.services.yml
index d1223e9..e977317 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1438,11 +1438,14 @@ services:
     arguments: ['@app.root', '@theme_handler', '@cache.bootstrap', '@module_handler']
   theme.registry:
     class: Drupal\Core\Theme\Registry
-    arguments: ['@app.root', '@cache.default', '@lock', '@module_handler', '@theme_handler', '@theme.initialization']
+    arguments: ['@app.root', '@cache.default', '@lock', '@module_handler', '@theme_handler', '@theme.initialization', '@theme.component.discovery']
     tags:
       - { name: needs_destruction }
     calls:
       - [setThemeManager, ['@theme.manager']]
+  theme.component.discovery:
+    class: Drupal\Core\Theme\ThemeComponentDiscovery
+    arguments: ['@module_handler']
   authentication:
     class: Drupal\Core\Authentication\AuthenticationManager
     arguments: ['@authentication_collector']
diff --git a/core/lib/Drupal/Core/Theme/Registry.php b/core/lib/Drupal/Core/Theme/Registry.php
index 6f5cb1e..8020c9f 100644
--- a/core/lib/Drupal/Core/Theme/Registry.php
+++ b/core/lib/Drupal/Core/Theme/Registry.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Theme;
 
+use Drupal\Component\Discovery\DiscoverableInterface;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\DestructableInterface;
@@ -141,6 +142,20 @@ class Registry implements DestructableInterface {
   protected $themeManager;
 
   /**
+   * The theme initialization.
+   *
+   * @var \Drupal\Core\Theme\ThemeInitializationInterface
+   */
+  protected $themeInitialization;
+
+  /**
+   * The theme component discovery.
+   *
+   * @var \Drupal\Component\Discovery\DiscoverableInterface
+   */
+  protected $themeComponentDiscovery;
+
+  /**
    * Constructs a \Drupal\Core\Theme\Registry object.
    *
    * @param string $root
@@ -155,10 +170,12 @@ class Registry implements DestructableInterface {
    *   The theme handler.
    * @param \Drupal\Core\Theme\ThemeInitializationInterface $theme_initialization
    *   The theme initialization.
+   * @param \Drupal\Component\Discovery\DiscoverableInterface $theme_component_discovery
+   *   The theme component discovery.
    * @param string $theme_name
    *   (optional) The name of the theme for which to construct the registry.
    */
-  public function __construct($root, CacheBackendInterface $cache, LockBackendInterface $lock, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ThemeInitializationInterface $theme_initialization, $theme_name = NULL) {
+  public function __construct($root, CacheBackendInterface $cache, LockBackendInterface $lock, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, ThemeInitializationInterface $theme_initialization, DiscoverableInterface $theme_component_discovery, $theme_name = NULL) {
     $this->root = $root;
     $this->cache = $cache;
     $this->lock = $lock;
@@ -166,6 +183,7 @@ public function __construct($root, CacheBackendInterface $cache, LockBackendInte
     $this->themeName = $theme_name;
     $this->themeHandler = $theme_handler;
     $this->themeInitialization = $theme_initialization;
+    $this->themeComponentDiscovery = $theme_component_discovery;
   }
 
   /**
@@ -319,8 +337,9 @@ protected function build() {
       $cache = $cached->data;
     }
     else {
-      foreach ($this->moduleHandler->getImplementations('theme') as $module) {
-        $this->processExtension($cache, $module, 'module', $module, $this->getPath($module));
+      $theme_hooks = $this->themeComponentDiscovery->findAll();
+      foreach ($theme_hooks as $module => $content) {
+        $this->processThemeComponents($cache, $content, $module, 'module', $module, $this->getPath($module));
       }
       // Only cache this registry if all modules are loaded.
       if ($this->moduleHandler->isLoaded()) {
@@ -371,6 +390,46 @@ protected function build() {
    * Process a single implementation of hook_theme().
    *
    * @param array $cache
+   *   The theme registry that will eventually be cached.
+   * @param string $name
+   *   The name of the module, theme engine, base theme engine, theme or base
+   *   theme implementing hook_theme().
+   * @param string $type
+   *   One of 'module', 'theme_engine', 'base_theme_engine', 'theme', or
+   *   'base_theme'. Unlike regular hooks that can only be implemented by
+   *   modules, each of these can implement hook_theme(). This function is
+   *   called in aforementioned order and new entries override older ones. For
+   *   example, if a theme hook is both defined by a module and a theme, then
+   *   the definition in the theme will be used.
+   * @param string $theme
+   *   The actual name of theme, module, etc. that is being processed.
+   * @param string $path
+   *   The directory where $name is. For example, modules/system or
+   *   themes/bartik.
+   *
+   * @see ::processThemeComponent().
+   *
+   * @throws \BadFunctionCallException
+   *
+   * @todo convert all extension types to use yml.
+   */
+  protected function processExtension(array &$cache, $name, $type, $theme, $path) {
+    $result = [];
+
+    // Invoke the hook_theme() implementation, preprocess what is returned, and
+    // merge it into $cache.
+    $function = $name . '_theme';
+    if (function_exists($function)) {
+      $result = $function($cache, $type, $theme, $path);
+    }
+
+    $this->processThemeComponents($cache, $result, $name, $type, $theme, $path);
+  }
+
+  /**
+   * Process theme components provided by single extension.
+   *
+   * @param array $cache
    *   The theme registry that will eventually be cached; It is an associative
    *   array keyed by theme hooks, whose values are associative arrays
    *   describing the hook:
@@ -394,6 +453,8 @@ protected function build() {
    *     definition is kept.
    *   - See the @link themeable Theme system overview topic @endlink for
    *     detailed documentation.
+   * @param array $result
+   *   A single theme component definition.
    * @param string $name
    *   The name of the module, theme engine, base theme engine, theme or base
    *   theme implementing hook_theme().
@@ -414,12 +475,8 @@ protected function build() {
    * @see hook_theme()
    * @see \Drupal\Core\Extension\ThemeHandler::listInfo()
    * @see twig_render_template()
-   *
-   * @throws \BadFunctionCallException
    */
-  protected function processExtension(array &$cache, $name, $type, $theme, $path) {
-    $result = array();
-
+  protected function processThemeComponents(array &$cache, $result, $name, $type, $theme, $path) {
     $hook_defaults = array(
       'variables' => TRUE,
       'render element' => TRUE,
@@ -429,156 +486,168 @@ protected function processExtension(array &$cache, $name, $type, $theme, $path)
 
     $module_list = array_keys($this->moduleHandler->getModuleList());
 
-    // Invoke the hook_theme() implementation, preprocess what is returned, and
-    // merge it into $cache.
-    $function = $name . '_theme';
-    if (function_exists($function)) {
-      $result = $function($cache, $type, $theme, $path);
-      foreach ($result as $hook => $info) {
-        // When a theme or engine overrides a module's theme function
-        // $result[$hook] will only contain key/value pairs for information being
-        // overridden.  Pull the rest of the information from what was defined by
-        // an earlier hook.
-
-        // Fill in the type and path of the module, theme, or engine that
-        // implements this theme function.
-        $result[$hook]['type'] = $type;
-        $result[$hook]['theme path'] = $path;
-
-        // If a theme hook has a base hook, mark its preprocess functions always
-        // incomplete in order to inherit the base hook's preprocess functions.
-        if (!empty($result[$hook]['base hook'])) {
-          $result[$hook]['incomplete preprocess functions'] = TRUE;
-        }
-
-        if (isset($cache[$hook]['includes'])) {
-          $result[$hook]['includes'] = $cache[$hook]['includes'];
-        }
+    foreach ($result as $hook => $info) {
+      // When a theme or engine overrides a module's theme function
+      // $result[$hook] will only contain key/value pairs for information being
+      // overridden.  Pull the rest of the information from what was defined by
+      // an earlier hook.
+
+      // Fill in the type and path of the module, theme, or engine that
+      // implements this theme function.
+      $result[$hook]['type'] = $type;
+      $result[$hook]['theme path'] = $path;
+
+      // If a theme hook has a base hook, mark its preprocess functions always
+      // incomplete in order to inherit the base hook's preprocess functions.
+      if (!empty($result[$hook]['base hook'])) {
+        $result[$hook]['incomplete preprocess functions'] = TRUE;
+      }
 
-        // Load the includes, as they may contain preprocess functions.
-        if (isset($info['includes'])) {
-          foreach ($info['includes'] as $include_file) {
-            include_once $this->root . '/' . $include_file;
-          }
-        }
+      if (isset($cache[$hook]['includes'])) {
+        $result[$hook]['includes'] = $cache[$hook]['includes'];
+      }
 
-        // If the theme implementation defines a file, then also use the path
-        // that it defined. Otherwise use the default path. This allows
-        // system.module to declare theme functions on behalf of core .include
-        // files.
-        if (isset($info['file'])) {
-          $include_file = isset($info['path']) ? $info['path'] : $path;
-          $include_file .= '/' . $info['file'];
+      // Load the includes, as they may contain preprocess functions.
+      if (isset($info['includes'])) {
+        foreach ($info['includes'] as $include_file) {
           include_once $this->root . '/' . $include_file;
-          $result[$hook]['includes'][] = $include_file;
         }
+      }
 
-        // A template file is the default implementation for a theme hook, but
-        // if the theme hook specifies a function callback instead, check to
-        // ensure the function actually exists.
-        if (isset($info['function'])) {
-          if (!function_exists($info['function'])) {
-            throw new \BadFunctionCallException(sprintf(
-              'Theme hook "%s" refers to a theme function callback that does not exist: "%s"',
-              $hook,
-              $info['function']
-            ));
-          }
-        }
-        // Provide a default naming convention for 'template' based on the
-        // hook used. If the template does not exist, the theme engine used
-        // should throw an exception at runtime when attempting to include
-        // the template file.
-        elseif (!isset($info['template'])) {
-          $info['template'] = strtr($hook, '_', '-');
-          $result[$hook]['template'] = $info['template'];
-        }
+      // If the theme implementation defines a file, then also use the path
+      // that it defined. Otherwise use the default path. This allows
+      // system.module to declare theme functions on behalf of core .include
+      // files.
+      if (isset($info['file'])) {
+        $include_file = isset($info['path']) ? $info['path'] : $path;
+        $include_file .= '/' . $info['file'];
+        include_once $this->root . '/' . $include_file;
+        $result[$hook]['includes'][] = $include_file;
+      }
 
-        // Prepend the current theming path when none is set. This is required
-        // for the default theme engine to know where the template lives.
-        if (isset($result[$hook]['template']) && !isset($info['path'])) {
-          $result[$hook]['path'] = $path . '/templates';
+      // A template file is the default implementation for a theme hook, but
+      // if the theme hook specifies a function callback instead, check to
+      // ensure the function actually exists.
+      if (isset($info['function'])) {
+        if (!function_exists($info['function'])) {
+          throw new \BadFunctionCallException(sprintf(
+            'Theme hook "%s" refers to a theme function callback that does not exist: "%s"',
+            $hook,
+            $info['function']
+          ));
         }
+      }
+      // Provide a default naming convention for 'template' based on the
+      // hook used. If the template does not exist, the theme engine used
+      // should throw an exception at runtime when attempting to include
+      // the template file.
+      elseif (!isset($info['template'])) {
+        $info['template'] = strtr($hook, '_', '-');
+        $result[$hook]['template'] = $info['template'];
+      }
 
-        // If the default keys are not set, use the default values registered
-        // by the module.
-        if (isset($cache[$hook])) {
-          $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
-        }
+      // Prepend the current theming path when none is set. This is required
+      // for the default theme engine to know where the template lives.
+      if (isset($result[$hook]['template']) && !isset($info['path'])) {
+        $result[$hook]['path'] = $path . '/templates';
+      }
 
-        // Preprocess variables for all theming hooks, whether the hook is
-        // implemented as a template or as a function. Ensure they are arrays.
-        if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) {
-          $info['preprocess functions'] = array();
-          $prefixes = array();
-          if ($type == 'module') {
-            // Default variable preprocessor prefix.
-            $prefixes[] = 'template';
-            // Add all modules so they can intervene with their own variable
-            // preprocessors. This allows them to provide variable preprocessors
-            // even if they are not the owner of the current hook.
-            $prefixes = array_merge($prefixes, $module_list);
-          }
-          elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
-            // Theme engines get an extra set that come before the normally
-            // named variable preprocessors.
-            $prefixes[] = $name . '_engine';
-            // The theme engine registers on behalf of the theme using the
-            // theme's name.
-            $prefixes[] = $theme;
-          }
-          else {
-            // This applies when the theme manually registers their own variable
-            // preprocessors.
-            $prefixes[] = $name;
-          }
-          foreach ($prefixes as $prefix) {
-            // Only use non-hook-specific variable preprocessors for theming
-            // hooks implemented as templates. See the @defgroup themeable
-            // topic.
-            if (isset($info['template']) && function_exists($prefix . '_preprocess')) {
-              $info['preprocess functions'][] = $prefix . '_preprocess';
-            }
-            if (function_exists($prefix . '_preprocess_' . $hook)) {
-              $info['preprocess functions'][] = $prefix . '_preprocess_' . $hook;
-            }
-          }
+      // If the default keys are not set, use the default values registered
+      // by the module.
+      if (isset($cache[$hook])) {
+        $result[$hook] += array_intersect_key($cache[$hook], $hook_defaults);
+      }
+
+      // Preprocess variables for all theming hooks, whether the hook is
+      // implemented as a template or as a function. Ensure they are arrays.
+      if (!isset($info['preprocess functions']) || !is_array($info['preprocess functions'])) {
+        $info['preprocess functions'] = array();
+        $prefixes = array();
+        if ($type == 'module') {
+          // Default variable preprocessor prefix.
+          $prefixes[] = 'template';
+          // Add all modules so they can intervene with their own variable
+          // preprocessors. This allows them to provide variable preprocessors
+          // even if they are not the owner of the current hook.
+          $prefixes = array_merge($prefixes, $module_list);
         }
-        // Check for the override flag and prevent the cached variable
-        // preprocessors from being used. This allows themes or theme engines
-        // to remove variable preprocessors set earlier in the registry build.
-        if (!empty($info['override preprocess functions'])) {
-          // Flag not needed inside the registry.
-          unset($result[$hook]['override preprocess functions']);
+        elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
+          // Theme engines get an extra set that come before the normally
+          // named variable preprocessors.
+          $prefixes[] = $name . '_engine';
+          // The theme engine registers on behalf of the theme using the
+          // theme's name.
+          $prefixes[] = $theme;
         }
-        elseif (isset($cache[$hook]['preprocess functions']) && is_array($cache[$hook]['preprocess functions'])) {
-          $info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']);
+        else {
+          // This applies when the theme manually registers their own variable
+          // preprocessors.
+          $prefixes[] = $name;
+        }
+        foreach ($prefixes as $prefix) {
+          // Only use non-hook-specific variable preprocessors for theming
+          // hooks implemented as templates. See the @defgroup themeable
+          // topic.
+          if (isset($info['template']) && function_exists($prefix . '_preprocess')) {
+            $info['preprocess functions'][] = $prefix . '_preprocess';
+          }
+          if (function_exists($prefix . '_preprocess_' . $hook)) {
+            $info['preprocess functions'][] = $prefix . '_preprocess_' . $hook;
+          }
         }
-        $result[$hook]['preprocess functions'] = $info['preprocess functions'];
       }
-
-      // Merge the newly created theme hooks into the existing cache.
-      $cache = $result + $cache;
+      // Check for the override flag and prevent the cached variable
+      // preprocessors from being used. This allows themes or theme engines
+      // to remove variable preprocessors set earlier in the registry build.
+      if (!empty($info['override preprocess functions'])) {
+        // Flag not needed inside the registry.
+        unset($result[$hook]['override preprocess functions']);
+      }
+      elseif (isset($cache[$hook]['preprocess functions']) && is_array($cache[$hook]['preprocess functions'])) {
+        $info['preprocess functions'] = array_merge($cache[$hook]['preprocess functions'], $info['preprocess functions']);
+      }
+      $result[$hook]['preprocess functions'] = $info['preprocess functions'];
     }
 
+    // Merge the newly created theme hooks into the existing cache.
+    $cache = $result + $cache;
+
     // Let themes have variable preprocessors even if they didn't register a
     // template.
     if ($type == 'theme' || $type == 'base_theme') {
-      foreach ($cache as $hook => $info) {
-        // Check only if not registered by the theme or engine.
-        if (empty($result[$hook])) {
-          if (!isset($info['preprocess functions'])) {
-            $cache[$hook]['preprocess functions'] = array();
-          }
-          // Only use non-hook-specific variable preprocessors for theme hooks
-          // implemented as templates. See the @defgroup themeable topic.
-          if (isset($info['template']) && function_exists($name . '_preprocess')) {
-            $cache[$hook]['preprocess functions'][] = $name . '_preprocess';
-          }
-          if (function_exists($name . '_preprocess_' . $hook)) {
-            $cache[$hook]['preprocess functions'][] = $name . '_preprocess_' . $hook;
-            $cache[$hook]['theme path'] = $path;
-          }
+      $this->processThemeExtension($cache, array_keys($result), $name, $path);
+    }
+  }
+
+  /**
+   * Processes the theme extension.
+   *
+   * @param array $cache
+   *   The theme registry that will eventually be cached.
+   * @param array $result
+   *   A single theme component definition.
+   * @param string $name
+   *   The name of the module, theme engine, base theme engine, theme or base
+   *   theme implementing hook_theme().
+   * @param string $path
+   *   The directory where $name is. For example, modules/system or
+   *   themes/bartik.
+   */
+  protected function processThemeExtension(&$cache, $result, $name, $path) {
+    foreach ($cache as $hook => $info) {
+      // Check only if not registered by the theme or engine.
+      if (empty($result[$hook])) {
+        if (!isset($info['preprocess functions'])) {
+          $cache[$hook]['preprocess functions'] = array();
+        }
+        // Only use non-hook-specific variable preprocessors for theme hooks
+        // implemented as templates. See the @defgroup themeable topic.
+        if (isset($info['template']) && function_exists($name . '_preprocess')) {
+          $cache[$hook]['preprocess functions'][] = $name . '_preprocess';
+        }
+        if (function_exists($name . '_preprocess_' . $hook)) {
+          $cache[$hook]['preprocess functions'][] = $name . '_preprocess_' . $hook;
+          $cache[$hook]['theme path'] = $path;
         }
       }
     }
diff --git a/core/lib/Drupal/Core/Theme/ThemeComponentDiscovery.php b/core/lib/Drupal/Core/Theme/ThemeComponentDiscovery.php
new file mode 100644
index 0000000..7c4fc73
--- /dev/null
+++ b/core/lib/Drupal/Core/Theme/ThemeComponentDiscovery.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\Core\Theme;
+
+use Drupal\Component\Discovery\DiscoverableInterface;
+use Drupal\Core\Discovery\YamlDiscovery;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Discovers available theme components in Drupal.
+ */
+class ThemeComponentDiscovery implements DiscoverableInterface {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The YAML discovery.
+   *
+   * @var \Drupal\Component\Discovery\DiscoverableInterface
+   */
+  protected $yamlDiscovery;
+
+  /**
+   * Constructs a new ThemeComponentDiscovery object.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler) {
+    $this->yamlDiscovery = new YamlDiscovery('theme', $module_handler->getModuleDirectories());
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function findAll() {
+    $components = [];
+    // Gather all components defined in YAML.
+    foreach ($this->yamlDiscovery->findAll() as $provider => $result) {
+      if (isset($result['components'])) {
+        $components[$provider] = $result['components'];
+      }
+    }
+
+    // Gather all components defined in hook_theme(). If a module provides a
+    // YAML file, its hook_theme() will not be invoked.
+    foreach ($this->moduleHandler->getModuleList() as $module_name => $module) {
+      $path = $module->getPath();
+      $function = $module_name . '_theme';
+      if (empty($components[$module_name]) && function_exists($function)) {
+        $components[$module_name] = $function($components, 'module', $module_name, $path);
+      }
+    }
+
+    return $components;
+  }
+
+}
diff --git a/core/modules/help/help.module b/core/modules/help/help.module
index c41cd98..15760df 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -44,22 +44,6 @@ function help_help($route_name, RouteMatchInterface $route_match) {
 }
 
 /**
- * Implements hook_theme().
- */
-function help_theme($existing, $type, $theme, $path) {
-  return [
-    'help_section' => [
-      'variables' => [
-        'title' => NULL,
-        'description' => NULL,
-        'links' => NULL,
-        'empty' => NULL,
-      ],
-    ],
-  ];
-}
-
-/**
  * Implements hook_preprocess_HOOK() for block templates.
  */
 function help_preprocess_block(&$variables) {
diff --git a/core/modules/help/help.theme.yml b/core/modules/help/help.theme.yml
new file mode 100644
index 0000000..39ae466
--- /dev/null
+++ b/core/modules/help/help.theme.yml
@@ -0,0 +1,7 @@
+components:
+  help_section:
+    variables:
+      title: null
+      description: null
+      links: null
+      empty: null
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/RegistryTest.php b/core/tests/Drupal/KernelTests/Core/Theme/RegistryTest.php
index 257c61e..3aca9af 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/RegistryTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/RegistryTest.php
@@ -67,11 +67,12 @@ public function testMultipleSubThemes() {
     $theme_handler = \Drupal::service('theme_handler');
     $theme_handler->install(['test_basetheme', 'test_subtheme', 'test_subsubtheme']);
 
-    $registry_subsub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_subsubtheme');
+    $registry_subsub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), $this->container->get('theme.component.discovery'), 'test_subsubtheme');
     $registry_subsub_theme->setThemeManager(\Drupal::theme());
-    $registry_sub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_subtheme');
+    $registry_sub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), $this->container->get('theme.component.discovery'), 'test_subtheme');
     $registry_sub_theme->setThemeManager(\Drupal::theme());
-    $registry_base_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_basetheme');
+    $registry_base_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), $this->container->get('theme.component.discovery'), 'test_basetheme');
+
     $registry_base_theme->setThemeManager(\Drupal::theme());
 
     $preprocess_functions = $registry_subsub_theme->get()['theme_test_template_test']['preprocess functions'];
@@ -109,7 +110,7 @@ public function testSuggestionPreprocessFunctions() {
     $theme_handler = \Drupal::service('theme_handler');
     $theme_handler->install(['test_theme']);
 
-    $registry_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
+    $registry_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), $this->container->get('theme.component.discovery'), 'test_theme');
     $registry_theme->setThemeManager(\Drupal::theme());
 
     $suggestions = ['__kitten', '__flamingo'];
@@ -152,7 +153,7 @@ public function testThemeRegistryAlterByTheme() {
     $theme_handler->install(['test_theme']);
     $this->config('system.theme')->set('default', 'test_theme')->save();
 
-    $registry = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
+    $registry = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), $this->container->get('theme.component.discovery'), 'test_theme');
     $registry->setThemeManager(\Drupal::theme());
     $this->assertEqual('value', $registry->get()['theme_test_template_test']['variables']['additional']);
   }
diff --git a/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php b/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php
index 40d31e6..de73f65 100644
--- a/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Theme/StableTemplateOverrideTest.php
@@ -83,7 +83,7 @@ protected function installAllModules() {
    * Ensures that Stable overrides all relevant core templates.
    */
   public function testStableTemplateOverrides() {
-    $registry = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $this->themeHandler, \Drupal::service('theme.initialization'), 'stable');
+    $registry = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $this->themeHandler, \Drupal::service('theme.initialization'), $this->container->get('theme.component.discovery'), 'stable');
     $registry->setThemeManager(\Drupal::theme());
 
     $registry_full = $registry->get();
diff --git a/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php
index 1243118..1706f1f 100644
--- a/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php
+++ b/core/tests/Drupal/Tests/Core/Theme/RegistryTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Theme;
 
+use Drupal\Component\Discovery\DiscoverableInterface;
 use Drupal\Core\Theme\ActiveTheme;
 use Drupal\Core\Theme\Registry;
 use Drupal\Tests\UnitTestCase;
@@ -60,6 +61,13 @@ class RegistryTest extends UnitTestCase {
   protected $themeInitialization;
 
   /**
+   * The mocked theme component discovery.
+   *
+   * @var \Drupal\Component\Discovery\DiscoverableInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $themeComponentDiscovery;
+
+  /**
    * The theme manager.
    *
    * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -77,6 +85,7 @@ protected function setUp() {
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
     $this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
     $this->themeInitialization = $this->getMock('Drupal\Core\Theme\ThemeInitializationInterface');
+    $this->themeComponentDiscovery = $this->prophesize(DiscoverableInterface::class);
     $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface');
 
     $this->setupTheme();
@@ -119,10 +128,9 @@ public function testGetRegistryForModule() {
     // Include the module and theme files so that hook_theme can be called.
     include_once $this->root . '/core/modules/system/tests/modules/theme_test/theme_test.module';
     include_once $this->root . '/core/modules/system/tests/themes/test_stable/test_stable.theme';
-    $this->moduleHandler->expects($this->exactly(2))
-      ->method('getImplementations')
-      ->with('theme')
-      ->will($this->returnValue(array('theme_test')));
+    $this->themeComponentDiscovery->findAll()->willReturn([
+      'theme_test' => theme_test_theme([], 'module', 'theme_test', 'core/modules/system/tests/modules/theme_test'),
+    ])->shouldBeCalledTimes(2);
     $this->moduleHandler->expects($this->atLeastOnce())
       ->method('getModuleList')
       ->willReturn([]);
@@ -160,7 +168,7 @@ public function testGetRegistryForModule() {
   }
 
   protected function setupTheme() {
-    $this->registry = new TestRegistry($this->root, $this->cache, $this->lock, $this->moduleHandler, $this->themeHandler, $this->themeInitialization);
+    $this->registry = new TestRegistry($this->root, $this->cache, $this->lock, $this->moduleHandler, $this->themeHandler, $this->themeInitialization, $this->themeComponentDiscovery->reveal());
     $this->registry->setThemeManager($this->themeManager);
   }
 
