 core/modules/comment/comment.api.php               |   33 ++++++++++
 .../lib/Drupal/comment/CommentInterface.php        |    2 +-
 .../lib/Drupal/comment/CommentViewBuilder.php      |   68 +++++++++++++++++---
 .../Drupal/comment/Tests/CommentLinksAlterTest.php |   46 +++++++++++++
 .../tests/modules/comment_test/comment_test.module |   25 +++++++
 5 files changed, 163 insertions(+), 11 deletions(-)

diff --git a/core/modules/comment/comment.api.php b/core/modules/comment/comment.api.php
index e43c577..fac9547 100644
--- a/core/modules/comment/comment.api.php
+++ b/core/modules/comment/comment.api.php
@@ -1,6 +1,7 @@
 <?php
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\comment\CommentInterface;
 
 /**
  * @file
@@ -197,5 +198,37 @@ function hook_comment_delete(Drupal\comment\Comment $comment) {
 }
 
 /**
+ * Alter the links of a comment.
+ *
+ * @param array &$links
+ *   A renderable array representing the comment links.
+ * @param \Drupal\comment\CommentInterface $entity
+ *   The comment being rendered.
+ * @param array &$context
+ *   Various aspects of the context in which the comment links are going to be
+ *   displayed, with the following keys:
+ *   - 'view_mode': the view mode in which the comment is being viewed
+ *   - 'langcode': the language in which the comment is being viewed
+ *   - 'commented_entity': the entity to which the comment is attached
+ *
+ * @see \Drupal\comment\CommentViewBuilder::renderLinks()
+ * @see \Drupal\comment\CommentViewBuilder::buildLinks()
+ */
+function hook_comment_links_alter(array &$links, CommentInterface $entity, array &$context) {
+  $links['mymodule'] = array(
+    '#theme' => 'links__comment__mymodule',
+    '#attributes' => array('class' => array('links', 'inline')),
+    '#links' => array(
+      'comment-report' => array(
+        'title' => t('Report'),
+        'href' => "comment/{$entity->id()}/report",
+        'html' => TRUE,
+        'query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("comment/{$entity->id()}/report")),
+      ),
+    ),
+  );
+}
+
+/**
  * @} End of "addtogroup hooks".
  */
diff --git a/core/modules/comment/lib/Drupal/comment/CommentInterface.php b/core/modules/comment/lib/Drupal/comment/CommentInterface.php
index fb9c043..fe7f40d 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentInterface.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\comment\Entity\CommentInterface.
+ * Contains \Drupal\comment\CommentInterface.
  */
 
 namespace Drupal\comment;
diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
index 2713d52..b38460b 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
@@ -127,13 +127,17 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
       $entity->content['#entity'] = $entity;
       $entity->content['#theme'] = 'comment__' . $entity->field_id->value . '__' . $commented_entity->bundle();
       $entity->content['links'] = array(
-        '#theme' => 'links__comment',
-        '#pre_render' => array('drupal_pre_render_links'),
-        '#attributes' => array('class' => array('links', 'inline')),
+        '#type' => 'render_cache_placeholder',
+        '#callback' => '\Drupal\comment\CommentViewBuilder::renderLinks',
+        '#context' => array(
+          'comment_entity_id' => $entity->id(),
+          'view_mode' => $view_mode,
+          'langcode' => $langcode,
+          'commented_entity_type' => $commented_entity->entityType(),
+          'commented_entity_id' => $commented_entity->id(),
+          'in_preview' => !empty($entity->in_preview),
+        ),
       );
-      if (empty($entity->in_preview)) {
-        $entity->content['links'][$this->entityType] = $this->buildLinks($entity, $commented_entity);
-      }
 
       if (!isset($entity->content['#attached'])) {
         $entity->content['#attached'] = array();
@@ -146,9 +150,51 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
   }
 
   /**
+   * #post_render_cache callback; replaces the placeholder with comment links.
+   *
+   * Renders the links on a comment.
+   *
+   * @param array $context
+   *   An array with the following keys:
+   *   - comment_entity_id: a comment entity ID
+   *   - view_mode: the view mode in which the comment entity is being viewed
+   *   - langcode: in which language the comment entity is being viewed
+   *   - commented_entity_type: the entity type to which the comment is attached
+   *   - commented_entity_id: the entity ID to which the comment is attached
+   *   - in_preview: whether the comment is currently being previewed
+   *
+   * @return array
+   *   A renderable array representing the comment links.
+   */
+  public static function renderLinks(array $context) {
+    $links = array(
+      '#theme' => 'links__comment',
+      '#pre_render' => array('drupal_pre_render_links'),
+      '#attributes' => array('class' => array('links', 'inline')),
+    );
+
+    if (!$context['in_preview']) {
+      $entity = entity_load('comment', $context['comment_entity_id']);
+      $commented_entity = entity_load($context['commented_entity_type'], $context['commented_entity_id']);
+
+      $links['comment'] = self::buildLinks($entity, $commented_entity);
+
+      // Allow other modules to alter the comment links.
+      $hook_context = array(
+        'view_mode' => $context['view_mode'],
+        'langcode' => $context['langcode'],
+        'commented_entity' => $commented_entity
+      );
+      \Drupal::moduleHandler()->alter('comment_links', $links, $entity, $hook_context);
+    }
+
+    return $links;
+  }
+
+  /**
    * Build the default links (reply, edit, delete …) for a comment.
    *
-   * @param \Drupal\comment\Entity\CommentInterface $entity
+   * @param \Drupal\comment\CommentInterface $entity
    *   The comment object.
    * @param \Drupal\Core\Entity\EntityInterface $commented_entity
    *   The entity to which the comment is attached.
@@ -156,10 +202,12 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
    * @return array
    *   An array that can be processed by drupal_pre_render_links().
    */
