diff --git a/core/core.services.yml b/core/core.services.yml
index a6663b4..dd3e8ed 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1054,15 +1054,15 @@ services:
     class: Drupal\Core\Asset\AssetDumper
   library.discovery:
     class: Drupal\Core\Asset\LibraryDiscovery
-    arguments: ['@library.discovery.collector', '@module_handler']
+    arguments: ['@library.discovery.collector']
   library.discovery.collector:
     class: Drupal\Core\Asset\LibraryDiscoveryCollector
-    arguments: ['@cache.discovery', '@lock', '@library.discovery.parser']
+    arguments: ['@cache.discovery', '@lock', '@library.discovery.parser', '@module_handler']
     tags:
       - { name: needs_destruction }
   library.discovery.parser:
     class: Drupal\Core\Asset\LibraryDiscoveryParser
-    arguments: ['@app.root', '@module_handler']
+    arguments: ['@app.root', '@module_handler', '@theme_handler']
   info_parser:
     class: Drupal\Core\Extension\InfoParser
   twig:
diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscovery.php b/core/lib/Drupal/Core/Asset/LibraryDiscovery.php
index 9c28e33..5b5c811 100644
--- a/core/lib/Drupal/Core/Asset/LibraryDiscovery.php
+++ b/core/lib/Drupal/Core/Asset/LibraryDiscovery.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\Asset;
 
-use Drupal\Core\Cache\CacheCollectorInterface;
+use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 
 /**
@@ -16,18 +16,11 @@
 class LibraryDiscovery implements LibraryDiscoveryInterface {
 
   /**
-   * The library discovery cache collector.
+   * The cache key.
    *
-   * @var \Drupal\Core\Cache\CacheCollectorInterface
+   * @var string
    */
-  protected $collector;
-
-  /**
-   * The module handler.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
-   */
-  protected $moduleHandler;
+  protected $cacheKey = 'library_info';
 
   /**
    * The final library definitions, statically cached.
@@ -37,18 +30,42 @@ class LibraryDiscovery implements LibraryDiscoveryInterface {
    *
    * @var array
    */
-  protected $libraryDefinitions = [];
+  protected $libraryDefinitions = NULL;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * The library discovery parser.
+   *
+   * @var \Drupal\Core\Asset\LibraryDiscoveryParser
+   */
+  protected $discoveryParser;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
 
   /**
    * Constructs a new LibraryDiscovery instance.
    *
-   * @param \Drupal\Core\Cache\CacheCollectorInterface $library_discovery_collector
-   *   The library discovery cache collector.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   The cache backend.
+   * @param \Drupal\Core\Asset\LibraryDiscoveryParser $discovery_parser
+   *   The library discovery.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
    */
