diff --git a/core/core.services.yml b/core/core.services.yml
index 56142b9..9fafd95 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -421,6 +421,8 @@ services:
     class: Drupal\system\Plugin\ImageToolkitInterface
     factory_method: getDefaultToolkit
     factory_service: image.toolkit.manager
+  breadcrumb:
+    class: Drupal\Core\Breadcrumb\BreadcrumbManager
   token:
     class: Drupal\Core\Utility\Token
     arguments: ['@module_handler']
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 4f72021..2c2bb87 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -283,6 +283,11 @@ function drupal_get_profile() {
  * @param $breadcrumb
  *   Array of links, starting with "home" and proceeding up to but not including
  *   the current page.
+ *
+ * @deprecated This will be removed in 8.0.  Instead, register a new breadcrumb
+ *   builder service.
+ *
+ * @see Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface
  */
 function drupal_set_breadcrumb($breadcrumb = NULL) {
   $stored_breadcrumb = &drupal_static(__FUNCTION__);
@@ -295,6 +300,11 @@ function drupal_set_breadcrumb($breadcrumb = NULL) {
 
 /**
  * Gets the breadcrumb trail for the current page.
+ *
+ * @deprecated This will be removed in 8.0.  Instead, register a new breadcrumb
+ *   builder service.
+ *
+ * @see Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface
  */
 function drupal_get_breadcrumb() {
   $breadcrumb = drupal_set_breadcrumb();
diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
new file mode 100644
index 0000000..8dbd415
--- /dev/null
+++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface.
+ */
+
+namespace Drupal\Core\Breadcrumb;
+
+/**
+ * Defines an interface for classes that build breadcrumbs.
+ */
+interface BreadcrumbBuilderInterface {
+
+  /**
+   * Build the breadcrumb.
+   *
+   * @param array $attributes
+   *   Attributes representing the current page.
+   *
+   * @return array|FALSE|NULL
+   *   A render array for the breadcrumbs, or
+   *   FALSE, to suppress breadcrumbs on this page, or
+   *   NULL, to let other builders decide.
+   */
+  public function build(array $attributes);
+
+}
diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
new file mode 100644
index 0000000..87534b5
--- /dev/null
+++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Breadcrumb\BreadcrumbManager.
+ */
+
+namespace Drupal\Core\Breadcrumb;
+
+/**
+ * Breadcrumb manager.
+ *
+ * Holds an array of path processor objects and uses them to sequentially process
+ * a path, in order of processor priority.
+ */
+class BreadcrumbManager implements BreadcrumbBuilderInterface {
+
+  /**
+   * Holds the array of breadcrumb builders, sorted by priority.
+   *
+   * @var array
+   *   An array of breadcrumb builders sorted by priorities.
+   */
+  protected $builders = array();
+
+  /**
+   * Holds the array of breadcrumb builders sorted by priority.
+   *
+   * @var array|NULL
+   *   An array of breadcrumb builders sorted by priorities, or
+   *   NULL, if the array needs to be re-calculated.
+   */
+  protected $sortedBuilders;
+
+  /**
+   * Adds another breadcrumb builder.
+   *
+   * @param \Drupal\Core\Breadcrumb\BreadcrumbBuilder $builder
+   *   The breadcrumb builder to add.
+   * @param int $priority
+   *   Priority of the breadcrumb builder.
+   */
+  public function addBuilder(BreadcrumbBuilderInterface $builder, $priority) {
+    $this->builders[$priority][] = $builder;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $attributes) {
+    // Call the build method of registered breadcrumb builders,
+    // until one of them returns something other than NULL.
+    foreach ($this->getSortedBuilders() as $builder) {
+      $breadcrumb = $builder->build($attributes);
+      if (!isset($breadcrumb)) {
+        // The builder returned NULL, so we continue with the other builders.
+        continue;
+      }
+      elseif (is_array($breadcrumb)) {
+        // The builder returned an array of breadcrumb links.
+        return $breadcrumb;
+      }
+      elseif (FALSE === $breadcrumb) {
+        // A value of FALSE indicates that no breadcrumb should be displayed.
+        // BreadcrumbBuilderInterface dictates to return array() in this case.
+        return array();
+      }
+      else {
+        $class = get_class($builder);
+        // TODO: Dedicated exception class.
+        throw new \Exception("Invalid breadcrumb returned by $class::build().");
+      }
+    }
+
+    // Fall back to an empty breadcrumb.
+    return array();
+  }
+
+  /**
+   * Returns the sorted array of breadcrumb builders.
+   *
+   * @return array
+   *   An array of breadcrumb builder objects.
+   */
+  protected function getSortedBuilders() {
+    if (!isset($this->sortedBuilders)) {
+      // Sort the builders according to priority.
+      krsort($this->builders);
+      // Merge the nested $this->builders array into $this->sortedBuilders.
+      $this->sortedBuilders = array();
+      foreach ($this->builders as $builders) {
+        $this->sortedBuilders = array_merge($this->sortedBuilders, $builders);
+      }
+    }
+    return $this->sortedBuilders;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index d29da83..5d5ccb2 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -16,6 +16,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\DependencyInjection\Reference;
@@ -60,6 +61,9 @@ public function build(ContainerBuilder $container) {
     // Add the compiler pass that will process the tagged services.
     $container->addCompilerPass(new RegisterPathProcessorsPass());
     $container->addCompilerPass(new ListCacheBinsPass());
+    // Add the compiler pass that will process the tagged breadcrumb builder
+    // services.
+    $container->addCompilerPass(new RegisterBreadcrumbBuilderPass());
   }
 
   /**
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterBreadcrumbBuilderPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterBreadcrumbBuilderPass.php
new file mode 100644
index 0000000..2e26ed6
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterBreadcrumbBuilderPass.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass.
+ */
+
+namespace Drupal\Core\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\Reference;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+
+/**
+ * Adds services to the breadcrumb_builder service.
+ */
+class RegisterBreadcrumbBuilderPass implements CompilerPassInterface {
+
+  public function process(ContainerBuilder $container) {
+    if (!$container->hasDefinition('breadcrumb')) {
+      return;
+    }
+    $manager = $container->getDefinition('breadcrumb');
+    foreach ($container->findTaggedServiceIds('breadcrumb_builder') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addBuilder', array(new Reference($id), $priority));
+    }
+  }
+
+}
diff --git a/core/modules/forum/forum.services.yml b/core/modules/forum/forum.services.yml
new file mode 100644
index 0000000..19033e8
--- /dev/null
+++ b/core/modules/forum/forum.services.yml
@@ -0,0 +1,6 @@
+services:
+  forum.breadcrumb:
+    class: Drupal\forum\ForumBreadcrumbBuilder
+    arguments: ['@plugin.manager.entity', '@config.factory']
+    tags:
+      - { name: breadcrumb_builder, priority: 1001 }
diff --git a/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php b/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php
new file mode 100644
index 0000000..ee5267d
--- /dev/null
+++ b/core/modules/forum/lib/Drupal/forum/ForumBreadcrumbBuilder.php
@@ -0,0 +1,119 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\forum\ForumBreadcrumbBuilder.
+ */
+
+namespace Drupal\forum;
+
+use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Entity\EntityManager;
+
+/**
+ * Class to define the forum breadcrumb builder.
+ */
+class ForumBreadcrumbBuilder implements BreadcrumbBuilderInterface {
+
+  /**
+   * Configuration object for this builder.
+   *
+   * @var \Drupal\Core\Config\Config
+   */
+  protected $config;
+
+  /**
+   * Stores the Entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a new ForumBreadcrumbBuilder.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $configFactory
+   *   The configuration factory.
+   */
+  public function __construct(EntityManager $entity_manager, ConfigFactory $configFactory) {
+    $this->entityManager = $entity_manager;
+    $this->config = $configFactory->get('forum.settings');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $attributes) {
+
+    // @todo This only works for legacy routes. Once node/% and forum/% are
+    //   converted to the new router this code will need to be updated.
+    if (isset($attributes['drupal_menu_item'])) {
+      $item = $attributes['drupal_menu_item'];
+      switch ($item['path']) {
+
+        case 'node/%':
+          $node = $item['map'][1];
+          // Load the object in case of missing wildcard loaders.
+          $node = is_object($node) ? $node : node_load($node);
+          if (_forum_node_check_node_type($node)) {
+            $breadcrumb = $this->forumPostBreadcrumb($node);
+          }
+          break;
+
+        case 'forum/%':
+          $term = $item['map'][1];
+          // Load the object in case of missing wildcard loaders.
+          $term = is_object($term) ? $term : forum_forum_load($term);
+          $breadcrumb = $this->forumTermBreadcrumb($term);
+          break;
+      }
+    }
+
+    if (!empty($breadcrumb)) {
+      return $breadcrumb;
+    }
+  }
+
+  /**
+   * Builds the breadcrumb for a forum post page.
+   */
+  protected function forumPostBreadcrumb($node) {
+    $vocabularies = $this->entityManager->getStorageController('taxonomy_vocabulary')->load(array($this->config->get('vocabulary')));
+    $vocabulary = current($vocabularies);
+
+    $breadcrumb[] = l(t('Home'), NULL);
+    $breadcrumb[] = l($vocabulary->name, 'forum');
+    if ($parents = taxonomy_term_load_parents_all($node->forum_tid)) {
+      $parents = array_reverse($parents);
+      foreach ($parents as $parent) {
+        $breadcrumb[] = l($parent->label(), 'forum/' . $parent->tid);
+      }
+    }
+    return $breadcrumb;
+  }
+
+  /**
+   * Builds the breadcrumb for a forum term page.
+   */
+  protected function forumTermBreadcrumb($term) {
+    $vocabularies = $this->entityManager->getStorageController('taxonomy_vocabulary')->load(array($this->config->get('vocabulary')));
+    $vocabulary = current($vocabularies);
+
+    $breadcrumb[] = l(t('Home'), NULL);
+    if ($term->tid) {
+      // Parent of all forums is the vocabulary name.
+      $breadcrumb[] = l($vocabulary->label(), 'forum');
+    }
+    // Add all parent forums to breadcrumbs.
+    if ($term->parents) {
+      foreach (array_reverse($term->parents) as $parent) {
+        if ($parent->id() != $term->tid) {
+          $breadcrumb[] = l($parent->label(), 'forum/' . $parent->id());
+        }
+      }
+    }
+    return $breadcrumb;
+  }
+
+}
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php
new file mode 100644
index 0000000..a56856a
--- /dev/null
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkBreadcrumbBuilder.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link\MenuLinkBreadcrumbBuilder.
+ */
+
+namespace Drupal\menu_link;
+
+use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+
+/**
+ * Class to define the menu_link breadcrumb builder.
+ */
+class MenuLinkBreadcrumbBuilder implements BreadcrumbBuilderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $attributes) {
+    // @todo Rewrite the implementation.
+    $breadcrumb = menu_get_active_breadcrumb();
+    if (is_array($breadcrumb)) {
+      // $breadcrumb is expected to be an array of rendered breadcrumb links.
+      return $breadcrumb;
+    }
+    elseif (FALSE === $breadcrumb) {
+      // A value of FALSE indicates that no breadcrumb should be displayed.
+      // BreadcrumbBuilderInterface dictates to return array() in this case.
+      return array();
+    }
+  }
+
+}
diff --git a/core/modules/menu_link/menu_link.services.yml b/core/modules/menu_link/menu_link.services.yml
new file mode 100644
index 0000000..a428476
--- /dev/null
+++ b/core/modules/menu_link/menu_link.services.yml
@@ -0,0 +1,5 @@
+services:
+  menu_link.breadcrumb:
+    class: Drupal\menu_link\MenuLinkBreadcrumbBuilder
+    tags:
+      - { name: breadcrumb_builder, priority: 0 }
diff --git a/core/modules/system/lib/Drupal/system/LegacyBreadcrumbBuilder.php b/core/modules/system/lib/Drupal/system/LegacyBreadcrumbBuilder.php
new file mode 100644
index 0000000..3da7e81
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/LegacyBreadcrumbBuilder.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\LegacyBreadcrumbBuilder.
+ */
+
+namespace Drupal\system;
+
+use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+
+/**
+ * Class to define the legacy breadcrumb builder.
+ *
+ * @deprecated This will be removed in 8.0.  Instead, register a new breadcrumb
+ *   builder service.
+ *
+ * @see Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface
+ *
+ * This breadcrumb builder implements legacy support for the
+ * drupal_set_breadcrumb() mechanic.
+ * Remove this once drupal_set_breadcrumb() has been eliminated.
+ */
+class LegacyBreadcrumbBuilder implements BreadcrumbBuilderInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $attributes) {
+    $breadcrumb = drupal_set_breadcrumb();
+    if (is_array($breadcrumb)) {
+      // $breadcrumb is expected to be an array of rendered breadcrumb links.
+      return $breadcrumb;
+    }
+    elseif (FALSE === $breadcrumb) {
+      // A value of FALSE indicates that no breadcrumb should be displayed.
+      // BreadcrumbBuilderInterface dictates to return array() in this case.
+      return array();
+    }
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemBreadcrumbBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemBreadcrumbBlock.php
new file mode 100644
index 0000000..b678eb6
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemBreadcrumbBlock.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Plugin\Block\SystemBreadcrumbBlock.
+ */
+
+namespace Drupal\system\Plugin\Block;
+
+use Drupal\block\BlockBase;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Provides a block to display the breadcrumbs.
+ *
+ * @Plugin(
+ *   id = "system_breadcrumb_block",
+ *   admin_label = @Translation("Breadcrumbs"),
+ *   module = "system"
+ * )
+ */
+class SystemBreadcrumbBlock extends BlockBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function blockBuild() {
+    $breadcrumb_manager = \Drupal::service('breadcrumb');
+    $request = \Drupal::service('request');
+    $breadcrumb = $breadcrumb_manager->build($request->attributes->all());
+    if (!empty($breadcrumb)) {
+      // $breadcrumb is expected to be an array of rendered breadcrumb links.
+      return array(
+        '#theme' => 'breadcrumb',
+        '#breadcrumb' => $breadcrumb,
+      );
+    }
+  }
+
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTestBase.php
index eb7fbaf..cc8ca91 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuTestBase.php
@@ -34,13 +34,30 @@ protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, arr
     if (isset($goto)) {
       $this->drupalGet($goto);
     }
+    $this->assertBreadcrumbParts($trail);
+
+    // Additionally assert page title, if given.
+    if (isset($page_title)) {
+      $this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title)));
+    }
+
+    // Additionally assert active trail in a menu tree output, if given.
+    if ($tree) {
+      $this->assertMenuActiveTrail($tree, $last_active);
+    }
+  }
+
+  protected function assertBreadcrumbParts($trail) {
     // Compare paths with actual breadcrumb.
-    $parts = $this->getParts();
+    $parts = $this->getBreadcrumbParts();
     $pass = TRUE;
-    foreach ($trail as $path => $title) {
-      $url = url($path);
-      $part = array_shift($parts);
-      $pass = ($pass && $part['href'] === $url && $part['text'] === check_plain($title));
+    // There may be more than one breadcrumb on the page.
+    while (!empty($parts)) {
+      foreach ($trail as $path => $title) {
+        $url = url($path);
+        $part = array_shift($parts);
+        $pass = ($pass && $part['href'] === $url && $part['text'] === check_plain($title));
+      }
     }
     // No parts must be left, or an expected "Home" will always pass.
     $pass = ($pass && empty($parts));
@@ -49,14 +66,11 @@ protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, arr
       '%parts' => implode(' » ', $trail),
       '@path' => $this->getUrl(),
     )));
