diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 973bccbdd2e9f21980c92029af1de5abad797f60..c3b0357fd4d069cf06b9d0eb7d6e5c9e81d4a8de 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -228,14 +228,6 @@ function comment_menu() {
     'route_name' => 'comment_confirm_delete',
     'weight' => 20,
   );
-  $items['comment/reply/%node'] = array(
-    'title' => 'Add new comment',
-    'page callback' => 'comment_reply',
-    'page arguments' => array(2),
-    'access callback' => 'node_access',
-    'access arguments' => array('view', 2),
-    'file' => 'comment.pages.inc',
-  );
 
   return $items;
 }
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
deleted file mode 100644
index d193132b84ff1a54f73314290b791ce1b7e90698..0000000000000000000000000000000000000000
--- a/core/modules/comment/comment.pages.inc
+++ /dev/null
@@ -1,101 +0,0 @@
-<?php
-
-/**
- * @file
- * User page callbacks for the Comment module.
- */
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\comment\Entity\Comment;
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
-/**
- * Form constructor for the comment reply form.
- *
- * There are several cases that have to be handled, including:
- *   - replies to comments
- *   - replies to nodes
- *   - attempts to reply to nodes that can no longer accept comments
- *   - respecting access permissions ('access comments', 'post comments', etc.)
- *
- * The node or comment that is being replied to must appear above the comment
- * form to provide the user context while authoring the comment.
- *
- * @param \Drupal\Core\Entity\EntityInterface $node
- *   Every comment belongs to a node. This is that node.
- * @param $pid
- *   (optional) Some comments are replies to other comments. In those cases,
- *   $pid is the parent comment's comment ID. Defaults to NULL.
- *
- * @return array
- *   An associative array containing:
- *   - An array for rendering the node or parent comment.
- *     - comment_node: If the comment is a reply to the node.
- *     - comment_parent: If the comment is a reply to another comment.
- *   - comment_form: The comment form as a renderable array.
- */
-function comment_reply(EntityInterface $node, $pid = NULL) {
-  // Set the breadcrumb trail.
-  drupal_set_breadcrumb(array(l(t('Home'), NULL), l($node->label(), 'node/' . $node->id())));
-  $op = Drupal::request()->request->get('op');
-  $build = array();
-
-  // The user is previewing a comment prior to submitting it.
-  if ($op == t('Preview')) {
-    if (user_access('post comments')) {
-      $build['comment_form'] = comment_add($node, $pid);
-    }
-    else {
-      drupal_set_message(t('You are not authorized to post comments.'), 'error');
-      return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
-    }
-  }
-  else {
-    // $pid indicates that this is a reply to a comment.
-    if ($pid) {
-      if (user_access('access comments')) {
-        // Load the parent comment.
-        $comment = comment_load($pid);
-        if ($comment->status->value == COMMENT_PUBLISHED) {
-          // If that comment exists, make sure that the current comment and the
-          // parent comment both belong to the same parent node.
-          if ($comment->nid->target_id != $node->id()) {
-            // Attempting to reply to a comment not belonging to the current nid.
-            drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
-            return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
-          }
-          // Display the parent comment
-          $build['comment_parent'] = comment_view($comment);
-        }
-        else {
-          drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
-          return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
-        }
-      }
-      else {
-        drupal_set_message(t('You are not authorized to view comments.'), 'error');
-        return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
-      }
-    }
-    // This is the case where the comment is in response to a node. Display the node.
-    elseif (user_access('access content')) {
-      $build['comment_node'] = node_view($node);
-    }
-
-    // Should we show the reply box?
-    if ($node->comment->value != COMMENT_NODE_OPEN) {
-      drupal_set_message(t("This discussion is closed: you can't post new comments."), 'error');
-      return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
-    }
-    elseif (user_access('post comments')) {
-      $build['comment_form'] = comment_add($node, $pid);
-    }
-    else {
-      drupal_set_message(t('You are not authorized to post comments.'), 'error');
-      return new RedirectResponse(url('node/' . $node->id(), array('absolute' => TRUE)));
-    }
-  }
-
-  return $build;
-}
diff --git a/core/modules/comment/comment.routing.yml b/core/modules/comment/comment.routing.yml
index 7768cd07f34ab98e3a0aeff163bbbbf999a2ee33..948af220de67c96611dce900669406b41d8d65af 100644
--- a/core/modules/comment/comment.routing.yml
+++ b/core/modules/comment/comment.routing.yml
@@ -19,9 +19,18 @@ comment_permalink:
     _controller: '\Drupal\comment\Controller\CommentController::commentPermalink'
   requirements:
     _entity_access: 'comment.view'
+
 comment_confirm_delete:
   pattern: '/comment/{comment}/delete'
   defaults:
     _entity_form: 'comment.delete'
   requirements:
     _entity_access: 'comment.delete'