-  public function __construct(CacheCollectorInterface $library_discovery_collector, ModuleHandlerInterface $module_handler) {
-    $this->collector = $library_discovery_collector;
+  public function __construct(CacheBackendInterface $cache, LibraryDiscoveryParser $discovery_parser, ModuleHandlerInterface $module_handler) {
+    $this->cache = $cache;
+    $this->discoveryParser = $discovery_parser;
     $this->moduleHandler = $module_handler;
   }
 
@@ -56,27 +73,51 @@ public function __construct(CacheCollectorInterface $library_discovery_collector
    * {@inheritdoc}
    */
   public function getLibrariesByExtension($extension) {
-    if (!isset($this->libraryDefinitions[$extension])) {
-      $libraries = $this->collector->get($extension);
-      $this->libraryDefinitions[$extension] = [];
-      foreach ($libraries as $name => $definition) {
-        // Allow modules and themes to dynamically attach request and context
-        // specific data for this library; e.g., localization.
-        $library_name = "$extension/$name";
-        $this->moduleHandler->alter('library', $definition, $library_name);
-        $this->libraryDefinitions[$extension][$name] = $definition;
+    $this->loadLibraryDefinitions();
+
+    $libraries = [];
+    foreach ($this->libraryDefinitions as $library_name => $library) {
+      list($provider, $name) = explode('/', $library_name);
+      if ($provider == $extension) {
+        $libraries[$library_name] = $library;
       }
     }
 
-    return $this->libraryDefinitions[$extension];
+    return $libraries;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getLibraryByName($extension, $name) {
-    $extension = $this->getLibrariesByExtension($extension);
-    return isset($extension[$name]) ? $extension[$name] : FALSE;
+    $library_name = $extension . '/' . $name;
+    // @todo store that a library does not exist.
+    $this->loadLibraryDefinitions();
+
+    if (empty($this->libraryDefinitions[$library_name])) {
+      $this->libraryDefinitions[$library_name] = FALSE;
+    }
+    return $this->libraryDefinitions[$library_name];
+  }
+
+  /**
+   * Loads all library definitions, either from cache or rebuild it.
+   */
+  protected function loadLibraryDefinitions() {
+    if (isset($this->libraryDefinitions)) {
+      return;
+    }
+    if ($cache = $this->cache->get($this->cacheKey)) {
+      $libraries = $cache->data;
+    }
+    else {
+      $libraries = $this->discoveryParser->build();
+      $this->cache->set($this->cacheKey, $libraries);
+    }
+
+    // @todo Should themes be able to take part in this process as well?
+    $this->moduleHandler->alter('library', $libraries);
+    $this->libraryDefinitions = $libraries;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php
deleted file mode 100644
index dfac4fa..0000000
--- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Asset\LibraryDiscoveryCollector.
- */
-
-namespace Drupal\Core\Asset;
-
-use Drupal\Core\Cache\CacheCollector;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\Lock\LockBackendInterface;
-
-/**
- * A CacheCollector implementation for building library extension info.
- */
-class LibraryDiscoveryCollector extends CacheCollector {
-
-  /**
-   * The cache key.
-   *
-   * @var string
-   */
-  protected $cacheKey = 'library_info';
-
-  /**
-   * The cache backend.
-   *
-   * @var \Drupal\Core\Cache\CacheBackendInterface
-   */
-  protected $cache;
-
-  /**
-   * The lock backend.
-   *
-   * @var \Drupal\Core\Lock\LockBackendInterface
-   */
-  protected $lock;
-
-  /**
-   * The library discovery parser.
-   *
-   * @var \Drupal\Core\Asset\LibraryDiscoveryParser
-   */
-  protected $discoveryParser;
-
-  /**
-   * Constructs a CacheCollector object.
-   *
-   * @param string $cid
-   *   The cid for the array being cached.
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
-   *   The cache backend.
-   * @param \Drupal\Core\Lock\LockBackendInterface $lock
-   *   The lock backend.
-   * @param \Drupal\Core\Asset\LibraryDiscoveryParser $discovery_parser
-   *   The library discovery parser.
-   */
-  public function __construct(CacheBackendInterface $cache, LockBackendInterface $lock, LibraryDiscoveryParser $discovery_parser) {
-    parent::__construct($this->cacheKey, $cache, $lock, array($this->cacheKey));
-
-    $this->discoveryParser = $discovery_parser;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function resolveCacheMiss($key) {
-    $this->storage[$key] = $this->discoveryParser->buildByExtension($key);
-    $this->persist($key);
-
-    return $this->storage[$key];
-  }
-
-}
diff --git a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
index a28ca6e..4741e10 100644
--- a/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
+++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php
@@ -10,9 +10,11 @@
 use Drupal\Core\Asset\Exception\IncompleteLibraryDefinitionException;
 use Drupal\Core\Asset\Exception\InvalidLibraryFileException;
 use Drupal\Core\Asset\Exception\LibraryDefinitionMissingLicenseException;
+use Drupal\Core\Extension\Extension;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
 use Drupal\Component\Serialization\Yaml;
+use Drupal\Core\Extension\ThemeHandlerInterface;
 
 /**
  * Parses library files to get extension data.
@@ -34,6 +36,13 @@ class LibraryDiscoveryParser {
   protected $root;
 
   /**
+   * The theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface
+   */
+  protected $themeHandler;
+
+  /**
    * Constructs a new LibraryDiscoveryParser instance.
    *
    * @param string $root
@@ -41,9 +50,50 @@ class LibraryDiscoveryParser {
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
    */
-  public function __construct($root, ModuleHandlerInterface $module_handler) {
+  public function __construct($root, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
     $this->root = $root;
     $this->moduleHandler = $module_handler;
+    $this->themeHandler = $theme_handler;
+  }
+
+  /**
+   * Parses and builds up all the libraries information of all extensions.
+   *
+   * @throws \Drupal\Core\Asset\Exception\IncompleteLibraryDefinitionException
+   *   Thrown when a library has no js/css/setting.
+   * @throws \UnexpectedValueException
+   *   Thrown when a js file defines a positive weight.
+   *
+   * @return array
+   *   All library definitions.
+   */
+  public function build() {
+    $libraries = [];
+
+    $core_extension = new Extension($this->root, 'core', 'core');
+    $libraries_core = (array) $this->buildByExtension($core_extension);
+    foreach ($libraries_core as $name => $library) {
+      $libraries[$core_extension->getName() . '/' . $name] = $library;
+    }
+
+    foreach ((array) $this->moduleHandler->getModuleList() as $extension) {
+      $libraries_module = $this->buildByExtension($extension);
+      foreach ($libraries_module as $name => $library) {
+        $libraries[$extension->getName() . '/' . $name] = $library;
+      }
+    }
+
+    foreach ((array) $this->themeHandler->listInfo() as $extension) {
+      $libraries_theme = $this->buildByExtension($extension);
+      foreach ($libraries_theme as $name => $library) {
+        $libraries[$extension->getName() . '/' . $name] = $library;
+      }
+    }
+
+    // Allow modules to alter the module's registered libraries.
+    $this->moduleHandler->alter('library_info', $libraries);
+
+    return $libraries;
   }
 
   /**
@@ -60,27 +110,17 @@ public function __construct($root, ModuleHandlerInterface $module_handler) {
    * @throws \UnexpectedValueException
    *   Thrown when a js file defines a positive weight.
    */
-  public function buildByExtension($extension) {
+  protected function buildByExtension(Extension $extension) {
     $libraries = array();
 
-    if ($extension === 'core') {
-      $path = 'core';
-      $extension_type = 'core';
-    }
-    else {
-      if ($this->moduleHandler->moduleExists($extension)) {
-        $extension_type = 'module';
-      }
-      else {
-        $extension_type = 'theme';
-      }
-      $path = $this->drupalGetPath($extension_type, $extension);
-    }
+    $path = $extension->getPath();
+    $extension_type = $extension->getType();
+    $extension_name = $extension->getName();
 
-    $library_file = $path . '/' . $extension . '.libraries.yml';
+    $library_file = $path . '/' . $extension_name . '.libraries.yml';
 
     if ($library_file && file_exists($this->root . '/' . $library_file)) {
-      $libraries = $this->parseLibraryInfo($extension, $library_file);
+      $libraries = $this->parseLibraryInfo($extension_name, $library_file);
     }
 
     foreach ($libraries as $id => &$library) {
@@ -139,7 +179,7 @@ public function buildByExtension($extension) {
             $options = array();
           }
           if ($type == 'js' && isset($options['weight']) && $options['weight'] > 0) {
-            throw new \UnexpectedValueException("The $extension/$id library defines a positive weight for '$source'. Only negative weights are allowed (but should be avoided). Instead of a positive weight, specify accurate dependencies for this library.");
+            throw new \UnexpectedValueException("The {$extension->getName()}/$id library defines a positive weight for '$source'. Only negative weights are allowed (but should be avoided). Instead of a positive weight, specify accurate dependencies for this library.");
           }
           // Unconditionally apply default groups for the defined asset files.
           // The library system is a dependency management system. Each library
@@ -240,20 +280,11 @@ protected function parseLibraryInfo($extension, $library_file) {
       // Rethrow a more helpful exception to provide context.
       throw new InvalidLibraryFileException(sprintf('Invalid library definition in %s: %s', $library_file, $e->getMessage()), 0, $e);
     }
-    // Allow modules to alter the module's registered libraries.
-    $this->moduleHandler->alter('library_info', $libraries, $extension);
 
     return $libraries;
   }
 
   /**
-   * Wraps drupal_get_path().
-   */
-  protected function drupalGetPath($type, $name) {
-    return drupal_get_path($type, $name);
-  }
-
-  /**
    * Wraps file_valid_uri().
    */
   protected function fileValidUri($source) {
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 5b00869..98eda7c 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -69,13 +69,18 @@ function color_form_system_theme_settings_alter(&$form, FormStateInterface $form
  *
  * Replaces style sheets declared in libraries with color-altered style sheets.
  */
-function color_library_alter(&$library, $name) {
+function color_library_alter(array &$libraries) {
   $active_theme = \Drupal::theme()->getActiveTheme();
   $theme_key = $active_theme->getName();
   $theme_libraries = $active_theme->getLibraries();
-  if (in_array($name, $theme_libraries)) {
+
+  $config = \Drupal::config('color.theme.' . $theme_key);
+
+  $intersect_libraries = array_intersect($theme_libraries, array_keys($libraries));
+  foreach ($intersect_libraries as $library_name) {
+    $library = &$libraries[$library_name];
     // Override stylesheets.
-    $color_paths = \Drupal::config('color.theme.' . $theme_key)->get('stylesheets');
+    $color_paths = $config->get('stylesheets');
     if (!empty($color_paths)) {
       foreach ($library['css'] as &$css) {
         // Loop over the path array with recolored CSS files to find matching
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index c5add1d..6a8e81e 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -528,8 +528,9 @@ function locale_js_translate(array $files = array()) {
  *
  * Provides the language support for the jQuery UI Date Picker.
  */
-function locale_library_alter(array &$library, $name) {
-  if ($name == 'core/jquery.ui.datepicker') {
+function locale_library_alter(array &$libraries) {
+  if (isset($libraries['core/jquery.ui.datepicker'])) {
+    $library =& $libraries['core/jquery.ui.datepicker'];
     // locale.datepicker.js should be added in the JS_LIBRARY group, so that
     // the behavior executes early. JS_LIBRARY is the default.
     $library['dependencies'][] = 'locale/drupal.locale.datepicker';
diff --git a/core/modules/quickedit/quickedit.module b/core/modules/quickedit/quickedit.module
index 7ed6f92..abfa248 100644
--- a/core/modules/quickedit/quickedit.module
+++ b/core/modules/quickedit/quickedit.module
@@ -85,21 +85,21 @@ function quickedit_page_attachments(array &$page) {
  * @todo Remove this in favor of the 'stylesheets-additional' property proposed
  *   in https://drupal.org/node/1209958
  */
-function quickedit_library_alter(array &$library, $name, $theme = NULL) {
-  if ($name == 'quickedit/quickedit') {
-    // Retrieve the admin theme.
-    if (!isset($theme)) {
-      $theme = Drupal::config('system.theme')->get('admin');
-    }
+function quickedit_library_alter(array &$libraries, $theme = NULL) {
+  // Retrieve the admin theme.
+  if (!isset($theme)) {
+    $theme = Drupal::config('system.theme')->get('admin');
+  }
+  if (isset($libraries['quickedit/quickedit'])) {
     if ($theme && $theme_path = drupal_get_path('theme', $theme)) {
       $info = system_get_info('theme', $theme);
       // Recurse to process base theme(s) first.
       if (isset($info['base theme'])) {
-        quickedit_library_alter($library, $name, $info['base theme']);
+        quickedit_library_alter($libraries, $info['base theme']);
       }
       if (isset($info['quickedit_stylesheets']) && is_array($info['quickedit_stylesheets'])) {
         foreach ($info['quickedit_stylesheets'] as $path) {
-          $library['css'][$theme_path . '/' . $path] = array(
+          $libraries['quickedit/quickedit']['css'][$theme_path . '/' . $path] = array(
             'group' => CSS_AGGREGATE_THEME,
             'weight' => CSS_THEME,
           );
diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module
index 24aa3da..36fcbef 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -167,12 +167,12 @@ function common_test_preprocess_common_test_render_element(&$variables) {
 /**
  * Implements hook_library_info_alter().
  */
-function common_test_library_info_alter(&$libraries, $module) {
-  if ($module == 'core' && isset($libraries['jquery.farbtastic'])) {
+function common_test_library_info_alter(&$libraries) {
+  if (isset($libraries['core/jquery.farbtastic'])) {
     // Change the version of Farbtastic to 0.0.
-    $libraries['jquery.farbtastic']['version'] = '0.0';
+    $libraries['core/jquery.farbtastic']['version'] = '0.0';
     // Make Farbtastic depend on jQuery Form to test library dependencies.
-    $libraries['jquery.farbtastic']['dependencies'][] = 'core/jquery.form';
+    $libraries['core/jquery.farbtastic']['dependencies'][] = 'core/jquery.form';
   }
 }
 
diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php
index f13210d..0c73511 100644
--- a/core/modules/system/theme.api.php
+++ b/core/modules/system/theme.api.php
@@ -726,18 +726,17 @@ function hook_js_alter(&$javascript) {
  * certain library also depend on the API of a certain library version.
  *
  * @param $libraries
- *   The JavaScript/CSS libraries provided by $module. Keyed by internal library
- *   name and passed by reference.
+ *   All JavaScript/CSS libraries. Keyed by library name.
  * @param $module
  *   The name of the module that registered the libraries.
  */
-function hook_library_info_alter(&$libraries, $module) {
+function hook_library_info_alter(&$libraries) {
   // Update Farbtastic to version 2.0.
-  if ($module == 'core' && isset($libraries['jquery.farbtastic'])) {
+  if (isset($libraries['core/jquery.farbtastic'])) {
     // Verify existing version is older than the one we are updating to.
-    if (version_compare($libraries['jquery.farbtastic']['version'], '2.0', '<')) {
+    if (version_compare($libraries['core/jquery.farbtastic']['version'], '2.0', '<')) {
       // Update the existing Farbtastic to version 2.0.
-      $libraries['jquery.farbtastic']['version'] = '2.0';
+      $libraries['core/jquery.farbtastic']['version'] = '2.0';
       // To accurately replace library files, the order of files and the options
       // of each file have to be retained; e.g., like this:
       $old_path = 'assets/vendor/farbtastic';
@@ -749,7 +748,7 @@ function hook_library_info_alter(&$libraries, $module) {
       $replacements = array(
         $old_path . '/farbtastic.js' => $new_path . '/farbtastic-2.0.js',
       );
-      foreach ($libraries['jquery.farbtastic']['js'] as $source => $options) {
+      foreach ($libraries['core/jquery.farbtastic']['js'] as $source => $options) {
         if (isset($replacements[$source])) {
           $new_js[$replacements[$source]] = $options;
         }
@@ -757,7 +756,7 @@ function hook_library_info_alter(&$libraries, $module) {
           $new_js[$source] = $options;
         }
       }
-      $libraries['jquery.farbtastic']['js'] = $new_js;
+      $libraries['core/jquery.farbtastic']['js'] = $new_js;
     }
   }
 }
@@ -765,20 +764,21 @@ function hook_library_info_alter(&$libraries, $module) {
 /**
  * Alters a JavaScript/CSS library before it is attached.
  *
- * Allows modules and themes to dynamically attach further assets to a library
+ * Allows modules and themes to dynamically attach further assets to libraries
  * when it is added to the page; e.g., to add JavaScript settings.
  *
- * This hook is only invoked once per library and page.
+ * This hook is invoked once per page.
  *
- * @param array $library
- *   The JavaScript/CSS library that is being added.
+ * @param array $libraries
+ *   The Javascript/CSS libraries which are added, keyed by library name.
  * @param string $name
  *   The name of the library.
  *
  * @see _drupal_add_library()
  */
-function hook_library_alter(array &$library, $name) {
-  if ($name == 'core/jquery.ui.datepicker') {
+function hook_library_alter(array &$libraries) {
+  if (isset($libraries['core/jquery.ui.datepicker'])) {
+    $library = &$libraries['core/jquery.ui.datepicker'];
     // Note: If the added assets do not depend on additional request-specific
     // data supplied here, consider to statically register it directly via
     // hook_library_info_alter() already.
diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php
index 076b2cf..a9616a9 100644
--- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Core\Asset;
 
 use Drupal\Core\Asset\LibraryDiscoveryParser;
+use Drupal\Core\Extension\Extension;
 use Drupal\Tests\UnitTestCase;
 
 if (!defined('CSS_AGGREGATE_DEFAULT')) {
@@ -59,13 +60,21 @@ class LibraryDiscoveryParserTest extends UnitTestCase {
   protected $lock;
 
   /**
+   * The mocked theme handler.
+   *
+   * @var \Drupal\Core\Extension\ThemeHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $themeHandler;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler);
+    $this->themeHandler = $this->getMock('Drupal\Core\Extension\ThemeHandlerInterface');
+    $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeHandler);
   }
 
   /**
@@ -74,17 +83,17 @@ protected function setUp() {
    * @covers ::buildByExtension
    */
   public function testBuildByExtensionSimple() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('example_module')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('example_module', 'example');
-    $library = $libraries['example'];
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'example_module' => new Extension($this->root, 'module', $path . '/example_module.info.yml')
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['example_module/example'];
 
     $this->assertCount(0, $library['js']);
     $this->assertCount(1, $library['css']);
@@ -101,17 +110,17 @@ public function testBuildByExtensionSimple() {
    * @covers ::buildByExtension
    */
   public function testBuildByExtensionWithTheme() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('example_theme')
-      ->will($this->returnValue(FALSE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('theme', 'example_theme', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('example_theme');
-    $library = $libraries['example'];
+    $this->themeHandler->expects($this->once())
+      ->method('listInfo')
+      ->willReturn([
+        'example_theme' => new Extension($this->root, 'theme', $path .'/example_theme.info.yml'),
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['example_theme/example'];
 
     $this->assertCount(0, $library['js']);
     $this->assertCount(1, $library['css']);
@@ -125,16 +134,17 @@ public function testBuildByExtensionWithTheme() {
    * @covers ::buildByExtension
    */
   public function testBuildByExtensionWithMissingLibraryFile() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('example_module')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files_not_existing';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'example_module', $path);
 
-    $this->assertSame($this->libraryDiscoveryParser->buildByExtension('example_module'), array());
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'example_module' => new Extension($this->root, 'module', $path . '/example_module.info.yml')
+      ]);
+
+
+    $this->assertSame($this->libraryDiscoveryParser->build(), []);
   }
 
   /**
@@ -145,16 +155,16 @@ public function testBuildByExtensionWithMissingLibraryFile() {
    * @covers ::buildByExtension
    */
   public function testInvalidLibrariesFile() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('invalid_file')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'invalid_file', $path);
 
-    $this->libraryDiscoveryParser->buildByExtension('invalid_file');
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'invalid_file' => new Extension($this->root, 'module', $path . '/invalid_file.info.yml')
+      ]);
+
+    $this->libraryDiscoveryParser->build();
   }
 
   /**
@@ -166,16 +176,16 @@ public function testInvalidLibrariesFile() {
    * @covers ::buildByExtension
    */
   public function testBuildByExtensionWithMissingInformation() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('example_module_missing_information')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'example_module_missing_information', $path);
 
-    $this->libraryDiscoveryParser->buildByExtension('example_module_missing_information');
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'example_module_missing_information' => new Extension($this->root, 'module', $path . '/example_module_missing_information.info.yml')
+      ]);
+
+    $this->libraryDiscoveryParser->build();
   }
 
   /**
@@ -184,17 +194,17 @@ public function testBuildByExtensionWithMissingInformation() {
    * @covers ::buildByExtension
    */
   public function testExternalLibraries() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('external')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'external', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('external', 'example_external');
-    $library = $libraries['example_external'];
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'external' => new Extension($this->root, 'module', $path . '/external.info.yml')
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['external/example_external'];
 
     $this->assertEquals($path . '/css/example_external.css', $library['css'][0]['data']);
     $this->assertEquals('3.14', $library['version']);
@@ -206,17 +216,17 @@ public function testExternalLibraries() {
    * @covers ::buildByExtension
    */
   public function testDefaultCssWeights() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('css_weights')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'css_weights', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('css_weights');
-    $library = $libraries['example'];
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'css_weights' => new Extension($this->root, 'module', $path . '/css_weights.info.yml')
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['css_weights/example'];
     $css = $library['css'];
     $this->assertCount(10, $css);
 
@@ -246,16 +256,16 @@ public function testDefaultCssWeights() {
    * @covers ::buildByExtension
    */
   public function testJsWithPositiveWeight() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('js_positive_weight')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'js_positive_weight', $path);
 
-    $this->libraryDiscoveryParser->buildByExtension('js_positive_weight');
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'js_positive_weight' => new Extension($this->root, 'module', $path . '/js_positive_weight.info.yml')
+      ]);
+
+    $this->libraryDiscoveryParser->build();
   }
 
   /**
@@ -264,17 +274,17 @@ public function testJsWithPositiveWeight() {
    * @covers ::buildByExtension
    */
   public function testLibraryWithCssJsSetting() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('css_js_settings')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'css_js_settings', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('css_js_settings');
-    $library = $libraries['example'];
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'css_js_settings' => new Extension($this->root, 'module', $path . '/css_js_settings.info.yml')
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['css_js_settings/example'];
 
     // Ensures that the group and type are set automatically.
     $this->assertEquals(-100, $library['js'][0]['group']);
@@ -295,17 +305,18 @@ public function testLibraryWithCssJsSetting() {
    * @covers ::buildByExtension
    */
   public function testLibraryWithDependencies() {
-     $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('dependencies')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'dependencies', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('dependencies');
-    $library = $libraries['example'];
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'dependencies' => new Extension($this->root, 'module', $path . '/dependencies.info.yml')
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
+
+    $library = $libraries['dependencies/example'];
 
     $this->assertCount(2, $library['dependencies']);
     $this->assertEquals('external/example_external', $library['dependencies'][0]);
@@ -318,20 +329,20 @@ public function testLibraryWithDependencies() {
    * @covers ::buildByExtension
    */
   public function testLibraryWithDataTypes() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('data_types')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'data_types', $path);
+
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'data_types' => new Extension($this->root, 'module', $path . '/data_types.info.yml')
+      ]);
 
     $this->libraryDiscoveryParser->setFileValidUri('public://test.css', TRUE);
     $this->libraryDiscoveryParser->setFileValidUri('public://test2.css', FALSE);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('data_types');
-    $library = $libraries['example'];
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['data_types/example'];
 
     $this->assertCount(5, $library['css']);
     $this->assertEquals('external', $library['css'][0]['type']);
@@ -350,17 +361,19 @@ public function testLibraryWithDataTypes() {
    * @covers ::buildByExtension
    */
   public function testLibraryWithJavaScript() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('js')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
+
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'js' => new Extension($this->root, 'module', $path . '/js.info.yml')
+      ]);
+
     $this->libraryDiscoveryParser->setPaths('module', 'js', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('js');
-    $library = $libraries['example'];
+    $libraries = $this->libraryDiscoveryParser->build();
+    $library = $libraries['js/example'];
 
     $this->assertCount(2, $library['js']);
     $this->assertEquals(FALSE, $library['js'][0]['minified']);
@@ -375,16 +388,16 @@ public function testLibraryWithJavaScript() {
    * @covers ::buildByExtension
    */
   public function testLibraryThirdPartyWithMissingLicense() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('licenses_missing_information')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'licenses_missing_information', $path);
 
-    $this->libraryDiscoveryParser->buildByExtension('licenses_missing_information');
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'licenses_missing_information' => new Extension($this->root, 'module', $path . '/licenses_missing_information.info.yml')
+      ]);
+
+    $this->libraryDiscoveryParser->build();
   }
 
   /**
@@ -393,20 +406,20 @@ public function testLibraryThirdPartyWithMissingLicense() {
    * @covers ::buildByExtension
    */
   public function testLibraryWithLicenses() {
-    $this->moduleHandler->expects($this->atLeastOnce())
-      ->method('moduleExists')
-      ->with('licenses')
-      ->will($this->returnValue(TRUE));
-
     $path = __DIR__ . '/library_test_files';
     $path = substr($path, strlen($this->root) + 1);
-    $this->libraryDiscoveryParser->setPaths('module', 'licenses', $path);
 
-    $libraries = $this->libraryDiscoveryParser->buildByExtension('licenses');
+    $this->moduleHandler->expects($this->once())
+      ->method('getModuleList')
+      ->willReturn([
+        'licenses' => new Extension($this->root, 'module', $path . '/licenses.info.yml')
+      ]);
+
+    $libraries = $this->libraryDiscoveryParser->build();
 
 
     // For libraries without license info, the default license is applied.
-    $library = $libraries['no-license-info'];
+    $library = $libraries['licenses/no-license-info'];
     $this->assertCount(1, $library['css']);
     $this->assertCount(1, $library['js']);
     $this->assertTrue(isset($library['license']));
@@ -418,7 +431,7 @@ public function testLibraryWithLicenses() {
     $this->assertEquals($library['license'], $default_license);
 
     // GPL2-licensed libraries.
-    $library = $libraries['gpl2'];
+    $library = $libraries['licenses/gpl2'];
     $this->assertCount(1, $library['css']);
     $this->assertCount(1, $library['js']);
     $expected_license = array(
@@ -429,7 +442,7 @@ public function testLibraryWithLicenses() {
     $this->assertEquals($library['license'], $expected_license);
 
     // MIT-licensed libraries.
-    $library = $libraries['mit'];
+    $library = $libraries['licenses/mit'];
     $this->assertCount(1, $library['css']);
     $this->assertCount(1, $library['js']);
     $expected_license = array(
@@ -440,7 +453,7 @@ public function testLibraryWithLicenses() {
     $this->assertEquals($library['license'], $expected_license);
 
     // Libraries in the Public Domain.
-    $library = $libraries['public-domain'];
+    $library = $libraries['licenses/public-domain'];
     $this->assertCount(1, $library['css']);
     $this->assertCount(1, $library['js']);
     $expected_license = array(
@@ -451,7 +464,7 @@ public function testLibraryWithLicenses() {
     $this->assertEquals($library['license'], $expected_license);
 
     // Apache-licensed libraries.
-    $library = $libraries['apache'];
+    $library = $libraries['licenses/apache'];
     $this->assertCount(1, $library['css']);
     $this->assertCount(1, $library['js']);
     $expected_license = array(
@@ -462,7 +475,7 @@ public function testLibraryWithLicenses() {
     $this->assertEquals($library['license'], $expected_license);
 
     // Copyrighted libraries.
-    $library = $libraries['copyright'];
+    $library = $libraries['licenses/copyright'];
     $this->assertCount(1, $library['css']);
     $this->assertCount(1, $library['js']);
     $expected_license = array(
diff --git a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php
index 193f86d..c39baa9 100644
--- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php
+++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryTest.php
@@ -24,13 +24,6 @@ class LibraryDiscoveryTest extends UnitTestCase {
   protected $libraryDiscovery;
 
   /**
-   * The mocked library discovery cache collector.
-   *
-   * @var \Drupal\Core\Cache\CacheCollectorInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $libraryDiscoveryCollector;
-
-  /**
    * The mocked module handler.
    *
    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -43,13 +36,15 @@ class LibraryDiscoveryTest extends UnitTestCase {
    * @var array
    */
   protected $libraryData = [
-    'test_1' => [
+    'test/test_1' => [
       'js' => [],
       'css' => [
-        'foo.css' => [],
+        'theme' => [
+          'foo.css' => [],
+        ],
       ],
     ],
-    'test_2' => [
+    'test/test_2' => [
       'js' => [
         'bar.js' => [],
       ],
@@ -58,41 +53,165 @@ class LibraryDiscoveryTest extends UnitTestCase {
   ];
 
   /**
+   * The mocked cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cache;
+
+  /**
+   * The mocked library discovery parser.
+   *
+   * @var \Drupal\Core\Asset\LibraryDiscoveryParser|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $discoveryParser;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
-    $this->libraryDiscoveryCollector = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryCollector')
+    $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+    $this->discoveryParser = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryParser')
       ->disableOriginalConstructor()
       ->getMock();
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->libraryDiscovery = new LibraryDiscovery($this->libraryDiscoveryCollector, $this->moduleHandler);
+    $this->libraryDiscovery = new LibraryDiscovery($this->cache, $this->discoveryParser, $this->moduleHandler);
+  }
+
+  /**
+   * @covers ::getLibraryByName()
+   * @covers ::loadLibraryDefinitions()
+   */
+  public function testGetLibraryByNameUncached() {
+    $library_data = $this->libraryData;
+    $this->discoveryParser->expects($this->once())
+      ->method('build')
+      ->willReturn($library_data);
+
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with('library_info');
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with('library_info', $library_data);
+
+    $this->moduleHandler->expects($this->once())
+      ->method('alter')
+      ->with('library', $library_data);
+
+    $result = $this->libraryDiscovery->getLibraryByName('test', 'test_1');
+    $this->assertEquals([
+      'js' => [],
+      'css' => [
+        'theme' => [
+          'foo.css' => [],
+        ],
+      ]], $result);
+
+    $result = $this->libraryDiscovery->getLibraryByName('test', 'test_2');
+    $this->assertEquals([
+      'js' => [
+        'bar.js' => [],
+      ],
+      'css' => [],
+    ], $result);
+
+    $result = $this->libraryDiscovery->getLibraryByName('test', 'not_existing');
+    $this->assertEquals(FALSE, $result);
+    $result = $this->libraryDiscovery->getLibraryByName('test_not_existing', 'test_1');
+    $this->assertEquals(FALSE, $result);
+  }
+
+  /**
+   * @covers ::getLibraryByName()
+   * @covers ::loadLibraryDefinitions()
+   */
+  public function testGetLibraryByNameCached() {
+    $this->discoveryParser->expects($this->never())
+      ->method('build');
+
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with('library_info')
+      ->willReturn((object) ['data' => $this->libraryData]);
+    $this->cache->expects($this->never())
+      ->method('set');
+
+    $this->moduleHandler->expects($this->once())
+      ->method('alter')
+      ->with('library', $this->libraryData);
+
+    $result = $this->libraryDiscovery->getLibraryByName('test', 'test_1');
+    $this->assertEquals([
+      'js' => [],
+      'css' => [
+        'theme' => [
+          'foo.css' => [],
+        ],
+      ]], $result);
+
+    $result = $this->libraryDiscovery->getLibraryByName('test', 'test_2');
+    $this->assertEquals([
+      'js' => [
+        'bar.js' => [],
+      ],
+      'css' => [],
+    ], $result);
+
+    $result = $this->libraryDiscovery->getLibraryByName('test', 'not_existing');
+    $this->assertEquals(FALSE, $result);
+    $result = $this->libraryDiscovery->getLibraryByName('test_not_existing', 'test_1');
+    $this->assertEquals(FALSE, $result);
+  }
+
+  /**
+   * @covers ::getLibrariesByExtension()
+   * @covers ::loadLibraryDefinitions()
+   */
+  public function testGetLibraryByExtensionUncached() {
+    $library_data = $this->libraryData;
+    $this->discoveryParser->expects($this->once())
+      ->method('build')
+      ->willReturn($library_data);
+
+    $this->cache->expects($this->once())
+      ->method('get')
+      ->with('library_info');
+    $this->cache->expects($this->once())
+      ->method('set')
+      ->with('library_info', $library_data);
+
+    $this->moduleHandler->expects($this->once())
+      ->method('alter')
+      ->with('library', $library_data);
+
+    $this->assertEquals($this->libraryData, $this->libraryDiscovery->getLibrariesbyExtension('test'));
+    $this->assertEquals([], $this->libraryDiscovery->getLibrariesbyExtension('test_not_existing'));
   }
 
   /**
    * @covers ::getLibrariesByExtension()
+   * @covers ::loadLibraryDefinitions()
    */
-  public function testGetLibrariesByExtension() {
-    $this->libraryDiscoveryCollector->expects($this->once())
+  public function testGetLibraryByExtensionCached() {
+    $this->discoveryParser->expects($this->never())
+      ->method('build');
+
+    $this->cache->expects($this->once())
       ->method('get')
-      ->with('test')
-      ->willReturn($this->libraryData);
-    $this->moduleHandler->expects($this->exactly(2))
+      ->with('library_info')
+      ->willReturn((object) ['data' => $this->libraryData]);
+    $this->cache->expects($this->never())
+      ->method('set');
+
+    $this->moduleHandler->expects($this->once())
       ->method('alter')
-      ->with(
-        'library',
-        $this->logicalOr($this->libraryData['test_1'], $this->libraryData['test_2']),
-        $this->logicalOr('test/test_1', 'test/test_2')
-      );
-
-    $this->libraryDiscovery->getLibrariesbyExtension('test');
-    // Verify that subsequent calls don't trigger hook_library_alter()
-    // invocations, nor do they talk to the collector again. This ensures that
-    // the alterations made by hook_library_alter() implementations are
-    // statically cached, as desired.
-    $this->libraryDiscovery->getLibraryByName('test', 'test_1');
-    $this->libraryDiscovery->getLibrariesbyExtension('test');
+      ->with('library', $this->libraryData);
+
+    $this->assertEquals($this->libraryData, $this->libraryDiscovery->getLibrariesbyExtension('test'));
+    $this->assertEquals([], $this->libraryDiscovery->getLibrariesbyExtension('test_not_existing'));
   }
 
 }