+  }
 
-    // Additionally assert page title, if given.
-    if (isset($page_title)) {
-      $this->assertTitle(strtr('@title | Drupal', array('@title' => $page_title)));
-    }
-
-    // Additionally assert active trail in a menu tree output, if given.
-    if ($tree) {
+  protected function assertMenuActiveTrail($tree, $last_active) {
+    // Indentation to reduce git diff.
+    if (TRUE) {
       end($tree);
       $active_link_path = key($tree);
       $active_link_title = array_pop($tree);
@@ -102,7 +116,7 @@ protected function assertBreadcrumb($goto, array $trail, $page_title = NULL, arr
   /**
    * Returns the breadcrumb contents of the current page in the internal browser.
    */
-  protected function getParts() {
+  protected function getBreadcrumbParts() {
     $parts = array();
     $elements = $this->xpath('//nav[@class="breadcrumb"]/ol/li/a');
     if (!empty($elements)) {
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index c049b0a..7758db9 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -6,3 +6,7 @@ services:
   plugin.manager.system.plugin_ui:
     class: Drupal\system\Plugin\Type\PluginUIManager
     arguments: ['@container.namespaces']
+  system.breadcrumb.legacy:
+    class: Drupal\system\LegacyBreadcrumbBuilder
+    tags:
+      - {name: breadcrumb_builder, priority: 500}
diff --git a/core/profiles/standard/config/block.block.bartik.breadcrumbs.yml b/core/profiles/standard/config/block.block.bartik.breadcrumbs.yml
new file mode 100644
index 0000000..268782a
--- /dev/null
+++ b/core/profiles/standard/config/block.block.bartik.breadcrumbs.yml
@@ -0,0 +1,22 @@
+id: bartik.breadcrumbs
+weight: '-5'
+status: '1'
+langcode: en
+region: content
+plugin: system_breadcrumb_block
+settings:
+  label: Breadcrumbs
+  module: system
+  label_display: '0'
+  cache: '-1'
+visibility:
+  path:
+    visibility: '0'
+    pages: ''
+  role:
+    roles: {  }
+  node_type:
+    types:
+      article: '0'
+      page: '0'
+  visibility__active_tab: edit-visibility-path
diff --git a/core/profiles/standard/config/block.block.seven.breadcrumbs.yml b/core/profiles/standard/config/block.block.seven.breadcrumbs.yml
new file mode 100644
index 0000000..2520d77
--- /dev/null
+++ b/core/profiles/standard/config/block.block.seven.breadcrumbs.yml
@@ -0,0 +1,22 @@
+id: seven.breadcrumbs
+weight: '-2'
+status: '1'
+langcode: en
+region: content
+plugin: system_breadcrumb_block
+settings:
+  label: Breadcrumbs
+  module: system
+  label_display: '0'
+  cache: '-1'
+visibility:
+  path:
+    visibility: '0'
+    pages: ''
+  role:
+    roles: {  }
+  node_type:
+    types:
+      article: '0'
+      page: '0'
+  visibility__active_tab: edit-visibility-path