+
+comment_reply:
+  pattern: 'comment/reply/{node}/{pid}'
+  defaults:
+    _content: '\Drupal\comment\Controller\CommentController::getReplyForm'
+    pid: ~
+  requirements:
+    _entity_access: 'node.view'
diff --git a/core/modules/comment/comment.services.yml b/core/modules/comment/comment.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..074d8120fd795aa6053f564b448199c761bd1e3d
--- /dev/null
+++ b/core/modules/comment/comment.services.yml
@@ -0,0 +1,6 @@
+services:
+  comment.breadcrumb:
+    class: Drupal\comment\CommentBreadcrumbBuilder
+    tags:
+      - { name: breadcrumb_builder, priority: 100 }
+    arguments: ['@string_translation']
diff --git a/core/modules/comment/lib/Drupal/comment/CommentBreadcrumbBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentBreadcrumbBuilder.php
new file mode 100644
index 0000000000000000000000000000000000000000..38dd91fcc8e7b424a2ef64bb43d2e11c535b255e
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/CommentBreadcrumbBuilder.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment\CommentBreadcrumbBuilder.
+ */
+
+namespace Drupal\comment;
+
+use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\StringTranslation\TranslationManager;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+
+/**
+ * Class to define the comment breadcrumb builder.
+ */
+class CommentBreadcrumbBuilder implements BreadcrumbBuilderInterface {
+
+  /**
+   * The translation manager service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationManager
+   */
+  protected $translation;
+
+  /**
+   * Constructs a CommentBreadcrumbBuilder object.
+   *
+   * @param \Drupal\Core\StringTranslation\TranslationManager $translation
+   *   The translation manager.
+   */
+  public function __construct(TranslationManager $translation) {
+    $this->translation = $translation;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $attributes) {
+    if (isset($attributes[RouteObjectInterface::ROUTE_NAME]) && $attributes[RouteObjectInterface::ROUTE_NAME] == 'comment_reply' && isset($attributes['node'])) {
+      $node = $attributes['node'];
+      $uri = $node->uri();
+      $breadcrumb[] = l($this->t('Home'), NULL);
+      $breadcrumb[] = l($node->label(), $uri['path']);
+      return $breadcrumb;
+    }
+  }
+
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return $this->translation->translate($string, $args, $options);
+  }
+
+}
diff --git a/core/modules/comment/lib/Drupal/comment/Controller/CommentController.php b/core/modules/comment/lib/Drupal/comment/Controller/CommentController.php
index f7ee85b56eb9b0f36daf41ea4ac9b4f8c3c1ff0e..d3f41507a02faefb0027efcca6edb7614a0fe717 100644
--- a/core/modules/comment/lib/Drupal/comment/Controller/CommentController.php
+++ b/core/modules/comment/lib/Drupal/comment/Controller/CommentController.php
@@ -9,8 +9,10 @@
 
 use Drupal\comment\CommentInterface;
 use Drupal\comment\Entity\Comment;
+use Drupal\Core\Access\CsrfTokenGenerator;
+use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Routing\UrlGeneratorInterface;
+use Drupal\Node\NodeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Request;
@@ -23,41 +25,41 @@
  *
  * @see \Drupal\comment\Entity\Comment.
  */
