diff --git a/core/core.services.yml b/core/core.services.yml
index 238a169..b8e2e8b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -275,6 +275,18 @@ services:
   plugin.manager.menu.link:
     class: Drupal\Core\Menu\MenuLinkManager
     arguments: ['@menu.tree_storage', '@menu_link.static.overrides', '@module_handler']
+  menu.link_tree:
+    class: Drupal\Core\Menu\MenuLinkTree
+    arguments: ['@menu.tree_storage', '@plugin.manager.menu.link', '@router.route_provider', '@menu.active_trail', '@controller_resolver']
+  menu.default_tree_manipulators:
+    class: Drupal\Core\Menu\DefaultMenuLinkTreeManipulators
+    arguments: ['@access_manager', '@current_user']
+  menu.active_trail:
+    class: Drupal\Core\Menu\MenuActiveTrail
+    arguments: ['@plugin.manager.menu.link', '@current_route_match']
+  menu.parent_form_selector:
+    class: Drupal\Core\Menu\MenuParentFormSelector
+    arguments: ['@menu.link_tree', '@entity.manager']
   plugin.manager.menu.local_action:
     class: Drupal\Core\Menu\LocalActionManager
     arguments: ['@controller_resolver', '@request_stack', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user']
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 1675b8c..153a7de 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -445,16 +445,15 @@ function _batch_finished() {
       }
     }
 
-    // Use \Drupal\Core\Form\FormSubmitterInterface::redirectForm() to handle
-    // the redirection logic.
-    $redirect = \Drupal::service('form_submitter')->redirectForm($_batch['form_state']);
+    // Use drupal_redirect_form() to handle the redirection logic.
+    $redirect = drupal_redirect_form($_batch['form_state']);
     if (is_object($redirect)) {
       return $redirect;
     }
 
     // If no redirection happened, redirect to the originating page. In case the
     // form needs to be rebuilt, save the final $form_state for
-    // \Drupal\Core\Form\FormBuilderInterface::buildForm().
+    // drupal_build_form().
     if (!empty($_batch['form_state']['rebuild'])) {
       $_SESSION['batch_form_state'] = $_batch['form_state'];
     }
diff --git a/core/lib/Drupal/Core/Form/FormBuilderInterface.php b/core/lib/Drupal/Core/Form/FormBuilderInterface.php
index 463a7a2..bf00f50 100644
--- a/core/lib/Drupal/Core/Form/FormBuilderInterface.php
+++ b/core/lib/Drupal/Core/Form/FormBuilderInterface.php
@@ -48,7 +48,7 @@ public function getFormId($form_arg, &$form_state);
    * @return array
    *   The form array.
    *
-   * @see \Drupal\Core\Form\FormBuilderInterface::buildForm()
+   * @see drupal_build_form()
    */
   public function getForm($form_arg);
 