-  protected function buildLinks(CommentInterface $entity, EntityInterface $commented_entity) {
+  protected static function buildLinks(CommentInterface $entity, EntityInterface $commented_entity) {
     $links = array();
     $status = $commented_entity->get($entity->field_name->value)->status;
 
+    $container = \Drupal::getContainer();
+
     if ($status == COMMENT_OPEN) {
       if ($entity->access('delete')) {
         $links['comment-delete'] = array(
@@ -188,7 +236,7 @@ protected function buildLinks(CommentInterface $entity, EntityInterface $comment
           'title' => t('Approve'),
           'href' => "comment/{$entity->id()}/approve",
           'html' => TRUE,
-          'query' => array('token' => $this->csrfToken->get("comment/{$entity->id()}/approve")),
+          'query' => array('token' => $container->get('csrf_token')->get("comment/{$entity->id()}/approve")),
         );
       }
       if (empty($links)) {
@@ -203,7 +251,7 @@ protected function buildLinks(CommentInterface $entity, EntityInterface $comment
     }
 
     // Add translations link for translation-enabled comment bundles.
-    if ($this->moduleHandler->moduleExists('content_translation') && content_translation_translate_access($entity)) {
+    if ($container->get('module_handler')->moduleExists('content_translation') && content_translation_translate_access($entity)) {
       $links['comment-translations'] = array(
         'title' => t('Translate'),
         'href' => 'comment/' . $entity->id() . '/translations',
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksAlterTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksAlterTest.php
new file mode 100644
index 0000000..be11b76
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLinksAlterTest.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\comment\Tests\CommentLinksAlterTest.
+ */
+
+namespace Drupal\comment\Tests;
+
+/**
+ * Tests comment links altering.
+ */
+class CommentLinksAlterTest extends CommentTestBase {
+
+  public static $modules = array('comment_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'hook_comment_links_alter()',
+      'description' => 'Tests comment links altering.',
+      'group' => 'Comment',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+
+    // Enable comment_test.module's hook_comment_links_alter() implementation.
+    $this->container->get('state')->set('comment_test_links_alter_enabled', TRUE);
+  }
+
+  /**
+   * Tests comment links altering.
+   */
+  public function testCommentLinksAlter() {
+    $this->drupalLogin($this->web_user);
+    $comment_text = $this->randomName();
+    $subject = $this->randomName();
+    $comment = $this->postComment($this->node, $comment_text, $subject);
+
+    $this->drupalGet('node/' . $this->node->id());
+
+    $this->assertLink(t('Report'));
+  }
+
+}
diff --git a/core/modules/comment/tests/modules/comment_test/comment_test.module b/core/modules/comment/tests/modules/comment_test/comment_test.module
index 8e903db..da43a04 100644
--- a/core/modules/comment/tests/modules/comment_test/comment_test.module
+++ b/core/modules/comment/tests/modules/comment_test/comment_test.module
@@ -6,6 +6,8 @@
  * the Comment module.
  */
 
+use Drupal\comment\CommentInterface;
+
 /**
  * Implements hook_entity_info_alter().
  */
@@ -15,3 +17,26 @@ function comment_test_entity_info_alter(&$info) {
     $info['comment']['translation']['comment_test'] = TRUE;
   }
 }
+
+/**
+ * Implements hook_comment_links_alter().
+ */
+function comment_test_comment_links_alter(array &$links, CommentInterface &$entity, array &$context) {
+  // Allow tests to enable or disable this alter hook.
+  if (!\Drupal::state()->get('comment_test_links_alter_enabled', FALSE)) {
+    return;
+  }
+
+  $links['comment_test'] = array(
+    '#theme' => 'links__comment__comment_test',
+    '#attributes' => array('class' => array('links', 'inline')),
+    '#links' => array(
+      'comment-report' => array(
+        'title' => t('Report'),
+        'href' => "comment/{$entity->id()}/report",
+        'html' => TRUE,
+        'query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("comment/{$entity->id()}/report")),
+      ),
+    ),
+  );
+}
