diff --git a/core/core.services.yml b/core/core.services.yml
index 23ce7bf..a236f47 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -175,6 +175,9 @@ services:
   plugin.manager.menu.local_task:
     class: Drupal\Core\Menu\LocalTaskManager
     arguments: ['@container.namespaces', '@controller_resolver', '@request', '@router.route_provider', '@module_handler']
+  menu_link.access:
+    class: Drupal\Core\Menu\MenuLinkAccess
+    arguments: ['@access_manager', '@router.dynamic']
   request:
     class: Symfony\Component\HttpFoundation\Request
     # @TODO the synthetic setting must be uncommented whenever drupal_session_initialize()
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index d205ac0..5000567 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -895,7 +895,6 @@ function _menu_link_translate(&$item, $translate = FALSE) {
       // current path, and we can take over the argument map for a link like
       // 'foo/%/bar'. This inheritance is only valid for breadcrumb links.
       // @see _menu_tree_check_access()
-      // @see menu_get_active_breadcrumb()
       elseif ($translate && ($current_router_item = menu_get_item())) {
         // If $translate is TRUE, then this link is in the active trail.
         // Only translate paths within the current path.
@@ -964,37 +963,13 @@ function _menu_link_translate(&$item, $translate = FALSE) {
  *   TRUE if the user has access or FALSE if the user should be presented
  *   with access denied.
  *
+ * @deprecated Use \Drupal::service('menu_link.access')->access() instead.
+ *
  * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException
  *   If the system path in $href does not match the $route.
  */
 function menu_item_route_access(Route $route, $href, &$map) {
-  $request = Request::create('/' . $href);
-  $request->attributes->set('_system_path', $href);
-  // Attempt to match this path to provide a fully built request to the
-  // access checker.
-  try {
-    $request->attributes->add(Drupal::service('router.dynamic')->matchRequest($request));
-  }
-  catch (NotFoundHttpException $e) {
-    return FALSE;
-  }
-
-  // Populate the map with any matching values from the request.
-  $path_bits = explode('/', trim($route->getPath(), '/'));
-  foreach ($map as $index => $map_item) {
-    $matches = array();
-    // Search for placeholders wrapped by curly braces. For example, a path
-    // 'foo/{bar}/baz' would return 'bar'.
-    if (isset($path_bits[$index]) && preg_match('/{(?<placeholder>.*)}/', $path_bits[$index], $matches)) {
-      // If that placeholder is present on the request attributes, replace the
-      // placeholder in the map with the value.
-      if ($request->attributes->has($matches['placeholder'])) {
-        $map[$index] = $request->attributes->get($matches['placeholder']);
-      }
-    }
-  }
-
-  return Drupal::service('access_manager')->check($route, $request);
+  return Drupal::service('menu_link.access')->access($route, $href, $map);
 }
 
 /**
@@ -2610,57 +2585,6 @@ function menu_get_active_trail() {
 }
 
 /**
- * Gets the breadcrumb for the current page, as determined by the active trail.
- *
- * @see menu_set_active_trail()
- */
-function menu_get_active_breadcrumb() {
-  $breadcrumb = array();
-
-  // No breadcrumb for the front page.
-  if (drupal_is_front_page()) {
-    return $breadcrumb;
-  }
-
-  $item = menu_get_item();
-  if (!empty($item['access'])) {
-    $active_trail = menu_get_active_trail();
-
-    // Allow modules to alter the breadcrumb, if possible, as that is much
-    // faster than rebuilding an entirely new active trail.
-    drupal_alter('menu_breadcrumb', $active_trail, $item);
-
-    // Don't show a link to the current page in the breadcrumb trail.
-    $end = end($active_trail);
-    if (Drupal::request()->attributes->get('_system_path') == $end['href']) {
-      array_pop($active_trail);
-    }
-
-    // Remove the tab root (parent) if the current path links to its parent.
-    // Normally, the tab root link is included in the breadcrumb, as soon as we
-    // are on a local task or any other child link. However, if we are on a
-    // default local task (e.g., node/%/view), then we do not want the tab root
-    // link (e.g., node/%) to appear, as it would be identical to the current
-    // page. Since this behavior also needs to work recursively (i.e., on
-    // default local tasks of default local tasks), and since the last non-task
-    // link in the trail is used as page title (see menu_get_active_title()),
-    // this condition cannot be cleanly integrated into menu_get_active_trail().
-    // menu_get_active_trail() already skips all links that link to their parent
-    // (commonly MENU_DEFAULT_LOCAL_TASK). In order to also hide the parent link
-    // itself, we always remove the last link in the trail, if the current
-    // router item links to its parent.
-    if (($item['type'] & MENU_LINKS_TO_PARENT) == MENU_LINKS_TO_PARENT) {
-      array_pop($active_trail);
-    }
-
-    foreach ($active_trail as $parent) {
-      $breadcrumb[] = l($parent['title'], $parent['href'], $parent['localized_options']);
-    }
-  }
-  return $breadcrumb;
-}
-
-/**
  * Gets the title of the current page, as determined by the active trail.
  */
 function menu_get_active_title() {
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkAccess.php b/core/lib/Drupal/Core/Menu/MenuLinkAccess.php
new file mode 100644
index 0000000..980da87
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuLinkAccess.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuLinkAccess.
+ */
+
+namespace Drupal\Core\Menu;
+
+use Drupal\Core\Access\AccessManager;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\Route;
+use Symfony\Cmf\Component\Routing\DynamicRouter;
+
+/**
+ * Provides access checking for menu links provided by routes.
+ */
+class MenuLinkAccess {
+
+  /**
+   * The access manager service.
+   *
+   * @var \Drupal\Core\Access\AccessManager
+   */
+  protected $accessManager;
+
+  /**
+   * The dynamic router service.
+   *
+   * @var \Symfony\Cmf\Component\Routing\DynamicRouter
+   */
+  protected $dynamicRouter;
+
+  /**
+   * Constructs a MenuLinkAccess object.
+   *
+   * @param \Drupal\Core\Access\AccessManager $access_manager
+   *   The access manager service.
+   * @param \Symfony\Cmf\Component\Routing\DynamicRouter $dynamic_router
+   */
+  public function __construct(AccessManager $access_manager, DynamicRouter $dynamic_router) {
+    $this->accessManager = $access_manager;
+    $this->dynamicRouter = $dynamic_router;
+  }
+
+  /**
+   * Checks access to a menu item by mocking a request for a path.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   Router for the given menu item.
+   * @param string $href
+   *   Menu path as returned by link_path of the menu item.
+   * @param array $map
+   *   An array of path arguments; for example, array('node', '5').
+   *
+   * @return bool
+   *   TRUE if the user has access or FALSE if the user should be presented
+   *   with access denied.
+   *
+   * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException
+   *   If the system path in $href does not match the $route.
+   */
+  public function access(Route $route, $href, &$map) {
+    $request = Request::create('/' . $href);
+    $request->attributes->set('_system_path', $href);
+    // Attempt to match this path to provide a fully built request to the
+    // access checker.
+    try {
+      $request->attributes->add($this->dynamicRouter->matchRequest($request));
+    }
+    catch (NotFoundHttpException $e) {
+      return FALSE;
+    }
+
+    // Populate the map with any matching values from the request.
+    $path_bits = explode('/', trim($route->getPath(), '/'));
+    foreach ($map as $index => $map_item) {
+      $matches = array();
+      // Search for placeholders wrapped by curly braces. For example, a path
+      // 'foo/{bar}/baz' would return 'bar'.
+      if (isset($path_bits[$index]) && preg_match('/{(?<placeholder>.*)}/', $path_bits[$index], $matches)) {
+        // If that placeholder is present on the request attributes, replace the
+        // placeholder in the map with the value.
+        if ($request->attributes->has($matches['placeholder'])) {
+          $map[$index] = $request->attributes->get($matches['placeholder']);
+        }
+      }
+    }
+
+    return $this->accessManager->check($route, $request);
+  }
+
+}
diff --git a/core/modules/book/book.services.yml b/core/modules/book/book.services.yml
index 9d8c140..71de5a0 100644
--- a/core/modules/book/book.services.yml
+++ b/core/modules/book/book.services.yml
@@ -1,4 +1,9 @@
 services:
+  book.breadcrumb:
+    class: Drupal\book\BookBreadcrumbBuilder
+    arguments: ['@router.route_provider', '@plugin.manager.entity', '@menu_link.access', '@string_translation']
+    tags:
+      - { name: breadcrumb_builder, priority: 701 }
   book.manager:
     class: Drupal\book\BookManager
     arguments: ['@database', '@plugin.manager.entity']
diff --git a/core/modules/book/lib/Drupal/book/BookBreadcrumbBuilder.php b/core/modules/book/lib/Drupal/book/BookBreadcrumbBuilder.php
new file mode 100644
index 0000000..277263b
--- /dev/null
+++ b/core/modules/book/lib/Drupal/book/BookBreadcrumbBuilder.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\book\BookBreadcrumbBuilder.
+ */
+
+namespace Drupal\book;
+
+use Drupal\menu_link\MenuLinkBreadcrumbBuilder;
+
+/**
+ * Provides a breadcrumb builder for blocks.
+ */
+class BookBreadcrumbBuilder extends MenuLinkBreadcrumbBuilder {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $attributes) {
+    if (!empty($attributes['_system_path']) && isset($attributes['_drupal_menu_item']['map'][1]->book)) {
+      $mlids = array();
+      // @todo Replace with link generator service when
+      //   https://drupal.org/node/2047619 lands.
+      $links = array(l($this->translation->translate('Home'), '<front>'));
+      $book = $attributes['_drupal_menu_item']['map'][1]->book;
+      $depth = 1;
+      while (!empty($book['p' . $depth])) {
+        $mlids[] = $book['p' . $depth];
+        $depth++;
+      }
+      $menu_links = $this->menuLinkStorage->loadMultiple($mlids);
+      if (count($menu_links) > 0) {
+        $depth = 1;
+        while (!empty($book['p' . $depth])) {
+          if (!empty($menu_links[$book['p' . $depth]]) && ($menu_link = $menu_links[$book['p' . $depth]])) {
+            if (!empty($menu_link->route_name)) {
+              try {
+                if ($route = $this->routeProvider->getRouteByName($menu_link->route_name)) {
+                  try {
+                    if ($this->menuLinkAccess->access($route, $menu_link->link_path, $path_elements)) {
+                      // @todo Replace with link generator service when
+                      //   https://drupal.org/node/2047619 lands.
+                      $links[] = l($menu_link->label(), $menu_link->link_path, $menu_link->options);
+                    }
+                  }
+                  catch (NotFoundHttpException $e) {
+                    continue;
+                  }
+                }
+              }
+              catch (RouteNotFoundException $e) {
+                continue;
+              }
+            }
+            else {
+              // Legacy hook_menu page callback.
+              // @todo Remove this once all routes are converted.
+              if ($item = menu_get_item($menu_link->link_path)) {
+                if ($item['access']) {
+                  $links[] = l($item['title'], $menu_link->link_path, $item['options']);
+                }
+              }
+            }
+          }
+          $depth++;
+        }
+      }
+      return $links;
+    }
+  }
+
+}
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php
index acece08..e0c791c 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php
@@ -8,6 +8,13 @@
 namespace Drupal\menu_link;
 
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Routing\RouteProviderInterface;
+use Drupal\Core\Routing\RouteCompiler;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Menu\MenuLinkAccess;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
 
 /**
  * Class to define the menu_link breadcrumb builder.
@@ -15,12 +22,141 @@
 class MenuLinkBreadcrumbBuilder implements BreadcrumbBuilderInterface {
 
   /**
+   * The route provider service.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
+  /**
+   * The menu link storage controller.
+   *
+   * @var \Drupal\menu_link\MenuLinkStorageControllerInterface
+   */
+  protected $menuLinkStorage;
+
+  /**
+   * The menu link access service.
+   *
+   * @var \Drupal\menu_link\MenuLinkAccess
+   */
+  protected $menuLinkAccess;
+
+  /**
+   * The translation manager service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface;
+   */
+  protected $translation;
+
+  /**
+   * The menu storage controller.
+   *
+   * @var Drupal\Core\Config\Entity\ConfigStorageController
+   */
+  protected $menuStorage;
+
+  /**
+   * Constructs the MenuLinkBreadcrumbBuilder.
+   *
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider service.
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager service.
+   * @param \Drupal\menu_link\MenuLinkAccess $menu_link_access
+   *   The menu link access service.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
+   *   The translation manager service.
+   */
+  public function __construct(RouteProviderInterface $route_provider, EntityManager $entity_manager, MenuLinkAccess $menu_link_access, TranslationInterface $translation) {
+    $this->routeProvider = $route_provider;
+    $this->menuLinkStorage = $entity_manager->getStorageController('menu_link');
+    $this->menuLinkAccess = $menu_link_access;
+    $this->translation = $translation;
+    $this->menuStorage = $entity_manager->getStorageController('menu');
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function build(array $attributes) {
-    // @todo Rewrite the implementation.
-    // Currently the result always array.
-    return menu_get_active_breadcrumb();
+    $links = array();
+
+    // Custom breadcrumb behaviour for editing menu links, we append a link to
+    // the menu in which the link is found.
+    if (!empty($attributes['_route']) && $attributes['_route'] == 'menu_link_edit' && !empty($attributes['menu_link'])) {
+      $menu_link = $attributes['menu_link'];
+      if (!$menu_link->isNew()) {
+        // Add a link to the menu admin screen.
+        $menu = $this->menuStorage->load($menu_link->menu_name);
+        $links[] = l($menu->label(), 'admin/structure/menu/manage/' . $menu_link->menu_name);
+      }
+    }
+
+    // General path-based breadcrumbs.
+    if (!empty($attributes['_system_path'])) {
+      $path_elements = explode('/', $attributes['_system_path']);
+      while (count($path_elements) > 0) {
+        array_pop($path_elements);
+        $pattern = implode('/', $path_elements);
+        $menu_links = $this->menuLinkStorage->loadByProperties(array(
+          'link_path' => $pattern,
+        ));
+        if (empty($menu_links)) {
+          // No matching menu link with identical pattern, but could be a case
+          // of placeholders.
+          try {
+            $routes = $this->routeProvider->getRoutesByPattern('/' . $pattern)->all();
+            if (!empty($routes)) {
+              $route = reset($routes);
+              $parameterized_path = RouteCompiler::getPatternOutline($route->getPath());
+              $menu_links = $this->menuLinkStorage->loadByProperties(array(
+                // Strip the leading '/'.
+                'link_path' => substr($parameterized_path, 1),
+              ));
+            }
+          }
+          catch (NotFoundHttpException $e) {}
+        }
+        if (count($menu_links) > 0) {
+          $menu_link = reset($menu_links);
+          if (!empty($menu_link->route_name)) {
+            try {
+              if ($route = $this->routeProvider->getRouteByName($menu_link->route_name)) {
+                try {
+                  if ($this->menuLinkAccess->access($route, $pattern, $path_elements)) {
+                    // @todo Move _menu_item_localize() to a service.
+                    _menu_item_localize($menu_link, $path_elements, FALSE);
+                    // @todo Replace with link generator service when
+                    //   https://drupal.org/node/2047619 lands.
+                    $links[] = l($menu_link->title, $pattern, $menu_link->options);
+                  }
+                }
+                catch (NotFoundHttpException $e) {
+                  continue;
+                }
+              }
+            }
+            catch (RouteNotFoundException $e) {
+              continue;
+            }
+          }
+        }
+        else {
+          // Legacy hook_menu page callback.
+          // @todo Remove this once all routes are converted.
+          if ($item = menu_get_item($pattern)) {
+            if ($item['access']) {
+              $links[] = l($item['title'], $pattern, $item['options']);
+            }
+          }
+        }
+      }
+    }
+    // @todo Replace with link generator service when
+    //   https://drupal.org/node/2047619 lands.
+    $links[] = l($this->translation->translate('Home'), '<front>');
+    return array_reverse($links);
   }
 
 }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
index 94ad467..740c43a 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkFormController.php
@@ -76,17 +76,6 @@ public function form(array $form, array &$form_state) {
     // item, do it here instead.
     _menu_link_translate($menu_link);
 
-    if (!$menu_link->isNew()) {
-      // Get the human-readable menu title from the given menu name.
-      $titles = menu_get_menus();
-      $current_title = $titles[$menu_link->menu_name];
-
-      // Get the current breadcrumb and add a link to that menu's overview page.
-      $breadcrumb = menu_get_active_breadcrumb();
-      $breadcrumb[] = l($current_title, 'admin/structure/menu/manage/' . $menu_link->menu_name);
-      drupal_set_breadcrumb($breadcrumb);
-    }
-
     $form['link_title'] = array(
       '#type' => 'textfield',
       '#title' => t('Menu link title'),
diff --git a/core/modules/menu_link/menu_link.services.yml b/core/modules/menu_link/menu_link.services.yml
index a428476..f3c48f8 100644
--- a/core/modules/menu_link/menu_link.services.yml
+++ b/core/modules/menu_link/menu_link.services.yml
@@ -1,5 +1,6 @@
 services:
   menu_link.breadcrumb:
     class: Drupal\menu_link\MenuLinkBreadcrumbBuilder
+    arguments: ['@router.route_provider', '@plugin.manager.entity', '@menu_link.access', '@string_translation']
     tags:
       - { name: breadcrumb_builder, priority: 0 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php
index fbd3e34..4da001e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php
@@ -83,23 +83,9 @@ function testMenuTreeSetPath() {
       'admin/config/development/menu-trail' => t('Menu trail - Case 2'),
     );
 
-    $override_breadcrumb = $config + array(
-      'admin/config/system' => t('System'),
-      'admin/config/system/site-information' => t('Site information'),
-    );
-    $override_tree = $config_tree + array(
-      'admin/config/system' => t('System'),
-      'admin/config/system/site-information' => t('Site information'),
-    );
-
     // Test the tree generation for the Administration menu.
     \Drupal::state()->delete('menu_test.menu_tree_set_path');
     $this->assertBreadcrumb('admin/config/development/menu-trail', $breadcrumb, t('Menu trail - Case 2'), $tree);
-
-    // Override the active trail for the Administration tree; it should affect
-    // the breadcrumbs and Administration tree.
-    \Drupal::state()->set('menu_test.menu_tree_set_path', $test_menu_path);
-    $this->assertBreadcrumb('admin/config/development/menu-trail', $override_breadcrumb, t('Menu trail - Case 2'), $override_tree);
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeAccessTest.php
index 263e2fc..df10877 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeAccessTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeAccessTest.php
@@ -33,6 +33,13 @@ class TreeAccessTest extends DrupalUnitTestBase {
    */
   protected $routeCollection;
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('menu_link');
+
   public static function getInfo() {
     return array(
       'name' => 'Menu tree access',
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 6339d1c..4d7aae0 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -940,53 +940,6 @@ function hook_local_task_alter(&$local_tasks) {
 }
 
 /**
- * Alter links in the active trail before it is rendered as the breadcrumb.
- *
- * This hook is invoked by menu_get_active_breadcrumb() and allows alteration
- * of the breadcrumb links for the current page, which may be preferred instead
- * of setting a custom breadcrumb via drupal_set_breadcrumb().
- *
- * Implementations should take into account that menu_get_active_breadcrumb()
- * subsequently performs the following adjustments to the active trail *after*
- * this hook has been invoked:
- * - The last link in $active_trail is removed, if its 'href' is identical to
- *   the 'href' of $item. This happens, because the breadcrumb normally does
- *   not contain a link to the current page.
- * - The (second to) last link in $active_trail is removed, if the current $item
- *   is a MENU_DEFAULT_LOCAL_TASK. This happens in order to do not show a link
- *   to the current page, when being on the path for the default local task;
- *   e.g. when being on the path node/%/view, the breadcrumb should not contain
- *   a link to node/%.
- *
- * Each link in the active trail must contain:
- * - title: The localized title of the link.
- * - href: The system path to link to.
- * - localized_options: An array of options to pass to url().
- *
- * @param $active_trail
- *   An array containing breadcrumb links for the current page.
- * @param $item
- *   The menu router item of the current page.
- *
- * @see drupal_set_breadcrumb()
- * @see menu_get_active_breadcrumb()
- * @see menu_get_active_trail()
- * @see menu_set_active_trail()
- */
-function hook_menu_breadcrumb_alter(&$active_trail, $item) {
-  // Always display a link to the current page by duplicating the last link in
-  // the active trail. This means that menu_get_active_breadcrumb() will remove
-  // the last link (for the current page), but since it is added once more here,
-  // it will appear.
-  if (!drupal_is_front_page()) {
-    $end = end($active_trail);
-    if ($item['href'] == $end['href']) {
-      $active_trail[] = $end;
-    }
-  }
-}
-
-/**
  * Alter contextual links before they are rendered.
  *
  * This hook is invoked by menu_contextual_links(). The system-determined