diff --git a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
new file mode 100644
index 0000000..e58830e
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
@@ -0,0 +1,175 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators.
+ */
+
+namespace Drupal\Core\Menu;
+
+use Drupal\Core\Access\AccessManager;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides a couple of menu link tree manipulators.
+ */
+class DefaultMenuLinkTreeManipulators {
+
+  /**
+   * The access manager.
+   *
+   * @var \Drupal\Core\Access\AccessManager
+   */
+  protected $accessManager;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * Constructs a \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators object.
+   *
+   * @param \Drupal\Core\Access\AccessManager $access_manager
+   *   The access manager.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user.
+   */
+  public function __construct(AccessManager $access_manager, AccountInterface $account) {
+    $this->accessManager = $access_manager;
+    $this->account = $account;
+  }
+
+  /**
+   * Performs access checks of a menu tree.
+   *
+   * Removes menu links from the given menu tree whose links are inaccessible
+   * for the current user, sets the 'access' property to TRUE on tree elements
+   * that are accessible for the current user.
+   *
+   * Makes the resulting menu tree impossible to render cache, unless render
+   * caching per user is acceptable.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu link tree to manipulate.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   The manipulated menu link tree.
+   */
+  public function checkAccess(array $tree) {
+    foreach ($tree as $key => $element) {
+      // Other menu tree manipulators may already have calculated access, do not
+      // overwrite the existing value in that case.
+      if (!isset($element->access)) {
+        $tree[$key]->access = $this->menuLinkCheckAccess($element->link);
+      }
+      if ($tree[$key]->access) {
+        if ($tree[$key]->subtree) {
+          $tree[$key]->subtree = $this->checkAccess($tree[$key]->subtree);
+        }
+      }
+      else {
+        unset($tree[$key]);
+      }
+    }
+    return $tree;
+  }
+
+  /**
+   * Checks access for one menu link instance.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkInterface $instance
+   *   The menu link instance.
+   *
+   * @return bool
+   *   TRUE if the current user can access the link, FALSE otherwise.
+   */
+  protected function menuLinkCheckAccess(MenuLinkInterface $instance) {
+    // Use the definition here since that's a lot faster than creating a Url
+    // object that we don't need.
+    $definition = $instance->getPluginDefinition();
+    // 'url' should only be populated for external links.
+    if (!empty($definition['url']) && empty($definition['route_name'])) {
+      $access = TRUE;
+    }
+    else {
+      $access = $this->accessManager->checkNamedRoute($definition['route_name'], $definition['route_parameters'], $this->account);
+    }
+    return $access;
+  }
+
+  /**
+   * Generates a unique index and sorts by it.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu link tree to manipulate.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   The manipulated menu link tree.
+   */
+  public function generateIndexAndSort(array $tree) {
+    $new_tree = array();
+    foreach ($tree as $key => $v) {
+      if ($tree[$key]->subtree) {
+        $tree[$key]->subtree = $this->generateIndexAndSort($tree[$key]->subtree);
+      }
+      $instance = $tree[$key]->link;
+      // The weights are made a uniform 5 digits by adding 50000 as an offset.
+      // After $this->menuLinkCheckAccess(), $instance->getTitle() has the
+      // localized or translated title. Adding the plugin id to the end of the
+      // index insures that it is unique.
+      $new_tree[(50000 + $instance->getWeight()) . ' ' . $instance->getTitle() . ' ' . $instance->getPluginId()] = $tree[$key];
+    }
+    ksort($new_tree);
+    return $new_tree;
+  }
+
+  /**
+   * Flattens the tree to a single level.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu link tree to manipulate.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   The manipulated menu link tree.
+   */
+  public function flatten(array $tree) {
+    foreach ($tree as $key => $element) {
+      if ($tree[$key]->subtree) {
+        $tree += $this->flatten($tree[$key]->subtree);
+      }
+      $tree[$key]->subtree = array();
+    }
+    return $tree;
+  }
+
+  /**
+   * Extracts a subtree of the active trail.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu link tree to manipulate.
+   * @param int $level
+   *   The level in the active trail to extract.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   The manipulated menu link tree.
+   */
+  public function extractSubtreeOfActiveTrail(array $tree, $level) {
+    // Go down the active trail until the right level is reached.
+    while ($level-- > 0 && $tree) {
+      // Loop through the current level's elements  until we find one that is in
+      // the active trail.
+      while ($element = array_shift($tree)) {
+        if ($element->inActiveTrail) {
+          // If the element is in the active trail, we continue in the subtree.
+          $tree = $element->subtree;
+          break;
+        }
+      }
+    }
+    return $tree;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuActiveTrail.php b/core/lib/Drupal/Core/Menu/MenuActiveTrail.php
new file mode 100644
index 0000000..4732c5c
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuActiveTrail.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuActiveTrail.
+ */
+
+namespace Drupal\Core\Menu;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Provides the default implementation of the active menu trail service.
+ *
+ * It uses the current route name and route parameters to compare with the ones
+ * of the menu links.
+ */
+class MenuActiveTrail implements MenuActiveTrailInterface {
+
+  /**
+   * The menu link plugin manager.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkManagerInterface
+   */
+  protected $menuLinkManager;
+
+  /**
+   * The route match object for the current page.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * Constructs a \Drupal\Core\Menu\MenuActiveTrail object.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
+   *   The menu link plugin manager.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   A route match object for finding the active link.
+   */
+  public function __construct(MenuLinkManagerInterface $menu_link_manager, RouteMatchInterface $route_match) {
+    $this->menuLinkManager = $menu_link_manager;
+    $this->routeMatch = $route_match;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getActiveTrailIds($menu_name) {
+    // Parent ids; used both as key and value to ensure uniqueness.
+    // We always want all the top-level links with parent == ''.
+    $active_trail = array('' => '');
+
+    // If a link in the given menu indeed matches the route, then use it to
+    // complete the active trail.
+    if ($active_link = $this->getActiveLink($menu_name)) {
+      if ($parents = $this->menuLinkManager->getParentIds($active_link->getPluginId())) {
+        $active_trail = $parents + $active_trail;
+      }
+    }
+
+    return $active_trail;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getActiveTrailCacheKey($menu_name) {
+    return 'menu_trail.' . implode('|', $this->getActiveTrailIds($menu_name));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getActiveLink($menu_name = NULL) {
+    // Note: this is a very simple implementation. If you need more control
+    // over the return value, such as matching a prioritized list of menu names,
+    // you should substitute your own implementation for the 'menu.active_trail'
+    // service in the container.
+    // The menu links coming from the storage are already sorted by depth,
+    // weight and ID.
+    $found = NULL;
+
+    $route_name = $this->routeMatch->getRouteName();
+    // On a default (not custom) 403 page the route name is NULL. On a custom
+    // 403 page we will get the route name for that page, so we can consider
+    // it a feature that a relevant menu tree may be displayed.
+    if ($route_name) {
+      $route_parameters = $this->routeMatch->getRawParameters()->all();
+
+      // Load links matching this route.
+      $links = $this->menuLinkManager->loadLinksByRoute($route_name, $route_parameters, $menu_name);
+      // Select the first matching link.
+      if ($links) {
+        $found = reset($links);
+      }
+    }
+    return $found;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuActiveTrailInterface.php b/core/lib/Drupal/Core/Menu/MenuActiveTrailInterface.php
new file mode 100644
index 0000000..db485c4
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuActiveTrailInterface.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuActiveTrailInterface.
+ */
+
+namespace Drupal\Core\Menu;
+
+/**
+ * Defines an interface for the active menu trail service.
+ *
+ * The active trail of a given menu is the trail from the current page to the
+ * root of that menu's tree.
+ */
+interface MenuActiveTrailInterface {
+
+  /**
+   * Gets the active trail IDs of the specified menu tree.
+   *
+   * @param string $menu_name
+   *   The menu name of the requested tree.
+   *
+   * @return array
+   *   An array containing the active trail: a list of plugin IDs.
+   */
+  public function getActiveTrailIds($menu_name);
+
+  /**
+   * Gets the active trail cache key of the specified menu tree.
+   *
+   * @param string $menu_name
+   *   The menu name of the requested tree.
+   *
+   * @return string
+   *   The cache key that uniquely identifies the active trail of the menu tree.
+   */
+  public function getActiveTrailCacheKey($menu_name);
+
+  /**
+   * Fetches a menu link which matches the route name, parameters and menu name.
+   *
+   * @param string|NULL $menu_name
+   *   (optional) The menu within which to find the active link. If omitted, all
+   *   menus will be searched.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkInterface|NULL
+   *   The menu link for the given route name, parameters and menu, or NULL if
+   *   there is no matching menu link or the current user cannot access the
+   *   current page (i.e. we have a 403 response).
+   */
+  public function getActiveLink($menu_name = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkBase.php b/core/lib/Drupal/Core/Menu/MenuLinkBase.php
index 438d862..43cb49a 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkBase.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkBase.php
@@ -91,7 +91,7 @@ public function isExpanded() {
   /**
    * {@inheritdoc}
    */
-  public function isResetable() {
+  public function isResettable() {
     return FALSE;
   }
 
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkDefault.php b/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
index 0439937..cf14df7 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
@@ -66,7 +66,7 @@ public static function create(ContainerInterface $container, array $configuratio
   /**
    * {@inheritdoc}
    */
-  public function isResetable() {
+  public function isResettable() {
     // The link can be reset if it has an override.
     return (bool) $this->staticOverride->loadOverride($this->getPluginId());
   }
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkInterface.php b/core/lib/Drupal/Core/Menu/MenuLinkInterface.php
index 6ab1434..811dbf0 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkInterface.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkInterface.php
@@ -88,7 +88,7 @@ public function isExpanded();
    * @return bool
    *   TRUE if it can be reset, FALSE otherwise.
    */
-  public function isResetable();
+  public function isResettable();
 
   /**
    * Returns whether this link can be translated.
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkManager.php b/core/lib/Drupal/Core/Menu/MenuLinkManager.php
index ab1f5dd..5c6d718 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkManager.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkManager.php
@@ -255,7 +255,7 @@ public function deleteLinksInMenu($menu_name) {
       if ($instance->isDeletable()) {
         $this->deleteInstance($instance, TRUE);
       }
-      elseif ($instance->isResetable()) {
+      elseif ($instance->isResettable()) {
         $new_instance = $this->resetInstance($instance);
         $affected_menus[$new_instance->getMenuName()] = $new_instance->getMenuName();
       }
@@ -395,7 +395,7 @@ public function resetLink($id) {
   protected function resetInstance(MenuLinkInterface $instance) {
     $id = $instance->getPluginId();
 
-    if (!$instance->isResetable()) {
+    if (!$instance->isResettable()) {
       throw new PluginException(String::format('Menu link %id is not resettable', array('%id' => $id)));
     }
     // Get the original data from disk, reset the override and re-save the menu
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTree.php b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
new file mode 100644
index 0000000..8d09c2c
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
@@ -0,0 +1,228 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuLinkTree.
+ */
+
+namespace Drupal\Core\Menu;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Controller\ControllerResolverInterface;
+use Drupal\Core\Routing\RouteProviderInterface;
+
+/**
+ * Implements the loading, transforming and rendering of menu link trees.
+ */
+class MenuLinkTree implements MenuLinkTreeInterface {
+
+  /**
+   * The menu link tree storage.
+   *
+   * @var \Drupal\Core\Menu\MenuTreeStorageInterface
+   */
+  protected $treeStorage;
+
+  /**
+   * The route provider to load routes by name.
+   *
+   * @var \Drupal\Core\Routing\RouteProviderInterface
+   */
+  protected $routeProvider;
+
+  /**
+   * The active menu trail service.
+   *
+   * @var \Drupal\Core\Menu\MenuActiveTrailInterface
+   */
+  protected $menuActiveTrail;
+
+  /**
+   * The controller resolver.
+   *
+   * @var \Drupal\Core\Controller\ControllerResolverInterface
+   */
+  protected $controllerResolver;
+
+  /**
+   * Constructs a \Drupal\Core\Menu\MenuLinkTree object.
+   *
+   * @param \Drupal\Core\Menu\MenuTreeStorageInterface $tree_storage
+   *   The menu link tree storage.
+   * @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
+   *   The menu link plugin manager.
+   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
+   *   The route provider to load routes by name.
+   * @param \Drupal\Core\Menu\MenuActiveTrailInterface $menu_active_trail
+   *   The active menu trail service.
+   * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
+   *   The controller resolver.
+   */
+  public function __construct(MenuTreeStorageInterface $tree_storage, MenuLinkManagerInterface $menu_link_manager, RouteProviderInterface $route_provider, MenuActiveTrailInterface $menu_active_trail, ControllerResolverInterface $controller_resolver) {
+    $this->treeStorage = $tree_storage;
+    $this->menuLinkManager = $menu_link_manager;
+    $this->routeProvider = $route_provider;
+    $this->menuActiveTrail = $menu_active_trail;
+    $this->controllerResolver = $controller_resolver;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCurrentRouteMenuTreeParameters($menu_name) {
+    $active_trail = $this->menuActiveTrail->getActiveTrailIds($menu_name);
+
+    $parameters = new MenuTreeParameters();
+    $parameters->setActiveTrail($active_trail)
+      // We want links in the active trail to be expanded.
+      ->addExpandedParents($active_trail)
+      // We marked the links in the active trail to be expanded, but we also
+      // want their descendants that have the "expanded" flag enabled to be
+      // expanded.
+      ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail));
+
+    return $parameters;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function load($menu_name, MenuTreeParameters $parameters) {
+    $data = $this->treeStorage->loadTreeData($menu_name, $parameters);
+    // Pre-load all the route objects in the tree for access checks.
+    if ($data['route_names']) {
+      $this->routeProvider->getRoutesByNames($data['route_names']);
+    }
+    return $this->createInstances($data['tree']);
+  }
+
+  /**
+   * Returns a tree containing of MenuLinkTreeElement based upon tree data.
+   *
+   * This method converts the tree representation as array coming from the tree
+   * storage to a tree containing a list of MenuLinkTreeElement[].
+   *
+   * @param array $data_tree
+   *   The tree data coming from the menu tree storage.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   An array containing the elements of a menu tree.
+   */
+  protected function createInstances(array $data_tree) {
+    $tree = array();
+    foreach ($data_tree as $key => $element) {
+      $subtree = $this->createInstances($element['subtree']);
+      // Build a MenuLinkTreeElement out of the menu tree link definition:
+      // transform the tree link definition into a link definition and store
+      // tree metadata.
+      $tree[$key] = new MenuLinkTreeElement(
+        $this->menuLinkManager->createInstance($element['definition']['id']),
+        (bool) $element['has_children'],
+        (int) $element['depth'],
+        (bool) $element['in_active_trail'],
+        $subtree
+      );
+    }
+    return $tree;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function transform(array $tree, array $manipulators) {
+    foreach ($manipulators as $manipulator) {
+      $callable = $manipulator['callable'];
+      $callable = $this->controllerResolver->getControllerFromDefinition($callable);
+      // Prepare the arguments for the menu tree manipulator callable; the first
+      // argument is always the menu link tree.
+      if (isset($manipulator['args'])) {
+        array_unshift($manipulator['args'], $tree);
+        $tree = call_user_func_array($callable, $manipulator['args']);
+      }
+      else {
+        $tree = call_user_func($callable, $tree);
+      }
+    }
+    return $tree;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build(array $tree) {
+    $build = array();
+
+    foreach ($tree as $data) {
+      $class = array();
+      /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
+      $link = $data->link;
+      // Generally we only deal with visible links, but just in case.
+      if ($link->isHidden()) {
+        continue;
+      }
+      // Set a class for the <li>-tag. Only set 'expanded' class if the link
+      // also has visible children within the current tree.
+      if ($data->hasChildren && !empty($data->subtree)) {
+        $class[] = 'expanded';
+      }
+      elseif ($data->hasChildren) {
+        $class[] = 'collapsed';
+      }
+      else {
+        $class[] = 'leaf';
+      }
+      // Set a class if the link is in the active trail.
+      if ($data->inActiveTrail) {
+        $class[] = 'active-trail';
+      }
+
+      // Allow menu-specific theme overrides.
+      $element['#theme'] = 'menu_link__' . strtr($link->getMenuName(), '-', '_');
+      $element['#attributes']['class'] = $class;
+      $element['#title'] = $link->getTitle();
+      $element['#url'] = $link->getUrlObject();
+      $element['#below'] = $data->subtree ? $this->build($data->subtree) : array();
+      if (isset($data->options)) {
+        $element['#url']->setOptions(NestedArray::mergeDeep($element['#url']->getOptions(), $data->options));
+      }
+      $element['#original_link'] = $link;
+      // Index using the link's unique ID.
+      $build[$link->getPluginId()] = $element;
+    }
+    if ($build) {
+      // Make sure drupal_render() does not re-order the links.
+      $build['#sorted'] = TRUE;
+      // Get the menu name from the last link.
+      $menu_name = $link->getMenuName();
+      // Add the theme wrapper for outer markup.
+      // Allow menu-specific theme overrides.
+      $build['#theme_wrappers'][] = 'menu_tree__' . strtr($menu_name, '-', '_');
+      // Set cache tag.
+      $build['#cache']['tags']['menu'][$menu_name] = $menu_name;
+    }
+
+    return $build;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function maxDepth() {
+    return $this->treeStorage->maxDepth();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSubtreeHeight($id) {
+    return $this->treeStorage->getSubtreeHeight($id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getExpanded($menu_name, array $parents) {
+    return $this->treeStorage->getExpanded($menu_name, $parents);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTreeElement.php b/core/lib/Drupal/Core/Menu/MenuLinkTreeElement.php
new file mode 100644
index 0000000..ed79fd9
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeElement.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuLinkTreeElement.
+ */
+
+namespace Drupal\Core\Menu;
+
+/**
+ * Provides a value object to model an element in a menu link tree.
+ *
+ * \Drupal\Core\Menu\MenuLinkTreeElement objects represent a menu link's data.
+ * Objects of this class provide complimentary data: the placement in a tree.
+ * Therefore, we can summarize this split as follows:
+ * - Menu link objects contain all information about an individual menu link,
+ *   plus what their parent is. But they don't know where exactly in a menu link
+ *   tree they live.
+ * - Instances of this class are complimentary to those objects, they know:
+ *   1. all additional metadata from {menu_tree}, which contains "materialized"
+ *      metadata about a menu link tree, such as whether a link in the tree has
+ *      visible children and the depth relative to the root;
+ *   2. plus all additional metadata that's adjusted for the current tree query,
+ *      such as whether the link is in the active trail, whether the link is
+ *      accessible for the current user, and the link's children (which are only
+ *      loaded if the link was marked as "expanded" by the query).
+ *
+ * @see \Drupal\Core\Menu\MenuTreeStorage::loadTreeData()
+ */
+class MenuLinkTreeElement {
+
+  /**
+   * The menu link for this element in a menu link tree.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkInterface
+   */
+  public $link;
+
+  /**
+   * The subtree of this element in the menu link tree (this link's children).
+   *
+   * (Children of a link are only loaded if a link is marked as "expanded" by
+   * the query.)
+   *
+   * @var \Drupal\Core\Menu\MenuLinkTreeElement[]
+   */
+  public $subtree;
+
+  /**
+   * The depth of this link relative to the root of the tree.
+   *
+   * @var int
+   */
+  public $depth;
+
+  /**
+   * Whether this link has any children at all.
+   *
+   * @var bool
+   */
+  public $hasChildren;
+
+  /**
+   * Whether this link is in the active trail.
+   *
+   * @var bool
+   */
+  public $inActiveTrail;
+
+  /**
+   * Whether this link is accessible by the current user.
+   *
+   * If the value is NULL the access was not determined yet, if Boolean it was
+   * determined already.
+   *
+   * @var bool|NULL
+   */
+  public $access;
+
+  /**
+   * Additional options for this link.
+   *
+   * This is merged (\Drupal\Component\Utility\NestedArray::mergeDeep()) with
+   * \Drupal\Core\Menu\MenuLinkInterface::getOptions(), to allow menu link tree
+   * manipulators to add or override link options.
+   */
+  public $options = array();
+
+  /**
+   * Constructs a new \Drupal\Core\Menu\MenuLinkTreeElement.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkInterface $link
+   *   The menu link for this element in the menu link tree.
+   * @param bool $has_children
+   *   A flag as to whether this element has children even if they are not
+   *   included in the tree (i.e. this may be TRUE even if $subtree is empty).
+   * @param int $depth
+   *   The depth of this element relative to the tree root.
+   * @param bool $in_active_trail
+   *   A flag as to whether this link was included in the list of active trail
+   *  IDs used to build the tree.
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $subtree
+   *   The children of this element in the menu link tree.
+   */
+  public function __construct(MenuLinkInterface $link, $has_children, $depth, $in_active_trail, array $subtree) {
+    // Essential properties.
+    $this->link = $link;
+    $this->hasChildren = $has_children;
+    $this->depth = $depth;
+    $this->subtree = $subtree;
+    $this->inActiveTrail = $in_active_trail;
+  }
+
+  /**
+   * Counts all menu links in the current subtree.
+   *
+   * @return int
+   *   The number of menu links in this subtree (one plus the number of menu
+   *   links in all descendants).
+   */
+  public function count() {
+    $sum = function ($carry, MenuLinkTreeElement $element) {
+      return $carry + $element->count();
+    };
+    return 1 + array_reduce($this->subtree, $sum);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
new file mode 100644
index 0000000..115b27b
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuLinkTreeInterface.php
@@ -0,0 +1,139 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuLinkTreeInterface.
+ */
+
+namespace Drupal\Core\Menu;
+
+/**
+ * Defines an interface for loading, transforming and rendering menu link trees.
+ *
+ * The main goal of this service is to, given a menu name, load (::load()) the
+ * corresponding tree of menu links and turning this list of menu links into a
+ * tree (by looking at their tree metadata). Because menu links themselves are
+ * responsible for translation, this will already be translated for the current
+ * language.
+ * Which links are loaded can be specified in the menu link tree parameters that
+ * passed to ::load(). You can build your own set of parameter, but you can also
+ * start from a typical default (::getCurrentRouteMenuTreeParameters()).
+ *
+ * @see \Drupal\Core\Menu\MenuLinkTreeParameters
+ *
+ * If desired, one can transform (::transform()) that tree of menu links, for
+ * example performing access checking (to only show those links that can be
+ * accessed by the end user) or adding custom classes to links (to show icons
+ * next to the links). Very complex tasks can be performed as well (such as
+ * extracting a subtree from the menu link tree depending on the active trail).
+ * These transformations are performed by "menu link tree manipulators", and
+ * they can be used to perform any kind of transformation imaginable.
+ *
+ * @see \Drupal\menu_link\DefaultMenuTreeManipulators
+ *
+ * Finally, if desired, that tree of menu links can be built into a renderable
+ * array (::build()) for rendering as HTML.
+ */
+interface MenuLinkTreeInterface {
+
+  /**
+   * Gets the link tree parameters for rendering a specific menu.
+   *
+   * Builds menu link tree parameters that:
+   * - expand all links in the active trail based on route being viewed
+   * - also expands the descendants of the links in the active trail whose
+   *   'expanded' flag is enabled
+   *
+   * This only sets the (relatively complex) parameters to achieve the two above
+   * goals, but you can still further customize these parameters.
+   *
+   * @see \Drupal\Core\Menu\MenuLinkTreeParameters
+   *
+   * @param string $menu_name
+   *   The menu name, needed for retrieving the active trail and links with the
+   *   'expanded' flag enabled.
+   *
+   * @return \Drupal\Core\Menu\MenuTreeParameters
+   *   The parameters to determine which menu links to be loaded into a tree.
+   */
+  public function getCurrentRouteMenuTreeParameters($menu_name);
+
+  /**
+   * Loads a menu tree with a menu link plugin instance at each element.
+   *
+   * @param string $menu_name
+   *   The name of the menu.
+   * @param \Drupal\Core\Menu\MenuTreeParameters $parameters
+   *   The parameters to determine which menu links to be loaded into a tree.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   A menu link tree.
+   */
+  public function load($menu_name, MenuTreeParameters $parameters);
+
+  /**
+   * Applies menu link tree manipulators to transform the given tree.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu tree to manipulate.
+   * @param array $manipulators
+   *   The menu link tree manipulators to apply. Each is an array with keys:
+   *   - callable: a callable or a string that can be resolved to a callable
+   *               by \Drupal\Core\Controller\ControllerResolverInterface::getControllerFromDefinition()
+   *   - args: optional array of arguments to pass to the callable after $tree.
+   *
+   * @return \Drupal\Core\Menu\MenuLinkTreeElement[]
+   *   The manipulated menu link tree.
+   */
+  public function transform(array $tree, array $manipulators);
+
+  /**
+   * Builds a renderable array of a menu tree.
+   *
+   * The menu item's LI element is given one of the following classes:
+   * - expanded: The menu item is showing its submenu.
+   * - collapsed: The menu item has a submenu which is not shown.
+   * - leaf: The menu item has no submenu.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   A data structure representing the tree as returned from ::load().
+   *
+   * @return array
+   *   A renderable array.
+   */
+  public function build(array $tree);
+
+  /**
+   * Returns the maximum depth of tree that is supported.
+   *
+   * @return int
+   *   The maximum depth.
+   */
+  public function maxDepth();
+
+  /**
+   * Finds the height of a subtree rooted by of the given ID.
+   *
+   * @param string $id
+   *   The the ID of an item in the storage.
+   *
+   * @return int
+   *   Returns the height of the subtree. This will be at least 1 if the ID
+   *   exists, or 0 if the ID does not exist in the storage.
+   */
+  public function getSubtreeHeight($id);
+
+  /**
+   * Finds expanded links in a menu given a set of possible parents.
+   *
+   * @param string $menu_name
+   *   The menu name.
+   * @param array $parents
+   *   One or more parent IDs to match.
+   *
+   * @return array
+   *   The menu link IDs that are flagged as expanded in this menu.
+   */
+  public function getExpanded($menu_name, array $parents);
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php b/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php
new file mode 100644
index 0000000..5a1e1f2
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php
@@ -0,0 +1,173 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuParentFormSelector.
+ */
+
+namespace Drupal\Core\Menu;
+
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Default implementation of the menu parent form selector service.
+ *
+ * The form selector is a list of all appropriate menu links.
+ */
+class MenuParentFormSelector implements MenuParentFormSelectorInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * The menu link tree service.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
+   */
+  protected $menuLinkTree;
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a \Drupal\Core\Menu\MenuParentFormSelector
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
+   *   The menu link tree service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(MenuLinkTreeInterface $menu_link_tree, EntityManagerInterface $entity_manager) {
+    $this->menuLinkTree = $menu_link_tree;
+    $this->entityManager = $entity_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getParentSelectOptions($id = '', array $menus = NULL) {
+    if (!isset($menus)) {
+      $menus = $this->getMenuOptions();
+    }
+
+    $options = array();
+    $depth_limit = $this->getParentDepthLimit($id);
+    foreach ($menus as $menu_name => $menu_title) {
+      $options[$menu_name . ':'] = '<' . $menu_title . '>';
+
+      $parameters = new MenuTreeParameters();
+      $parameters->setMaxDepth($depth_limit);
+      $tree = $this->menuLinkTree->load($menu_name, $parameters);
+      $manipulators = array(
+        array('callable' => 'menu.default_tree_manipulators:checkAccess'),
+        array('callable' => 'menu.default_tree_manipulators:generateIndexAndSort'),
+      );
+      $tree = $this->menuLinkTree->transform($tree, $manipulators);
+      $this->parentSelectOptionsTreeWalk($tree, $menu_name, '--', $options, $id, $depth_limit);
+    }
+    return $options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function parentSelectElement($menu_parent, $id = '', array $menus = NULL) {
+    $options = $this->getParentSelectOptions($id, $menus);
+    // If no options were found, there is nothing to select.
+    if ($options) {
+      if (!isset($options[$menu_parent])) {
+        // Try putting it at the top level in the current menu.
+        list($menu_name, $parent) = explode(':', $menu_parent, 2);
+        $menu_parent = $menu_name . ':';
+      }
+      if (isset($options[$menu_parent])) {
+        return array(
+          '#type' => 'select',
+          '#options' => $options,
+          '#default_value' => $menu_parent,
+        );
+      }
+    }
+    return array();
+  }
+
+  /**
+   * Returns the maximum depth of the possible parents of the menu link.
+   *
+   * @param string $id
+   *   The menu link plugin ID or an empty value for a new link.
+   *
+   * @return int
+   *   The depth related to the depth of the given menu link.
+   */
+  protected function getParentDepthLimit($id) {
+    if ($id) {
+      $limit = $this->menuLinkTree->maxDepth() - $this->menuLinkTree->getSubtreeHeight($id);
+    }
+    else {
+      $limit = $this->menuLinkTree->maxDepth() - 1;
+    }
+    return $limit;
+  }
+
+  /**
+   * Iterates over all items in the tree to prepare the parents select options.
+   *
+   * @param \Drupal\Core\Menu\MenuLinkTreeElement[] $tree
+   *   The menu tree.
+   * @param string $menu_name
+   *   The menu name.
+   * @param string $indent
+   *   The indentation string used for the label.
+   * @param array $options
+   *   The select options.
+   * @param string $exclude
+   *   An excluded menu link.
+   * @param int $depth_limit
+   *   The maximum depth of menu links considered for the select options.
+   */
+  protected function parentSelectOptionsTreeWalk(array $tree, $menu_name, $indent, array &$options, $exclude, $depth_limit) {
+    foreach ($tree as $element) {
+      if ($element->depth > $depth_limit) {
+        // Don't iterate through any links on this level.
+        break;
+      }
+      $link = $element->link;
+      if ($link->getPluginId() != $exclude) {
+        $title = $indent . ' ' . Unicode::truncate($link->getTitle(), 30, TRUE, FALSE);
+        if ($link->isHidden()) {
+          $title .= ' (' . $this->t('disabled') . ')';
+        }
+        $options[$menu_name . ':' . $link->getPluginId()] = $title;
+        if (!empty($element->subtree)) {
+          $this->parentSelectOptionsTreeWalk($element->subtree, $menu_name, $indent . '--', $options, $exclude, $depth_limit);
+        }
+      }
+    }
+  }
+
+  /**
+   * Gets a list of menu names for use as options.
+   *
+   * @param array $menu_names
+   *   (optional) Array of menu names to limit the options, or NULL to load all.
+   *
+   * @return array
+   *   Keys are menu names (ids) values are the menu labels.
+   */
+  protected function getMenuOptions(array $menu_names = NULL) {
+    $menus = $this->entityManager->getStorage('menu')->loadMultiple($menu_names);
+    $options = array();
+    /** @var \Drupal\system\MenuInterface[] $menus */
+    foreach ($menus as $menu) {
+      $options[$menu->id()] = $menu->label();
+    }
+    return $options;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuParentFormSelectorInterface.php b/core/lib/Drupal/Core/Menu/MenuParentFormSelectorInterface.php
new file mode 100644
index 0000000..afe8ec2
--- /dev/null
+++ b/core/lib/Drupal/Core/Menu/MenuParentFormSelectorInterface.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\MenuParentFormSelectorInterface.
+ */
+
+namespace Drupal\Core\Menu;
+
+/**
+ * Defines an interface for menu selector form elements and menu link options.
+ */
+interface MenuParentFormSelectorInterface {
+
+  /**
+   * Gets the options for a select element to choose a menu and parent.
+   *
+   * @param string $id
+   *   Optional ID of a link plugin. This will exclude the link and its
+   *   children from the select options.
+   * @param array $menus
+   *   Optional array of menu names as keys and titles as values to limit
+   *   the select options.  If NULL, all menus will be included.
+   *
+   * @return array
+   *   Keyed array where the keys are contain a menu name and parent ID and
+   *   the values are a menu name or link title indented by depth.
+   *
+   * @deprecated
+   */
+  public function getParentSelectOptions($id = '', array $menus = NULL);
+
+  /**
+   * Gets a form element to choose a menu and parent.
+   *
+   * The specific type of form element will vary depending on the
+   * implementation, but callers will normally need to set the #title for the
+   * element.
+   *
+   * @param string $menu_parent
+   *   A menu name and parent ID concatenated with a ':' character to use as the
+   *   default value.
+   * @param string $id
+   *   (optional) ID of a link plugin. This will exclude the link and its
+   *   children from being selected.
+   * @param array $menus
+   *   (optional) array of menu names as keys and titles as values to limit
+   *   the values that may be selected. If NULL, all menus will be included.
+   *
+   * @return array
+   *   A form element to choose a parent, or an empty array if no possible
+   *   parents exist for the given parameters. The resulting form value will be
+   *   a single string containing the chosen menu name and parent ID separated
+   *   by a ':' character.
+   */
+  public function parentSelectElement($menu_parent, $id = '', array $menus = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/Menu/MenuTreeParameters.php b/core/lib/Drupal/Core/Menu/MenuTreeParameters.php
index 23a0992..c18ebb1 100644
--- a/core/lib/Drupal/Core/Menu/MenuTreeParameters.php
+++ b/core/lib/Drupal/Core/Menu/MenuTreeParameters.php
@@ -198,7 +198,7 @@ public function excludeHiddenLinks() {
    *
    * @return $this
    */
-  public function topLevelOnly() {
+  public function setTopLevelOnly() {
     $this->setMaxDepth(1);
     return $this;
   }
diff --git a/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php b/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php
new file mode 100644
index 0000000..80ca581
--- /dev/null
+++ b/core/modules/system/src/Tests/Menu/MenuLinkTreeTest.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Menu\MenuLinkTreeTest.
+ */
+
+namespace Drupal\system\Tests\Menu;
+
+use Drupal\Core\Menu\MenuLinkTreeElement;
+use Drupal\Core\Menu\MenuTreeParameters;
+use Drupal\simpletest\KernelTestBase;
+use Drupal\Tests\Core\Menu\MenuLinkMock;
+
+/**
+ * Tests the menu link tree.
+ *
+ * @group Menu
+ *
+ * @see \Drupal\Core\Menu\MenuLinkTree
+ */
+class MenuLinkTreeTest extends KernelTestBase {
+
+  /**
+   * The tested menu link tree.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkTree
+   */
+  protected $linkTree;
+
+  /**
+   * The menu link plugin manager.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkManagerInterface $menuLinkManager
+   */
+  protected $menuLinkManager;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array(
+    'system',
+    'menu_test',
+    'menu_link',
+    'menu_link_content',
+    'field',
+  );
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installSchema('system', array('router'));
+    $this->installEntitySchema('menu_link_content');
+
+    $this->linkTree = $this->container->get('menu.link_tree');
+    $this->menuLinkManager = $this->container->get('plugin.manager.menu.link');
+  }
+
+  /**
+   * Tests deleting all the links in a menu.
+   */
+  public function testDeleteLinksInMenu() {
+    \Drupal::service('router.builder')->rebuild();
+
+    \Drupal::entityManager()->getStorage('menu')->create(array('id' => 'menu1'))->save();
+    \Drupal::entityManager()->getStorage('menu')->create(array('id' => 'menu2'))->save();
+
+    \Drupal::entityManager()->getStorage('menu_link_content')->create(array('route_name' => 'menu_test.menu_name_test', 'menu_name' => 'menu1', 'bundle' => 'menu_link_content'))->save();
+    \Drupal::entityManager()->getStorage('menu_link_content')->create(array('route_name' => 'menu_test.menu_name_test', 'menu_name' => 'menu1', 'bundle' => 'menu_link_content'))->save();
+    \Drupal::entityManager()->getStorage('menu_link_content')->create(array('route_name' => 'menu_test.menu_name_test', 'menu_name' => 'menu2', 'bundle' => 'menu_link_content'))->save();
+
+    $output = $this->linkTree->load('menu1', new MenuTreeParameters());
+    $this->assertEqual(count($output), 2);
+    $output = $this->linkTree->load('menu2', new MenuTreeParameters());
+    $this->assertEqual(count($output), 1);
+
+    $this->menuLinkManager->deleteLinksInMenu('menu1');
+
+    $output = $this->linkTree->load('menu1', new MenuTreeParameters());
+    $this->assertEqual(count($output), 0);
+
+    $output = $this->linkTree->load('menu2', new MenuTreeParameters());
+    $this->assertEqual(count($output), 1);
+  }
+
+  /**
+   * Tests creating links with an expected tree structure.
+   */
+  public function testCreateLinksInMenu() {
+     // This creates a tree with the following structure:
+     // - 1
+     // - 2
+     //   - 3
+     //     - 4
+     // - 5
+     //   - 7
+     // - 6
+     // - 8
+     // With link 6 being the only external link.
+
+    $links = array(
+      1 => MenuLinkMock::create(array('id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '')),
+      2 => MenuLinkMock::create(array('id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => 'test.example1', 'route_parameters' => array('foo' => 'bar'))),
+      3 => MenuLinkMock::create(array('id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'route_parameters' => array('baz' => 'qux'))),
+      4 => MenuLinkMock::create(array('id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3')),
+      5 => MenuLinkMock::create(array('id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '')),
+      6 => MenuLinkMock::create(array('id' => 'test.example6', 'route_name' => '', 'url' => 'https://drupal.org/', 'title' => 'barbar', 'parent' => '')),
+      7 => MenuLinkMock::create(array('id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => '')),
+      8 => MenuLinkMock::create(array('id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '')),
+    );
+    foreach ($links as $instance) {
+      $this->menuLinkManager->addDefinition($instance->getPluginId(), $instance->getPluginDefinition());
+    }
+    $parameters = new MenuTreeParameters();
+    $tree = $this->linkTree->load('mock', $parameters);
+
+    $count = function(array $tree) {
+      $sum = function ($carry, MenuLinkTreeElement $item) {
+        return $carry + $item->count();
+      };
+      return array_reduce($tree, $sum);
+    };
+
+    $this->assertEqual($count($tree), 8);
+    $parameters = new MenuTreeParameters();
+    $parameters->setRoot('test.example2');
+    $tree = $this->linkTree->load($instance->getMenuName(), $parameters);
+    $top_link = reset($tree);
+    $this->assertEqual(count($top_link->subtree), 1);
+    $child = reset($top_link->subtree);
+    $this->assertEqual($child->link->getPluginId(), $links[3]->getPluginId());
+    $height = $this->linkTree->getSubtreeHeight('test.example2');
+    $this->assertEqual($height, 3);
+  }
+
+}
diff --git a/core/modules/views/includes/ajax.inc b/core/modules/views/includes/ajax.inc
index 7f6fc89..0bcb723 100644
--- a/core/modules/views/includes/ajax.inc
+++ b/core/modules/views/includes/ajax.inc
@@ -10,8 +10,7 @@
 use Drupal\Core\Ajax\AjaxResponse;
 
 /**
- * Wrapper around \Drupal\Core\Form\FormBuilderInterface::buildForm() to handle
- * some AJAX stuff automatically.
+ * Wrapper around drupal_build_form to handle some AJAX stuff automatically.
  * This makes some assumptions about the client.
  */
 function views_ajax_form_wrapper($form_id, &$form_state) {
@@ -25,7 +24,7 @@ function views_ajax_form_wrapper($form_id, &$form_state) {
     ),
   );
 
-  $form = \Drupal::formBuilder()->buildForm($form_id, $form_state);
+  $form = drupal_build_form($form_id, $form_state);
   $output = drupal_render($form);
 
   // These forms have the title built in, so set the title here:
diff --git a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
new file mode 100644
index 0000000..a62b20b
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
@@ -0,0 +1,251 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Menu\DefaultMenuLinkTreeManipulatorsTest.
+ */
+
+namespace Drupal\Tests\Core\Menu;
+
+use Drupal\Core\Menu\DefaultMenuLinkTreeManipulators;
+use Drupal\Core\Menu\MenuLinkTreeElement;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the default menu link tree manipulators.
+ *
+ * @group Menu
+ *
+ * @coversDefaultClass \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators
+ */
+class DefaultMenuLinkTreeManipulatorsTest extends UnitTestCase {
+
+  /**
+   * The mocked access manager.
+   *
+   * @var \Drupal\Core\Access\AccessManager|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $accessManager;
+
+  /**
+   * The mocked current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $currentUser;
+
+  /**
+   * The default menu link tree manipulators.
+   *
+   * @var \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators
+   */
+  protected $defaultMenuTreeManipulators;
+
+  /**
+   * The original menu tree build in mockTree().
+   *
+   * @var \Drupal\Tests\Core\Menu\MenuLinkMock[]
+   */
+  protected $originalTree = array();
+
+  /**
+   * Array of menu link instances
+   *
+   * @var \Drupal\Core\Menu\MenuLinkInterface[]
+   */
+  protected $links = array();
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->accessManager = $this->getMockBuilder('\Drupal\Core\Access\AccessManager')
+      ->disableOriginalConstructor()->getMock();
+    $this->currentUser = $this->getMock('Drupal\Core\Session\AccountInterface');
+
+    $this->defaultMenuTreeManipulators = new DefaultMenuLinkTreeManipulators($this->accessManager, $this->currentUser);
+  }
+
+  /**
+   * Creates a mock tree.
+   *
+   * This mocks a tree with the following structure:
+   * - 1
+   * - 2
+   *   - 3
+   *     - 4
+   * - 5
+   *   - 7
+   * - 6
+   * - 8
+   *
+   * With link 6 being the only external link.
+   */
+  protected function mockTree() {
+    $this->links = array(
+      1 => MenuLinkMock::create(array('id' => 'test.example1', 'route_name' => 'example1', 'title' => 'foo', 'parent' => '')),
+      2 => MenuLinkMock::create(array('id' => 'test.example2', 'route_name' => 'example2', 'title' => 'bar', 'parent' => 'test.example1', 'route_parameters' => array('foo' => 'bar'))),
+      3 => MenuLinkMock::create(array('id' => 'test.example3', 'route_name' => 'example3', 'title' => 'baz', 'parent' => 'test.example2', 'route_parameters' => array('baz' => 'qux'))),
+      4 => MenuLinkMock::create(array('id' => 'test.example4', 'route_name' => 'example4', 'title' => 'qux', 'parent' => 'test.example3')),
+      5 => MenuLinkMock::create(array('id' => 'test.example5', 'route_name' => 'example5', 'title' => 'foofoo', 'parent' => '')),
+      6 => MenuLinkMock::create(array('id' => 'test.example6', 'route_name' => '', 'url' => 'https://drupal.org/', 'title' => 'barbar', 'parent' => '')),
+      7 => MenuLinkMock::create(array('id' => 'test.example7', 'route_name' => 'example7', 'title' => 'bazbaz', 'parent' => '')),
+      8 => MenuLinkMock::create(array('id' => 'test.example8', 'route_name' => 'example8', 'title' => 'quxqux', 'parent' => '')),
+    );
+    $this->originalTree = array();
+    $this->originalTree[1] = new MenuLinkTreeElement($this->links[1], FALSE, 1, FALSE, array());
+    $this->originalTree[2] = new MenuLinkTreeElement($this->links[2], TRUE, 1, FALSE, array(
+      3 => new MenuLinkTreeElement($this->links[3], TRUE, 2, FALSE, array(
+        4 => new MenuLinkTreeElement($this->links[4], FALSE, 3, FALSE, array()),
+      )),
+    ));
+    $this->originalTree[5] = new MenuLinkTreeElement($this->links[5], TRUE, 1, FALSE, array(
+      7 => new MenuLinkTreeElement($this->links[7], FALSE, 2, FALSE, array()),
+    ));
+    $this->originalTree[6] = new MenuLinkTreeElement($this->links[6], FALSE, 1, FALSE, array());
+    $this->originalTree[8] = new MenuLinkTreeElement($this->links[8], FALSE, 1, FALSE, array());
+  }
+
+  /**
+   * Tests the generateIndexAndSort() tree manipulator.
+   *
+   * @covers ::generateIndexAndSort
+   */
+  public function testGenerateIndexAndSort() {
+    $this->mockTree();
+    $tree = $this->originalTree;
+    $tree = $this->defaultMenuTreeManipulators->generateIndexAndSort($tree);
+
+    // Validate that parent elements #1, #2, #5 and #6 exist on the root level.
+    $this->assertEquals($this->links[1]->getPluginId(), $tree['50000 foo test.example1']->link->getPluginId());
+    $this->assertEquals($this->links[2]->getPluginId(), $tree['50000 bar test.example2']->link->getPluginId());
+    $this->assertEquals($this->links[5]->getPluginId(), $tree['50000 foofoo test.example5']->link->getPluginId());
+    $this->assertEquals($this->links[6]->getPluginId(), $tree['50000 barbar test.example6']->link->getPluginId());
+    $this->assertEquals($this->links[8]->getPluginId(), $tree['50000 quxqux test.example8']->link->getPluginId());
+
+    // Verify that child element #4 is at the correct location in the hierarchy.
+    $this->assertEquals($this->links[4]->getPluginId(), $tree['50000 bar test.example2']->subtree['50000 baz test.example3']->subtree['50000 qux test.example4']->link->getPluginId());
+    // Verify that child element #7 is at the correct location in the hierarchy.
+    $this->assertEquals($this->links[7]->getPluginId(), $tree['50000 foofoo test.example5']->subtree['50000 bazbaz test.example7']->link->getPluginId());
+  }
+
+  /**
+   * Tests the checkAccess() tree manipulator.
+   *
+   * @covers ::checkAccess
+   */
+  public function testCheckAccess() {
+    // Those menu links that are non-external will have their access checks
+    // performed. 8 routes, but 1 is external, 2 already have their 'access'
+    // property set, and 1 is a child if an inaccessible menu link, so only 4
+    // calls will be made.
+    $this->accessManager->expects($this->exactly(4))
+      ->method('checkNamedRoute')
+      ->will($this->returnValueMap(array(
+        array('example1', array(), $this->currentUser, NULL, FALSE),
+        array('example2', array('foo' => 'bar'), $this->currentUser, NULL, TRUE),
+        array('example3', array('baz' => 'qux'), $this->currentUser, NULL, FALSE),
+        array('example5', array(), $this->currentUser, NULL, TRUE),
+      )));
+
+    $this->mockTree();
+    $this->originalTree[5]->subtree[7]->access = TRUE;
+    $this->originalTree[8]->access = FALSE;
+
+    $tree = $this->defaultMenuTreeManipulators->checkAccess($this->originalTree);
+
+    // Menu link 1: route without parameters, access forbidden, hence removed.
+    $this->assertFalse(array_key_exists(1, $tree));
+    // Menu link 2: route with parameters, access granted.
+    $element = $tree[2];
+    $this->assertTrue($element->access);
+    // Menu link 3: route with parameters, access forbidden, hence removed,
+    // including its children.
+    $this->assertFalse(array_key_exists(3, $tree[2]->subtree));
+    // Menu link 4: child of menu link 3, which already is removed.
+    $this->assertSame(array(), $tree[2]->subtree);
+    // Menu link 5: no route name, treated as external, hence access granted.
+    $element = $tree[5];
+    $this->assertTrue($element->access);
+    // Menu link 6: external URL, hence access granted.
+    $element = $tree[6];
+    $this->assertTrue($element->access);
+    // Menu link 7: 'access' already set.
+    $element = $tree[5]->subtree[7];
+    $this->assertTrue($element->access);
+    // Menu link 8: 'access' already set, to FALSE, hence removed.
+    $this->assertFalse(array_key_exists(8, $tree));
+  }
+
+  /**
+   * Tests the flatten() tree manipulator.
+   *
+   * @covers ::flatten
+   */
+  public function testFlatten() {
+    $this->mockTree();
+    $tree = $this->defaultMenuTreeManipulators->flatten($this->originalTree);
+    $this->assertEquals(array(1, 2, 5, 6, 8), array_keys($this->originalTree));
+    $this->assertEquals(array(1, 2, 5, 6, 8, 3, 4, 7), array_keys($tree));
+  }
+
+  /**
+   * Tests the extractSubtreeOfActiveTrail() tree manipulator.
+   *
+   * @covers ::extractSubtreeOfActiveTrail
+   */
+  public function testExtractSubtreeOfActiveTrail() {
+    // No link in the active trail.
+    $this->mockTree();
+    // Get level 0.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 0);
+    $this->assertEquals(array(1, 2, 5, 6, 8), array_keys($tree));
+    // Get level 1.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 1);
+    $this->assertEquals(array(), array_keys($tree));
+    // Get level 2.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 1);
+    $this->assertEquals(array(), array_keys($tree));
+
+    // Link 5 in the active trail.
+    $this->mockTree();
+    $this->originalTree[5]->inActiveTrail = TRUE;
+    // Get level 0.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 0);
+    $this->assertEquals(array(1, 2, 5, 6, 8), array_keys($tree));
+    // Get level 1.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 1);
+    $this->assertEquals(array(7), array_keys($tree));
+    // Get level 2.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 2);
+    $this->assertEquals(array(), array_keys($tree));
+
+    // Link 2 in the active trail.
+    $this->mockTree();
+    $this->originalTree[2]->inActiveTrail = TRUE;
+    // Get level 0.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 0);
+    $this->assertEquals(array(1, 2, 5, 6, 8), array_keys($tree));
+    // Get level 1.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 1);
+    $this->assertEquals(array(3), array_keys($tree));
+    // Get level 2.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 2);
+    $this->assertEquals(array(), array_keys($tree));
+
+    // Links 2 and 3 in the active trail.
+    $this->mockTree();
+    $this->originalTree[2]->inActiveTrail = TRUE;
+    $this->originalTree[2]->subtree[3]->inActiveTrail = TRUE;
+    // Get level 0.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 0);
+    $this->assertEquals(array(1, 2, 5, 6, 8), array_keys($tree));
+    // Get level 1.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 1);
+    $this->assertEquals(array(3), array_keys($tree));
+    // Get level 2.
+    $tree = $this->defaultMenuTreeManipulators->extractSubtreeOfActiveTrail($this->originalTree, 2);
+    $this->assertEquals(array(4), array_keys($tree));
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/MenuActiveTrailTest.php b/core/tests/Drupal/Tests/Core/Menu/MenuActiveTrailTest.php
new file mode 100644
index 0000000..ce9c5fb
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Menu/MenuActiveTrailTest.php
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link\Tests\MenuActiveTrailTest.
+ */
+
+namespace Drupal\Tests\Core\Menu;
+
+use Drupal\Core\Menu\MenuActiveTrail;
+use Drupal\Core\Routing\CurrentRouteMatch;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Tests the active menu trail service.
+ *
+ * @group Menu
+ *
+ * @coversDefaultClass \Drupal\Core\Menu\MenuActiveTrail
+ */
+class MenuActiveTrailTest extends UnitTestCase {
+
+  /**
+   * The tested active menu trail service.
+   *
+   * @var \Drupal\Core\Menu\MenuActiveTrail
+   */
+  protected $menuActiveTrail;
+
+  /**
+   * The test request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * The current route match service.
+   *
+   * @var \Drupal\Core\Routing\CurrentRouteMatch;
+   */
+  protected $currentRouteMatch;
+
+  /**
+   * The mocked menu link manager.
+   *
+   * @var \Drupal\Core\Menu\MenuLinkManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $menuLinkManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->requestStack = new RequestStack();
+    $this->currentRouteMatch = new CurrentRouteMatch($this->requestStack);
+    $this->menuLinkManager = $this->getMock('Drupal\Core\Menu\MenuLinkManagerInterface');
+
+    $this->menuActiveTrail = new MenuActiveTrail($this->menuLinkManager, $this->currentRouteMatch);
+  }
+
+  /**
+   * Provides test data for all test methods.
+   *
+   * @return array
+   *   Returns a list of test data of which each is an array containing the following
+   *   elements:
+   *     - request: A request object.
+   *     - links: An array of menu links keyed by ID.
+   *     - menu_name: The active menu name.
+   *     - expected_link: The expected active link for the given menu.
+   */
+  public function provider() {
+    $data = array();
+
+    $mock_route = new Route('');
+
+    $request = new Request();
+    $request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'baby_llama');
+    $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, $mock_route);
+    $request->attributes->set('_raw_variables', new ParameterBag(array()));
+
+    $link_1 = MenuLinkMock::create(array('id' => 'baby_llama_link_1', 'route_name' => 'baby_llama', 'title' => 'Baby llama', 'parent' => 'mama_llama_link'));
+    $link_2 = MenuLinkMock::create(array('id' => 'baby_llama_link_2', 'route_name' => 'baby_llama', 'title' => 'Baby llama', 'parent' => 'papa_llama_link'));
+
+    // @see \Drupal\Core\Menu\MenuLinkManagerInterface::getParentIds()
+    $link_1_parent_ids = array('baby_llama_link_1', 'mama_llama_link', '');
+    $empty_active_trail = array('');
+
+    $link_1__active_trail_cache_key = 'menu_trail.baby_llama_link_1|mama_llama_link|';
+    $empty_active_trail_cache_key = 'menu_trail.';
+
+    // No active link is returned when zero links match the current route.
+    $data[] = array($request, array(), $this->randomName(), NULL, $empty_active_trail, $empty_active_trail_cache_key);
+
+    // The first (and only) matching link is returned when one link matches the
+    // current route.
+    $data[] = array($request, array('baby_llama_link_1' => $link_1), $this->randomName(), $link_1, $link_1_parent_ids, $link_1__active_trail_cache_key);
+
+    // The first of multiple matching links is returned when multiple links
+    // match the current route, where "first" is determined by sorting by key.
+    $data[] = array($request, array('baby_llama_link_1' => $link_1, 'baby_llama_link_2' => $link_2), $this->randomName(), $link_1, $link_1_parent_ids, $link_1__active_trail_cache_key);
+
+    // No active link is returned in case of a 403.
+    $request = new Request();
+    $request->attributes->set('_exception_statuscode', 403);
+    $data[] = array($request, FALSE, $this->randomName(), NULL, $empty_active_trail, $empty_active_trail_cache_key);
+
+    // No active link is returned when the route name is missing.
+    $request = new Request();
+    $data[] = array($request, FALSE, $this->randomName(), NULL, $empty_active_trail, $empty_active_trail_cache_key);
+
+    return $data;
+  }
+
+  /**
+   * Tests getActiveLink().
+   *
+   * @covers ::getActiveLink()
+   * @dataProvider provider
+   */
+  public function testGetActiveLink(Request $request, $links, $menu_name, $expected_link) {
+    $this->requestStack->push($request);
+    if ($links !== FALSE) {
+      $this->menuLinkManager->expects($this->exactly(2))
+        ->method('loadLinksbyRoute')
+        ->with('baby_llama')
+        ->will($this->returnValue($links));
+    }
+    // Test with menu name.
+    $this->assertSame($expected_link, $this->menuActiveTrail->getActiveLink($menu_name));
+    // Test without menu name.
+    $this->assertSame($expected_link, $this->menuActiveTrail->getActiveLink());
+  }
+
+  /**
+   * Tests getActiveTrailIds().
+   *
+   * @covers ::getActiveTrailIds()
+   * @covers ::getActiveTrailCacheKey()
+   * @dataProvider provider
+   */
+  public function testGetActiveTrailIds(Request $request, $links, $menu_name, $expected_link, $expected_trail, $expected_cache_key) {
+    $expected_trail_ids = array_combine($expected_trail, $expected_trail);
+
+    $this->requestStack->push($request);
+    if ($links !== FALSE) {
+      $this->menuLinkManager->expects($this->exactly(2))
+        ->method('loadLinksbyRoute')
+        ->with('baby_llama')
+        ->will($this->returnValue($links));
+      if ($expected_link !== NULL) {
+        $this->menuLinkManager->expects($this->exactly(2))
+          ->method('getParentIds')
+          ->will($this->returnValueMap(array(
+            array($expected_link->getPluginId(), $expected_trail_ids),
+          )));
+      }
+    }
+
+    $this->assertSame($expected_trail_ids, $this->menuActiveTrail->getActiveTrailIds($menu_name));
+    $this->assertSame($expected_cache_key, $this->menuActiveTrail->getActiveTrailCacheKey($menu_name));
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/MenuLinkMock.php b/core/tests/Drupal/Tests/Core/Menu/MenuLinkMock.php
new file mode 100644
index 0000000..7086aa6
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Menu/MenuLinkMock.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Menu\MenuLinkMock
+ */
+
+namespace Drupal\Tests\Core\Menu;
+
+use Drupal\Core\Menu\MenuLinkBase;
+
+/**
+ * Defines a mock implementation of a menu link used in tests only.
+ */
+class MenuLinkMock extends MenuLinkBase {
+
+  protected static $defaults = array(
+    'menu_name' => 'mock',
+    'route_name' => 'MUST BE PROVIDED',
+    'route_parameters' => array(),
+    'url' => '',
+    'title' => 'MUST BE PROVIDED',
+    'title_arguments' => array(),
+    'title_context' => '',
+    'description' => '',
+    'parent' => 'MUST BE PROVIDED',
+    'weight' => '0',
+    'options' => array(),
+    'expanded' => '0',
+    'hidden' => '0',
+    'discovered' => '1',
+    'provider' => 'simpletest',
+    'metadata' => array(),
+    'class' => 'Drupal\\Tests\\Core\Menu\\MenuLinkMock',
+    'form_class' => 'Drupal\\Core\\Menu\\Form\\MenuLinkDefaultForm',
+    'id' => 'MUST BE PROVIDED',
+  );
+
+  /**
+   * Create an instance from a definition with at least id, title, route_name.
+   */
+  public static function create($definition) {
+    return new static(array(), $definition['id'], $definition + static::$defaults);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTitle() {
+    return $this->pluginDefinition['title'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDescription() {
+    if ($this->pluginDefinition['description']) {
+      return $this->pluginDefinition['description'];
+    }
+    return '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateLink(array $new_definition_values, $persist) {
+    // No-op.
+    return $this->pluginDefinition;
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/MenuLinkTreeElementTest.php b/core/tests/Drupal/Tests/Core/Menu/MenuLinkTreeElementTest.php
new file mode 100644
index 0000000..da1a318
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Menu/MenuLinkTreeElementTest.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Menu\MenuLinkTreeElementTest.
+ */
+
+namespace Drupal\Tests\Core\Menu;
+
+use Drupal\Core\Menu\MenuLinkTreeElement;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the menu link tree element value object.
+ *
+ * @group Menu
+ *
+ * @coversDefaultClass \Drupal\Core\Menu\MenuLinkTreeElement
+ */
+class MenuLinkTreeElementTest extends UnitTestCase {
+
+  /**
+   * Tests construction.
+   *
+   * @covers ::__construct
+   */
+  public function testConstruction() {
+    $link = MenuLinkMock::create(array('id' => 'test'));
+    $item = new MenuLinkTreeElement($link, FALSE, 3, FALSE, array());
+    $this->assertSame($link, $item->link);
+    $this->assertSame(FALSE, $item->hasChildren);
+    $this->assertSame(3, $item->depth);
+    $this->assertSame(FALSE, $item->inActiveTrail);
+    $this->assertSame(array(), $item->subtree);
+  }
+
+  /**
+   * Tests count().
+   *
+   * @covers ::count
+   */
+  public function testCount() {
+    $link_1 = MenuLinkMock::create(array('id' => 'test_1'));
+    $link_2 = MenuLinkMock::create(array('id' => 'test_2'));
+    $child_item = new MenuLinkTreeElement($link_2, FALSE, 2, FALSE, array());
+    $parent_item = new MenuLinkTreeElement($link_1, FALSE, 2, FALSE, array($child_item));
+    $this->assertSame(1, $child_item->count());
+    $this->assertSame(2, $parent_item->count());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Menu/MenuTreeParametersTest.php b/core/tests/Drupal/Tests/Core/Menu/MenuTreeParametersTest.php
new file mode 100644
index 0000000..750cb93
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Menu/MenuTreeParametersTest.php
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Menu\MenuTreeParametersTest.
+ */
+
+namespace Drupal\Tests\Core\Menu;
+
+use Drupal\Core\Menu\MenuTreeParameters;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the menu link tree parameters value object.
+ *
+ * @group Menu
+ *
+ * @coversDefaultClass \Drupal\Core\Menu\MenuTreeParameters
+ */
+class MenuTreeParametersTest extends UnitTestCase {
+
+  /**
+   * Provides test data for testSetMinDepth().
+   */
+  public function providerTestSetMinDepth() {
+    $data = array();
+
+    // Valid values at the extremes and in the middle.
+    $data[] = array(1, 1);
+    $data[] = array(2, 2);
+    $data[] = array(9, 9);
+
+    // Invalid values are mapped to the closest valid value.
+    $data[] = array(-10000, 1);
+    $data[] = array(0, 1);
+    // … except for those invalid values that reach beyond the maximum depth,
+    // because MenuTreeParameters is a value object and hence cannot depend
+    // on anything; to know the actual maximum depth, it'd have to depend on the
+    // MenuTreeStorage service.
+    $data[] = array(10, 10);
+    $data[] = array(100000, 100000);
+
+    return $data;
+  }
+
+  /**
+   * Tests setMinDepth().
+   *
+   * @covers ::setMinDepth
+   * @dataProvider providerTestSetMinDepth
+   */
+  public function testSetMinDepth($min_depth, $expected) {
+    $parameters = new MenuTreeParameters();
+    $parameters->setMinDepth($min_depth);
+    $this->assertEquals($expected, $parameters->minDepth);
+  }
+
+  /**
+   * Tests addExpandedParents().
+   *
+   * @covers ::addExpandedParents
+   */
+  public function testAddExpanded() {
+    $parameters = new MenuTreeParameters();
+
+    // Verify default value.
+    $this->assertEquals(array(), $parameters->expandedParents);
+
+    // Add actual menu link plugin IDs to be expanded.
+    $parameters->addExpandedParents(array('foo', 'bar', 'baz'));
+    $this->assertEquals(array('foo', 'bar', 'baz'), $parameters->expandedParents);
+
+    // Add additional menu link plugin IDs; they should be merged, not replacing
+    // the old ones.
+    $parameters->addExpandedParents(array('qux', 'quux'));
+    $this->assertEquals(array('foo', 'bar', 'baz', 'qux', 'quux'), $parameters->expandedParents);
+
+    // Add pre-existing menu link plugin IDs; they should not be added again;
+    // this is a set.
+    $parameters->addExpandedParents(array('bar', 'quux'));
+    $this->assertEquals(array('foo', 'bar', 'baz', 'qux', 'quux'), $parameters->expandedParents);
+  }
+
+  /**
+   * Tests addCondition().
+   *
+   * @covers ::addCondition
+   */
+  public function testAddCondition() {
+    $parameters = new MenuTreeParameters();
+
+    // Verify default value.
+    $this->assertEquals(array(), $parameters->conditions);
+
+    // Add a condition.
+    $parameters->addCondition('expanded', 1);
+    $this->assertEquals(array('expanded' => 1), $parameters->conditions);
+
+    // Add another condition.
+    $parameters->addCondition('has_children', 0);
+    $this->assertEquals(array('expanded' => 1, 'has_children' => 0), $parameters->conditions);
+
+    // Add a condition with an operator.
+    $parameters->addCondition('provider', array('module1', 'module2'), 'IN');
+    $this->assertEquals(array('expanded' => 1, 'has_children' => 0, 'provider' => array(array('module1', 'module2'), 'IN')), $parameters->conditions);
+
+    // Add another condition with an operator.
+    $parameters->addCondition('id', 1337, '<');
+    $this->assertEquals(array('expanded' => 1, 'has_children' => 0, 'provider' => array(array('module1', 'module2'), 'IN'), 'id' => array(1337, '<')), $parameters->conditions);
+
+    // It's impossible to add two conditions on the same field; in that case,
+    // the old condition will be overwritten.
+    $parameters->addCondition('provider', 'other_module');
+    $this->assertEquals(array('expanded' => 1, 'has_children' => 0, 'provider' => 'other_module', 'id' => array(1337, '<')), $parameters->conditions);
+  }
+
+  /**
+   * Tests excludeHiddenLinks().
+   *
+   * @covers ::excludeHiddenLinks
+   */
+  public function testExcludeHiddenLinks() {
+    $parameters = new MenuTreeParameters();
+    $parameters->excludeHiddenLinks();
+    $this->assertEquals(0, $parameters->conditions['hidden']);
+  }
+
+  /**
+   * Tests setTopLevelOnly().
+   *
+   * @covers ::setTopLevelOnly
+   */
+  public function testSetTopLevelOnly() {
+    $parameters = new MenuTreeParameters();
+    $parameters->setTopLevelOnly();
+    $this->assertEquals(1, $parameters->maxDepth);
+  }
+
+  /**
+   * Tests excludeRoot().
+   *
+   * @covers ::excludeRoot
+   */
+  public function testExcludeRoot() {
+    $parameters = new MenuTreeParameters();
+    $parameters->excludeRoot();
+    $this->assertEquals(1, $parameters->minDepth);
+  }
+
+}
