diff --git a/core/core.services.yml b/core/core.services.yml
index 0b18c3b..cc5ef06 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1519,6 +1519,12 @@ services:
     class: Drupal\Core\Asset\JsCollectionGrouper
   asset.js.dumper:
     class: Drupal\Core\Asset\AssetDumper
+  asset.js.optimizer_license_web_labels_annotator:
+    class: Drupal\Core\Asset\JsLicenseWebLabelsAnnotator
+    arguments: [ '@url_generator' ]
+  asset.js.collection_optimizer_license_web_labels_annotator:
+    class: Drupal\Core\Asset\JsCollectionOptimizer
+    arguments: [ '@asset.js.collection_grouper', '@asset.js.optimizer_license_web_labels_annotator', '@asset.js.dumper', '@state', 'system.js_license_web_labels_files' ]
   library.discovery:
     class: Drupal\Core\Asset\LibraryDiscovery
     arguments: ['@library.discovery.collector']
diff --git a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
index 566722d..cff6b88 100644
--- a/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
+++ b/core/lib/Drupal/Core/Asset/JsCollectionOptimizer.php
@@ -4,7 +4,6 @@
 
 use Drupal\Core\State\StateInterface;
 
-
 /**
  * Optimizes JavaScript assets.
  */
@@ -39,6 +38,13 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface {
   protected $state;
 
   /**
+   * The name for the JavaScript asset collections optimized by this instance.
+   *
+   * @var string
+   */
+  protected $name;
+
+  /**
    * Constructs a JsCollectionOptimizer.
    *
    * @param \Drupal\Core\Asset\AssetCollectionGrouperInterface $grouper
@@ -49,12 +55,16 @@ class JsCollectionOptimizer implements AssetCollectionOptimizerInterface {
    *   The dumper for optimized JS assets.
    * @param \Drupal\Core\State\StateInterface $state
    *   The state key/value store.
+   * @param string
+   *   (optional) The name for the JavaScript asset collections optimized by
+   *   this instance.
    */
-  public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state) {
+  public function __construct(AssetCollectionGrouperInterface $grouper, AssetOptimizerInterface $optimizer, AssetDumperInterface $dumper, StateInterface $state, $name = 'system.js_cache_files') {
     $this->grouper = $grouper;
     $this->optimizer = $optimizer;
     $this->dumper = $dumper;
     $this->state = $state;
+    $this->name = $name;
   }
 
   /**
@@ -81,7 +91,7 @@ public function optimize(array $js_assets) {
     // Drupal contrib can override this default JS aggregator to keep the same
     // grouping, optimizing and dumping, but change the strategy that is used to
     // determine when the aggregate should be rebuilt (e.g. mtime, HTTPS …).
-    $map = $this->state->get('system.js_cache_files') ?: array();
+    $map = $this->state->get($this->name) ?: array();
     $js_assets = array();
     foreach ($js_groups as $order => $js_group) {
       // We have to return a single asset, not a group of assets. It is now up
@@ -105,7 +115,7 @@ public function optimize(array $js_assets) {
               $uri = $map[$key];
             }
             if (empty($uri) || !file_exists($uri)) {
-              // Concatenate each asset within the group.
+              // Concatenate and optimize each asset within the group.
               $data = '';
               foreach ($js_group['items'] as $js_asset) {
                 // Optimize this JS file, but only if it's not yet minified.
@@ -127,7 +137,7 @@ public function optimize(array $js_assets) {
               $js_assets[$order]['data'] = $uri;
               // Persist the URI for this aggregate file.
               $map[$key] = $uri;
-              $this->state->set('system.js_cache_files', $map);
+              $this->state->set($this->name, $map);
             }
             else {
               // Use the persisted URI for the optimized JS file.
@@ -170,14 +180,14 @@ protected function generateHash(array $js_group) {
    * {@inheritdoc}
    */
   public function getAll() {
-    return $this->state->get('system.js_cache_files');
+    return $this->state->get($this->name);
   }
 
   /**
    * {@inheritdoc}
    */
   public function deleteAll() {
-    $this->state->delete('system.js_cache_files');
+    $this->state->delete($this->name);
     $delete_stale = function($uri) {
       // Default stale file threshold is 30 days.
       if (REQUEST_TIME - filemtime($uri) > \Drupal::config('system.performance')->get('stale_file_threshold')) {
diff --git a/core/lib/Drupal/Core/Asset/JsLicenseWebLabelsAnnotator.php b/core/lib/Drupal/Core/Asset/JsLicenseWebLabelsAnnotator.php
new file mode 100644
index 0000000..6e8ef5d
--- /dev/null
+++ b/core/lib/Drupal/Core/Asset/JsLicenseWebLabelsAnnotator.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * Contains \Drupal\Core\Asset\JsLicenseWebLabelsAnnotator.
+ */
+
+namespace Drupal\Core\Asset;
+
+use Drupal\Core\Routing\UrlGeneratorInterface;
+use Drupal\Component\Utility\Html;
+
+/**
+ * Annotates a JavaScript asset with a link to its license information.j
+ *
+ * @see \Drupal\system\Controller\AssetLicenseInfoController
+ */
+class JsLicenseWebLabelsAnnotator implements AssetOptimizerInterface {
+
+  /**
+   * The URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * Constructs a JsLicenseWebLabelsAnnotator.
+   *
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface
+   *   The URL generator.
+   */
+  public function __construct(UrlGeneratorInterface $url_generator) {
+    $this->urlGenerator = $url_generator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function optimize(array $js_asset) {
+    if ($js_asset['type'] !== 'file') {
+      throw new \Exception('Only file JavaScript assets can be optimized.');
+    }
+    if ($js_asset['type'] === 'file' && !$js_asset['preprocess']) {
+      throw new \Exception('Only file JavaScript assets with preprocessing enabled can be optimized.');
+    }
+
+    // Generate a prefix to be prepended to the "optimized" asset (no actual
+    // optimizations are made; this is a no-op optimizer) that deep-links to the
+    // license information on the JavaScript License Web Labels page.
+    $url = $this->urlGenerator->generateFromRoute('system.javascript_license_web_labels', [], ['fragment' => Html::cleanCssIdentifier($js_asset['data'])]);
+    $prefix = "/** JavaScript asset: " . $js_asset['data'] . '; for license information, see ' . $url  . "  **/\n\n";
+
+    return $prefix . file_get_contents($js_asset['data']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clean($content) {
+    return $content;
+  }
+
+}
diff --git a/core/modules/system/src/Controller/AssetLicenseInfoController.php b/core/modules/system/src/Controller/AssetLicenseInfoController.php
new file mode 100644
index 0000000..0abeba9
--- /dev/null
+++ b/core/modules/system/src/Controller/AssetLicenseInfoController.php
@@ -0,0 +1,182 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Controller\AssetLicenseInfoController.
+ */
+
+namespace Drupal\system\Controller;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\Asset\AssetCollectionOptimizerInterface;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Asset\LibraryDiscoveryInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Returns responses for Asset License Info routes.
+ */
+class AssetLicenseInfoController extends ControllerBase {
+
+  /**
+   * The asset library discovery service.
+   *
+   * @var \Drupal\Core\Asset\LibraryDiscoveryInterface
+   */
+  protected $libraryDiscovery;
+
+  /**
+   * The JavaScript asset collection optimizer service.
+   *
+   * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
+   */
+  protected $jsCollectionOptimizer;
+
+  /**
+   * The JavaScript asset collection optimizer license annotator service.
+   *
+   * @var \Drupal\Core\Asset\AssetCollectionOptimizerInterface
+   */
+  protected $jsCollectionOptimizerLicenseAnnotator;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('library.discovery'),
+      $container->get('asset.js.collection_optimizer'),
+      $container->get('asset.js.collection_optimizer_license_web_labels_annotator')
+    );
+  }
+
+  /**
+   * Constructs a AssetLicenseInfoController object.
+   *
+   * @param \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery
+   *   The asset library discovery service.
+   * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer
+   *   The JavaScript asset collection optimizer service.
+   * @param \Drupal\Core\Asset\AssetCollectionOptimizerInterface $js_collection_optimizer_license_annotator
+   *   The JavaScript asset collection optimizer license annotator service.
+   */
+  public function __construct(LibraryDiscoveryInterface $library_discovery, AssetCollectionOptimizerInterface $js_collection_optimizer, AssetCollectionOptimizerInterface $js_collection_optimizer_license_annotator) {
+    $this->libraryDiscovery = $library_discovery;
+    $this->jsCollectionOptimizer = $js_collection_optimizer;
+    $this->jsCollectionOptimizerLicenseAnnotator = $js_collection_optimizer_license_annotator;
+  }
+
+  /**
+   * Returns the JavaScript License Web Labels page for all JavaScript assets.
+   *
+   * @see http://www.gnu.org/licenses/javascript-labels.html
+   *
+   * @return array()
+   *   A renderable array.
+   */
+  public function jslicense() {
+    $rows = array();
+
+    // List all extensions' JavaScript assets.
+    $module_list = $this->moduleHandler()->getModuleList();
+    $extensions = array_merge(array('core'), array_keys($module_list));
+    foreach ($extensions as $extension) {
+      $libraries = $this->libraryDiscovery->getLibrariesByExtension($extension);
+      $rows_for_extension = array();
+      foreach ($libraries as $library) {
+        $license_column = l($library['license']['name'], $library['license']['url']);
+        if (!isset($library['js'])) {
+          continue;
+        }
+        foreach ($library['js'] as $js_asset) {
+          // This is core's special "drupalSettings" JavaScript asset.
+          if ($js_asset['type'] === 'setting') {
+            continue;
+          }
+          else if ($js_asset['type'] === 'external') {
+            $url = $js_asset['data'];
+          }
+          else {
+            $url = file_create_url($js_asset['data']);
+          }
+          $js_asset_basename = basename($js_asset['data']);
+          $js_file_column = l('<code>' . $js_asset_basename . '</code>', $url, array('html' => TRUE));
+          $source_column = l('<code>' . $js_asset_basename . '</code>', $url, array('html' => TRUE));
+          $rows_for_extension[] = array(
+            'data' => array($js_file_column, $license_column, $source_column),
+            'id' => drupal_clean_css_identifier($js_asset['data']),
+          );
+        }
+      }
+
+      // Add header for the current extension; add all rows for this extension.
+      if (count($rows_for_extension)) {
+        if ($extension !== 'core') {
+          $rows[] = array(
+            array(
+              'data' => $this->t('JavaScript files of the <code>@extension-name</code> @extension-type', array(
+                  '@extension-name' => $module_list[$extension]->getName(),
+                  '@extension-type' => $module_list[$extension]->getType(),
+                )),
+              'header' => TRUE,
+              'colspan' => 3,
+            ),
+          );
+        }
+        foreach ($rows_for_extension as $row) {
+          $rows[] = $row;
+        }
+      }
+    }
+
+    // List all aggregated (and optimized) JavaScript assets.
+    $rows[] = array(
+      array(
+        'data' => $this->t('Aggregated & optimized (minified) JavaScript files'),
+        'header' => TRUE,
+        'colspan' => 3,
+      ),
+    );
+
+    $aggregated_js_assets = $this->jsCollectionOptimizer->getAll();
+    if (count($aggregated_js_assets)) {
+      $unoptimized_aggregated_js_assets = $this->jsCollectionOptimizerLicenseAnnotator->getAll();
+      $license_column = $this->t('Combination');
+      foreach (array_keys($aggregated_js_assets) as $hash) {
+        $optimized_js = $aggregated_js_assets[$hash];
+        $unoptimized_js = $unoptimized_aggregated_js_assets[$hash];
+        $js_file_column = l('<code>' . basename($optimized_js) . '</code>', file_create_url($optimized_js), array('html' => TRUE));
+        $source_column = l('<code>' . basename($unoptimized_js) . '</code>', file_create_url($unoptimized_js), array('html' => TRUE));
+        $rows[] = array($js_file_column, $license_column, $source_column);
+      }
+    }
+
+    $table = array(
+      '#type' => 'table',
+      '#header' => array(
+        'javascript_asset' => array(
+          'data' => $this->t('JavaScript file'),
+        ),
+        'license' => array(
+          'data' => $this->t('License'),
+        ),
+        'source' => array(
+          'data' => $this->t('Source code'),
+          'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
+        ),
+
+      ),
+      '#sticky' => TRUE,
+      '#responsive' => TRUE,
+      '#rows' => $rows,
+      '#attributes' => array(
+        // This ID is required by the JavaScript License Web Labels standard;
+        // see http://www.gnu.org/licenses/javascript-labels.html.
+        'id' => 'jslicense-labels1',
+      ),
+    );
+
+    return $table;
+  }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 2f627be..884c0d6 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1056,6 +1056,12 @@ function _system_rebuild_module_data_ensure_required($module, &$modules) {
       }
     }
   }
+
+  // Add the JavaScript Web License Labels link to all pages.
+  $page['#attached']['drupal_add_html_head_link'][][] = array(
+    'rel' => 'jslicense',
+    'href' => \Drupal::urlGenerator()->generateFromRoute('system.javascript_license_web_labels'),
+  );
 }
 
 /**
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index ae89fb0..45fdcca 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -507,3 +507,12 @@ system.csrftoken:
     _controller: '\Drupal\system\Controller\CsrfTokenController::csrfToken'
   requirements:
     _access: 'TRUE'
+
+system.javascript_license_web_labels:
+  path: '/system/jslicense'
+  defaults:
+    _content: '\Drupal\system\Controller\AssetLicenseInfoController::jslicense'
+    _title: 'JavaScript license information'
+  requirements:
+    _access: 'TRUE'
+     