-class CommentController implements ContainerInjectionInterface {
+class CommentController extends ControllerBase implements ContainerInjectionInterface {
 
   /**
-   * The url generator service.
+   * The HTTP kernel.
    *
-   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
    */
-  protected $urlGenerator;
+  protected $httpKernel;
 
   /**
-   * The HTTP kernel.
+   * The CSRF token manager service.
    *
-   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
+   * @var \Drupal\Core\Access\CsrfTokenGenerator
    */
-  protected $httpKernel;
+  protected $csrfToken;
 
   /**
    * Constructs a CommentController object.
    *
-   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
-   *   The url generator service.
    * @param \Symfony\Component\HttpKernel\HttpKernelInterface $httpKernel
    *   HTTP kernel to handle requests.
+   * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
+   *   The CSRF token manager service.
    */
-  public function __construct(UrlGeneratorInterface $url_generator, HttpKernelInterface $httpKernel) {
-    $this->urlGenerator = $url_generator;
+  public function __construct(HttpKernelInterface $httpKernel, CsrfTokenGenerator $csrf_token) {
     $this->httpKernel = $httpKernel;
+    $this->csrfToken = $csrf_token;
   }
   /**
    * {@inheritdoc}
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('url_generator'),
-      $container->get('http_kernel')
+      $container->get('http_kernel'),
+      $container->get('csrf_token')
     );
   }
 
@@ -78,17 +80,17 @@ public function commentApprove(Request $request, CommentInterface $comment) {
     //   Integrate CSRF link token directly into routing system:
     //   https://drupal.org/node/1798296.
     $token = $request->query->get('token');
-    if (!isset($token) || !drupal_valid_token($token, 'comment/' . $comment->id() . '/approve')) {
+    if (!isset($token) || !$this->csrfToken->validate($token, 'comment/' . $comment->id() . '/approve')) {
       throw new AccessDeniedHttpException();
     }
 
     $comment->status->value = COMMENT_PUBLISHED;
     $comment->save();
 
-    drupal_set_message(t('Comment approved.'));
+    drupal_set_message($this->t('Comment approved.'));
     $permalink_uri = $comment->permalink();
     $permalink_uri['options']['absolute'] = TRUE;
-    $url = $this->urlGenerator->generateFromPath($permalink_uri['path'], $permalink_uri['options']);
+    $url = $this->urlGenerator()->generateFromPath($permalink_uri['path'], $permalink_uri['options']);
     return new RedirectResponse($url);
   }
 
@@ -132,4 +134,93 @@ public function commentPermalink(Request $request, CommentInterface $comment) {
     throw new NotFoundHttpException();
   }
 
+  /**
+   * Form constructor for the comment reply form.
+   *
+   * Both replies on the node itself and replies on other comments are
+   * supported. To provide context, the node or comment that is being replied on
+   * will be displayed along the comment reply form.
+   * The constructor takes care of access permissions and checks whether the
+   * node still accepts comments.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request object.
+   * @param \Drupal\node\NodeInterface $node
+   *   Every comment belongs to a node. This is that node.
+   * @param int $pid
+   *   (optional) Some comments are replies to other comments. In those cases,
+   *   $pid is the parent comment's ID. Defaults to NULL.
+   *
+   * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
+   *   One of the following:
+   *   - An associative array containing:
+   *     - An array for rendering the node or parent comment.
+   *        - comment_node: If the comment is a reply to the node.
+   *        - comment_parent: If the comment is a reply to another comment.
+   *     - comment_form: The comment form as a renderable array.
+   *   - A redirect response to current node:
+   *     - If user is not authorized to post comments.
+   *     - If parent comment doesn't belong to current node.
+   *     - If user is not authorized to view comments.
+   *     - If current node comments are disable.
+   */
+  public function getReplyForm(Request $request, NodeInterface $node, $pid = NULL) {
+    $uri = $node->uri();
+    $build = array();
+    $account = $this->currentUser();
+
+    $build['#title'] = $this->t('Add new comment');
+
+    // Check if the user has the proper permissions.
+    if (!$account->hasPermission('post comments')) {
+      drupal_set_message($this->t('You are not authorized to post comments.'), 'error');
+      return new RedirectResponse($this->urlGenerator()->generateFromPath($uri['path'], array('absolute' => TRUE)));
+    }
+
+    // The user is not just previewing a comment.
+    if ($request->request->get('op') != $this->t('Preview')) {
+      if ($node->comment->value != COMMENT_NODE_OPEN) {
+        drupal_set_message($this->t("This discussion is closed: you can't post new comments."), 'error');
+        return new RedirectResponse($this->urlGenerator()->generateFromPath($uri['path'], array('absolute' => TRUE)));
+      }
+
+      // $pid indicates that this is a reply to a comment.
+      if ($pid) {
+        // Check if the user has the proper permissions.
+        if (!$account->hasPermission('access comments')) {
+          drupal_set_message($this->t('You are not authorized to view comments.'), 'error');
+          return new RedirectResponse($this->urlGenerator()->generateFromPath($uri['path'], array('absolute' => TRUE)));
+        }
+        // Load the parent comment.
+        $comment = $this->entityManager()->getStorageController('comment')->load($pid);
+        // Check if the parent comment is published and belongs to the current nid.
+        if (($comment->status->value == COMMENT_NOT_PUBLISHED) || ($comment->nid->target_id != $node->id())) {
+          drupal_set_message($this->t('The comment you are replying to does not exist.'), 'error');
+          return new RedirectResponse($this->urlGenerator()->generateFromPath($uri['path'], array('absolute' => TRUE)));
+        }
+        // Display the parent comment.
+        $build['comment_parent'] = $this->entityManager()->getRenderController('comment')->view($comment);
+      }
+
+      // The comment is in response to a node.
+      elseif ($account->hasPermission('access content')) {
+        // Display the node.
+        $build['comment_node'] = $this->entityManager()->getRenderController('node')->view($node);
+      }
+    }
+    else {
+      $build['#title'] = $this->t('Preview comment');
+    }
+
+    // Show the actual reply box.
+    $comment = $this->entityManager()->getStorageController('comment')->create(array(
+      'nid' => $node->id(),
+      'pid' => $pid,
+      'node_type' => 'comment_node_' . $node->getType(),
+    ));
+    $build['comment_form'] = $this->entityManager()->getForm($comment);
+
+    return $build;
+  }
+
 }