From 42e2c36937b3b842a13417082a1d1ba2fd0d0b6c Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Fri, 24 Jan 2014 11:23:53 +0100
Subject: [PATCH] Revert "Issue #2047633 by dawehner, pwolanin, Xano,
 amateescu, tim.plunkett: Move definition of menu links to
 hook_menu_link_defaults(), decouple key name from path, and make 'parent'
 explicit."

This reverts commit 531fd59314965728e495ea3fb737e5c5bc4b5659.
---
 core/includes/bootstrap.inc                   |   9 +-
 core/includes/menu.inc                        | 358 ++++++++++--------
 core/includes/path.inc                        |  27 +-
 core/includes/theme.inc                       |   8 -
 core/modules/action/action.module             |  14 -
 core/modules/aggregator/aggregator.module     |  24 --
 core/modules/ban/ban.module                   |  15 -
 core/modules/block/block.module               |  14 -
 core/modules/book/book.module                 |  24 +-
 .../book/lib/Drupal/book/BookManager.php      |   6 +-
 core/modules/comment/comment.module           |  14 -
 core/modules/config/config.module             |  14 -
 .../tests/config_test/config_test.routing.yml |   1 -
 .../config_translation.module                 |  15 -
 core/modules/contact/contact.module           |  20 -
 .../content_translation.module                |   9 -
 core/modules/dblog/dblog.module               |  36 --
 core/modules/editor/editor.module             |   8 -
 core/modules/entity/entity.module             |  30 --
 .../EntityDisplayModeController.php           |   4 +-
 core/modules/field_ui/field_ui.module         |  14 -
 core/modules/filter/filter.module             |  20 -
 core/modules/forum/forum.module               |  18 -
 core/modules/forum/forum.routing.yml          |   1 -
 core/modules/help/help.module                 |  15 -
 core/modules/image/image.module               |  14 -
 core/modules/language/language.module         |  23 --
 core/modules/locale/locale.module             |  23 --
 .../lib/Drupal/menu/Form/MenuDeleteForm.php   |   4 +-
 .../menu/lib/Drupal/menu/Tests/MenuTest.php   |   2 +-
 core/modules/menu/menu.install                |   3 +-
 core/modules/menu/menu.module                 |  13 -
 .../lib/Drupal/menu_link/Entity/MenuLink.php  | 122 +++---
 .../Drupal/menu_link/MenuLinkInterface.php    |  48 ++-
 .../menu_link/MenuLinkStorageController.php   |  33 +-
 .../MenuLinkStorageControllerInterface.php    |  28 +-
 core/modules/menu_link/menu_link.api.php      |  11 +-
 core/modules/menu_link/menu_link.install      |  16 +-
 core/modules/node/node.module                 |  24 --
 core/modules/path/path.module                 |  15 -
 core/modules/picture/picture.module           |  15 -
 core/modules/search/search.module             |  20 -
 core/modules/shortcut/shortcut.module         |  14 -
 core/modules/simpletest/simpletest.module     |  15 -
 core/modules/statistics/statistics.module     |  15 +-
 .../lib/Drupal/system/SystemManager.php       |  13 +-
 .../system/Tests/Menu/BreadcrumbTest.php      |   5 +-
 .../Drupal/system/Tests/Menu/LinksTest.php    |   7 +-
 .../system/Tests/Menu/MenuRouterTest.php      | 108 +++++-
 .../Drupal/system/Tests/Menu/TrailTest.php    | 136 +++++++
 .../system/Tests/Menu/TreeOutputTest.php      |  14 +-
 core/modules/system/system.admin.inc          |   3 +-
 core/modules/system/system.api.php            |  66 ----
 core/modules/system/system.module             | 216 +----------
 core/modules/system/system.routing.yml        |   3 -
 .../Controller/MenuTestController.php         |  17 -
 .../tests/modules/menu_test/menu_test.module  | 145 +------
 .../modules/menu_test/menu_test.routing.yml   |  15 +-
 .../Controller/TaxonomyController.php         |  13 -
 core/modules/taxonomy/taxonomy.module         |  14 -
 core/modules/taxonomy/taxonomy.routing.yml    |   1 -
 .../toolbar/Tests/ToolbarAdminMenuTest.php    |   1 -
 core/modules/toolbar/toolbar.module           |   2 +-
 core/modules/tracker/tracker.module           |  12 -
 core/modules/update/update.module             |  15 -
 .../Drupal/user/Access/LoginStatusCheck.php   |   2 +-
 .../user/Tests/UserAccountLinksTests.php      |   7 +-
 core/modules/user/user.module                 |  64 +---
 .../views/display/DisplayPluginBase.php       |  15 -
 .../Plugin/views/display/PathPluginBase.php   |  64 ----
 .../views/lib/Drupal/views/ViewExecutable.php |  24 --
 core/modules/views/views.module               |  17 -
 core/modules/views_ui/views_ui.module         |  25 --
 core/themes/seven/seven.theme                 |   2 +-
 74 files changed, 679 insertions(+), 1523 deletions(-)
 create mode 100644 core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php

diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 0a7745655747..c4b0cb4aac17 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -1665,7 +1665,14 @@ function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
  *   The current page's title.
  */
 function drupal_get_title() {
-  return drupal_set_title() ?: '';
+  $title = drupal_set_title();
+
+  // During a bootstrap, menu.inc is not included and thus we cannot provide a title.
+  if (!isset($title) && function_exists('menu_get_active_title')) {
+    $title = String::checkPlain(menu_get_active_title());
+  }
+
+  return $title;
 }
 
 /**
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 061f571a836d..ca3ed515d6a0 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -656,7 +656,7 @@ function _menu_item_localize(&$item, $map, $link_translate = FALSE) {
   // itself; can't localize.
   // If we are translating a router item (tabs, page, breadcrumb), then we
   // can always use the information from the router item.
-  if (!$link_translate || !isset($item['link_title']) || ($item['title'] == $item['link_title'])) {
+  if (!$link_translate || ($item['title'] == $item['link_title'])) {
     // t() is a special case. Since it is used very close to all the time,
     // we handle it directly instead of using indirect, slower methods.
     if ($title_callback == 't') {
@@ -760,10 +760,7 @@ function _menu_translate(&$router_item, $map, $to_arg = FALSE) {
     $router_item['access'] = FALSE;
     return FALSE;
   }
-  // Avoid notices until we remove this function.
-  // @see https://drupal.org/node/2107533
-  $tab_root_map = array();
-  $tab_parent_map = array();
+
   // Generate the link path for the page request or local tasks.
   $link_map = explode('/', $router_item['path']);
   if (isset($router_item['tab_root'])) {
@@ -858,51 +855,95 @@ function menu_tail_load($arg, &$map, $index) {
 }
 
 /**
- * Provides menu link unserializing, access control, and argument handling.
+ * Provides menu link access control, translation, and argument handling.
  *
  * This function is similar to _menu_translate(), but it also does
  * link-specific preparation (such as always calling to_arg() functions).
  *
- * @param array $item
- *   The passed in item has the following keys:
- *   - access: (optional) Becomes TRUE if the item is accessible, FALSE
- *     otherwise. If the key is not set, the access manager is used to
- *     determine the access.
- *   - options: (required) Is unserialized and copied to $item['localized_options'].
- *   - link_title: (required) The title of the menu link.
- *   - route_name: (required) The route name of the menu link.
- *   - route_parameters: (required) The unserialized route parameters of the menu link.
- *   The passed in item is changed by the following keys:
- *   - href: The actual path to the link. This path is generated from the
- *     link_path of the menu link entity.
- *   - title: The title of the link. This title is generated from the
- *     link_title of the menu link entity.
- */
-function _menu_link_translate(&$item) {
+ * @param $item
+ *   A menu link.
+ * @param $translate
+ *   (optional) Whether to try to translate a link containing dynamic path
+ *   argument placeholders (%) based on the menu router item of the current
+ *   path. Defaults to FALSE. Internally used for breadcrumbs.
+ *
+ * @return
+ *   Returns the map of path arguments with objects loaded as defined in the
+ *   $item['load_functions'].
+ *   $item['access'] becomes TRUE if the item is accessible, FALSE otherwise.
+ *   $item['href'] is generated from link_path, possibly by to_arg functions.
+ *   $item['title'] is generated from link_title, and may be localized.
+ *   $item['options'] is unserialized; it is also changed within the call here
+ *   to $item['localized_options'] by _menu_item_localize().
+ */
+function _menu_link_translate(&$item, $translate = FALSE) {
   if (!is_array($item['options'])) {
-    $item['options'] = (array) unserialize($item['options']);
+    $item['options'] = unserialize($item['options']);
   }
-  $item['localized_options'] = $item['options'];
-  $item['title'] = $item['link_title'];
-  if ($item['external'] || empty($item['route_name'])) {
+  if ($item['external']) {
     $item['access'] = 1;
+    $map = array();
     $item['href'] = $item['link_path'];
-    $item['route_parameters'] = array();
-    // Set to NULL so that drupal_pre_render_link() is certain to skip it.
-    $item['route_name'] = NULL;
+    $item['title'] = $item['link_title'];
+    $item['localized_options'] = $item['options'];
   }
   else {
-    $item['href'] = NULL;
-    if (!is_array($item['route_parameters'])) {
-      $item['route_parameters'] = (array) unserialize($item['route_parameters']);
+    // Complete the path of the menu link with elements from the current path,
+    // if it contains dynamic placeholders (%).
+    $map = explode('/', $item['link_path']);
+    if (strpos($item['link_path'], '%') !== FALSE) {
+      // Invoke registered to_arg callbacks.
+      if (!empty($item['to_arg_functions'])) {
+        _menu_link_map_translate($map, $item['to_arg_functions']);
+      }
+      // Or try to derive the path argument map from the current router item,
+      // if this $item's path is within the router item's path. This means
+      // that if we are on the current path 'foo/%/bar/%/baz', then
+      // menu_get_item() will have translated the menu router item for the
+      // current path, and we can take over the argument map for a link like
+      // 'foo/%/bar'. This inheritance is only valid for breadcrumb links.
+      // @see _menu_tree_check_access()
+      elseif ($translate && ($current_router_item = menu_get_item())) {
+        // If $translate is TRUE, then this link is in the active trail.
+        // Only translate paths within the current path.
+        if (strpos($current_router_item['path'], $item['link_path']) === 0) {
+          $count = count($map);
+          $map = array_slice($current_router_item['original_map'], 0, $count);
+          $item['original_map'] = $map;
+          if (isset($current_router_item['map'])) {
+            $item['map'] = array_slice($current_router_item['map'], 0, $count);
+          }
+          // Reset access to check it (for the first time).
+          unset($item['access']);
+        }
+      }
+    }
+    $item['href'] = implode('/', $map);
+
+    // Skip links containing untranslated arguments.
+    if (strpos($item['href'], '%') !== FALSE) {
+      $item['access'] = FALSE;
+      return FALSE;
     }
     // menu_tree_check_access() may set this ahead of time for links to nodes.
     if (!isset($item['access'])) {
-      $item['access'] = \Drupal::getContainer()->get('access_manager')->checkNamedRoute($item['route_name'], $item['route_parameters'], \Drupal::currentUser());
+      if ($route = $item->getRoute()) {
+        $item['access'] = menu_item_route_access($route, $item['href'], $map);
+      }
+      elseif (!empty($item['load_functions']) && !_menu_load_objects($item, $map)) {
+        // An error occurred loading an object.
+        $item['access'] = FALSE;
+        return FALSE;
+      }
+      // Apply the access check defined in hook_menu() if there is not route
+      // defined.
+      else {
+        _menu_check_access($item, $map);
+      }
     }
     // For performance, don't localize a link the user can't access.
     if ($item['access']) {
-      _menu_item_localize($item, array(), TRUE);
+      _menu_item_localize($item, $map, TRUE);
     }
   }
 
@@ -912,6 +953,8 @@ function _menu_link_translate(&$item) {
   if (!empty($item['options']['alter'])) {
     drupal_alter('translated_menu_link', $item, $map);
   }
+
+  return $map;
 }
 
 /**
@@ -923,19 +966,17 @@ function _menu_link_translate(&$item) {
  *   Menu path as returned by $item['href'] of menu_get_item().
  * @param array $map
  *   An array of path arguments; for example, array('node', '5').
- * @param \Symfony\Component\HttpFoundation\Request $request
- *   The current request object, used to find the current route.
  *
  * @return bool
  *   TRUE if the user has access or FALSE if the user should be presented
  *   with access denied.
  *
+ * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException
+ *   If the system path in $href does not match the $route.
  */
-function menu_item_route_access(Route $route, $href, &$map, Request $request = NULL) {
-  if (!isset($request)) {
-    $request = RequestHelper::duplicate(\Drupal::request(), '/' . $href);
-    $request->attributes->set('_system_path', $href);
-  }
+function menu_item_route_access(Route $route, $href, &$map) {
+  $request = RequestHelper::duplicate(\Drupal::request(), '/' . $href);
+  $request->attributes->set('_system_path', $href);
   // Attempt to match this path to provide a fully built request to the
   // access checker.
   try {
@@ -1087,14 +1128,19 @@ function menu_tree_output($tree) {
       $class[] = 'active-trail';
       $data['link']['localized_options']['attributes']['class'][] = 'active-trail';
     }
+    // Normally, l() compares the href of every link with the current path and
+    // sets the active class accordingly. But local tasks do not appear in menu
+    // trees, so if the current path is a local task, and this link is its
+    // tab root, then we have to set the class manually.
+    if ($data['link']['href'] == $router_item['tab_root_href'] && $data['link']['href'] != current_path()) {
+      $data['link']['localized_options']['attributes']['class'][] = 'active';
+    }
 
     // Allow menu-specific theme overrides.
     $element['#theme'] = 'menu_link__' . strtr($data['link']['menu_name'], '-', '_');
     $element['#attributes']['class'] = $class;
     $element['#title'] = $data['link']['title'];
-    // @todo Use route name and parameters to generate the link path, unless
-    //    it is external.
-    $element['#href'] = $data['link']['link_path'];
+    $element['#href'] = $data['link']['href'];
     $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array();
     $element['#below'] = $data['below'] ? menu_tree_output($data['below']) : $data['below'];
     $element['#original_link'] = $data['link'];
@@ -1831,7 +1877,7 @@ function menu_navigation_links($menu_name, $level = 0) {
     if (!$item['link']['hidden']) {
       $class = '';
       $l = $item['link']['localized_options'];
-      $l['href'] = $item['link']['link_path'];
+      $l['href'] = $item['link']['href'];
       $l['title'] = $item['link']['title'];
       if ($item['link']['in_active_trail']) {
         $class = ' active-trail';
@@ -2258,10 +2304,10 @@ function menu_set_active_trail($new_trail = NULL) {
     // Try to retrieve a menu link corresponding to the current path. If more
     // than one exists, the link from the most preferred menu is returned.
     $preferred_link = menu_link_get_preferred();
+    $current_item = menu_get_item();
 
     // There is a link for the current path.
     if ($preferred_link) {
-       _menu_link_translate($preferred_link);
       // Pass TRUE for $only_active_trail to make menu_tree_page_data() build
       // a stripped down menu tree containing the active trail only, in case
       // the given menu has not been built in this request yet.
@@ -2270,6 +2316,7 @@ function menu_set_active_trail($new_trail = NULL) {
     }
     // There is no link for the current path.
     else {
+      $preferred_link = $current_item;
       $curr = FALSE;
     }
 
@@ -2287,7 +2334,7 @@ function menu_set_active_trail($new_trail = NULL) {
           // @see _menu_tree_check_access()
           // @see _menu_link_translate()
           if (strpos($link['href'], '%') !== FALSE) {
-            _menu_link_translate($link);
+            _menu_link_translate($link, TRUE);
           }
           if ($link['access']) {
             $trail[] = $link;
@@ -2342,11 +2389,20 @@ function menu_link_get_preferred($path = NULL, $selected_menu = NULL) {
     // which are ordered by priority (translated hrefs are preferred over
     // untranslated paths). Afterwards, the most relevant path is picked from
     // the menus, ordered by menu preference.
+    $item = menu_get_item($path);
     $path_candidates = array();
     // 1. The current item href.
-    // @todo simplify this code and convert to using route names.
-    // @see https://drupal.org/node/2154949
-    $path_candidates[$path] = $path;
+    $path_candidates[$item['href']] = $item['href'];
+    // 2. The tab root href of the current item (if any).
+    if ($item['tab_parent'] && ($tab_root = menu_get_item($item['tab_root_href']))) {
+      $path_candidates[$tab_root['href']] = $tab_root['href'];
+    }
+    // 3. The current item path (with wildcards).
+    $path_candidates[$item['path']] = $item['path'];
+    // 4. The tab root path of the current item (if any).
+    if (!empty($tab_root)) {
+      $path_candidates[$tab_root['path']] = $tab_root['path'];
+    }
 
     // Retrieve a list of menu names, ordered by preference.
     $menu_names = menu_get_active_menu_names();
@@ -2412,6 +2468,19 @@ function menu_get_active_trail() {
   return menu_set_active_trail();
 }
 
+/**
+ * Gets the title of the current page, as determined by the active trail.
+ */
+function menu_get_active_title() {
+  $active_trail = menu_get_active_trail();
+
+  foreach (array_reverse($active_trail) as $item) {
+    if (!(bool) ($item['type'] & MENU_IS_LOCAL_TASK)) {
+      return $item['title'];
+    }
+  }
+}
+
 /**
  * Clears the cached cached data for a single named menu.
  */
@@ -2469,7 +2538,7 @@ function menu_router_rebuild() {
 
   try {
     list($menu) = menu_router_build(TRUE);
-    menu_link_rebuild_defaults();
+    _menu_navigation_links_rebuild($menu);
     // Clear the menu, page and block caches.
     menu_cache_clear_all();
     _menu_clear_page_cache();
@@ -2579,147 +2648,110 @@ function menu_get_router() {
 }
 
 /**
- * Saves menu links recursively for menu_links_rebuild_defaults().
- */
-function _menu_link_save_recursive($controller, $machine_name, &$children, &$links) {
-  $menu_link = $links[$machine_name];
-  if ($menu_link->isNew() || !$menu_link->customized) {
-    if (!isset($menu_link->plid) && !empty($menu_link->parent) && !empty($links[$menu_link->parent])) {
-      $parent = $links[$menu_link->parent];
-
-      if (empty($menu_link->menu_name) || $parent->menu_name == $menu_link->menu_name) {
-        $menu_link->plid = $parent->id();
-        $menu_link->menu_name = $parent->menu_name;
-      }
-    }
-    $controller->save($menu_link);
-  }
-  if (!empty($children[$machine_name])) {
-    foreach ($children[$machine_name] as $next_name) {
-      _menu_link_save_recursive($controller, $next_name, $children, $links);
-    }
-  }
-  // Remove processed link names so we can find stragglers.
-  unset($children[$machine_name]);
-}
-
-/**
- * Gets all default menu link definitions.
+ * Builds menu links for the items in the menu router.
  *
- * @return array
- *   An array of default menu links.
- */
-function menu_link_get_defaults() {
-  $module_handler = \Drupal::moduleHandler();
-  $all_links = $module_handler->invokeAll('menu_link_defaults');
-  // Fill in the machine name from the array key.
-  foreach ($all_links as $machine_name => &$link) {
-    $link['machine_name'] = $machine_name;
-  }
-  $module_handler->alter('menu_link_defaults', $all_links);
-  return $all_links;
-}
-
-/**
- * Builds menu links for the items returned from hook_menu_link_defaults().
+ * @todo This function should be removed/refactored.
  */
-function menu_link_rebuild_defaults() {
-  $module_handler = \Drupal::moduleHandler();
-  if (!$module_handler->moduleExists('menu_link')) {
+function _menu_navigation_links_rebuild($menu) {
+  if (!\Drupal::moduleHandler()->moduleExists('menu_link')) {
     // The Menu link module may not be available during install, so rebuild
     // when possible.
     return;
   }
-  /** @var \Drupal\menu_link\MenuLinkStorageControllerInterface $menu_link_storage */
-  $menu_link_storage = \Drupal::entityManager()
+  $menu_link_controller = \Drupal::entityManager()
     ->getStorageController('menu_link');
-  $links = array();
-  $children = array();
-  $top_links = array();
-  $all_links = menu_link_get_defaults();
-  if ($all_links) {
-    foreach ($all_links as $machine_name => $link) {
+
+  // Add normal and suggested items as links.
+  $router_items = array();
+  foreach ($menu as $path => $router_item) {
+    if ($router_item['_visible']) {
+      $router_items[$path] = $router_item;
+      $sort[$path] = $router_item['_number_parts'];
+    }
+  }
+  if ($router_items) {
+    // Keep an array of processed menu links, to allow
+    // Drupal\menu_link\MenuLinkStorageController::save() to check this for
+    // parents instead of querying the database.
+    $parent_candidates = array();
+    // Make sure no child comes before its parent.
+    array_multisort($sort, SORT_NUMERIC, $router_items);
+
+    foreach ($router_items as $key => $router_item) {
       // For performance reasons, do a straight query now and convert to a menu
       // link entity later.
       // @todo revisit before release.
       $existing_item = db_select('menu_links')
         ->fields('menu_links')
-        ->condition('machine_name', $machine_name)
+        ->condition('link_path', $router_item['path'])
         ->condition('module', 'system')
-        ->execute()->fetchObject();
+        ->execute()->fetchAll();
       if ($existing_item) {
+        $existing_item = reset($existing_item);
         $existing_item->options = unserialize($existing_item->options);
         $existing_item->route_parameters = unserialize($existing_item->route_parameters);
-        $link['mlid'] = $existing_item->mlid;
-        $link['plid'] = $existing_item->plid;
-        $link['uuid'] = $existing_item->uuid;
-        $link['customized'] = $existing_item->customized;
-        $link['updated'] = $existing_item->updated;
-        $menu_link = $menu_link_storage->createFromDefaultLink($link);
 
-        // Convert the existing item to a typed object.
-        /** @var \Drupal\menu_link\MenuLinkInterface $existing_item */
-        $existing_item = $menu_link_storage->create(get_object_vars($existing_item));
-
-        if (!$existing_item->customized) {
-          // A change in hook_menu_link_defaults() may move the link to a
-          // different menu.
-          if (!empty($link['menu_name']) && ($link['menu_name'] != $existing_item->menu_name)) {
-            $menu_link->plid = NULL;
-            $menu_link->menu_name = $link['menu_name'];
-          }
-
-          $menu_link->original = $existing_item;
+        $router_item['mlid'] = $existing_item->mlid;
+        $router_item['uuid'] = $existing_item->uuid;
+        // A change in hook_menu may move the link to a different menu
+        if (empty($router_item['menu_name']) || ($router_item['menu_name'] == $existing_item->menu_name)) {
+          $router_item['menu_name'] = $existing_item->menu_name;
+          $router_item['plid'] = $existing_item->plid;
+        }
+        else {
+          // It moved to a new menu.
+          // Let Drupal\menu_link\MenuLinkStorageController::save() try to find
+          // a new parent based on the path.
+          unset($router_item['plid']);
         }
+        $router_item['has_children'] = $existing_item->has_children;
+        $router_item['updated'] = $existing_item->updated;
+
+        // Convert the existing item to a typed object.
+        $existing_item = $menu_link_controller->create(get_object_vars($existing_item));
       }
       else {
-        if (empty($link['route_name']) && empty($link['link_path'])) {
-          watchdog('error', 'Menu_link %machine_name does neither provide a route_name nor a lin_path, so it got skipped.');
-          continue;
-        }
-        $menu_link = $menu_link_storage->createFromDefaultLink($link);
+        $existing_item = NULL;
       }
-      if (!empty($link['parent'])) {
-        $children[$link['parent']][$machine_name] = $machine_name;
-        $menu_link->parent = $link['parent'];
-        if (empty($link['menu_name'])) {
-          // Unset the default menu name so it's populated from the parent.
-          unset($menu_link->menu_name);
-        }
+
+      if ($existing_item && $existing_item->customized) {
+        $parent_candidates[$existing_item->mlid] = $existing_item;
       }
       else {
-        // A top level link - we need them to root our tree.
-        $top_links[$machine_name] = $machine_name;
-        $menu_link->plid = 0;
+        $menu_link = MenuLink::buildFromRouterItem($router_item);
+        $menu_link->original = $existing_item;
+        $menu_link->parentCandidates = $parent_candidates;
+        $menu_link_controller->save($menu_link);
+        $parent_candidates[$menu_link->id()] = $menu_link;
+        unset($router_items[$key]);
       }
-      $links[$machine_name] = $menu_link;
     }
   }
-  foreach ($top_links as $machine_name) {
-    _menu_link_save_recursive($menu_link_storage, $machine_name, $children, $links);
-  }
-  // Handle any children we didn't find starting from top-level links.
-  foreach ($children as $orphan_links) {
-    foreach ($orphan_links as $machine_name) {
-      // Force it to the top level.
-      $links[$machine_name]->plid = 0;
-      _menu_link_save_recursive($menu_link_storage, $machine_name, $children, $links);
+
+  $paths = array_keys($menu);
+  // Updated and customized items whose router paths are gone need new ones.
+  $menu_links = $menu_link_controller->loadUpdatedCustomized($paths);
+  foreach ($menu_links as $menu_link) {
+    $router_path = _menu_find_router_path($menu_link->link_path);
+    if (!empty($router_path) && ($router_path != $menu_link->router_path || $menu_link->updated)) {
+      // If the router path and the link path matches, it's surely a working
+      // item, so we clear the updated flag.
+      $updated = $menu_link->updated && $router_path != $menu_link->link_path;
+
+      $menu_link->router_path = $router_path;
+      $menu_link->updated = (int) $updated;
+      $menu_link_controller->save($menu_link);
     }
   }
 
   // Find any item whose router path does not exist any more.
-  if ($all_links) {
-    $query = \Drupal::entityQuery('menu_link')
-      ->condition('machine_name', array_keys($all_links), 'NOT IN')
-      ->condition('external', 0)
-      ->condition('updated', 0)
-      ->condition('customized', 0)
-      ->sort('depth', 'DESC');
-    $result = $query->execute();
-  }
-  else {
-    $result = array();
-  }
+  $query = \Drupal::entityQuery('menu_link')
+    ->condition('router_path', $paths, 'NOT IN')
+    ->condition('external', 0)
+    ->condition('updated', 0)
+    ->condition('customized', 0)
+    ->sort('depth', 'DESC');
+  $result = $query->execute();
 
   // Remove all such items. Starting from those with the greatest depth will
   // minimize the amount of re-parenting done by the menu link controller.
@@ -2951,7 +2983,7 @@ function _menu_router_build($callbacks, $save = FALSE) {
           // previous iteration assigned one already), try to find the menu name
           // of the parent item in the currently stored menu links.
           if (!isset($parent['menu_name'])) {
-            $menu_name = db_query("SELECT menu_name FROM {menu_links} WHERE link_path = :link_path AND module = 'system'", array(':link_path' => $parent_path))->fetchField();
+            $menu_name = db_query("SELECT menu_name FROM {menu_links} WHERE router_path = :router_path AND module = 'system'", array(':router_path' => $parent_path))->fetchField();
             if ($menu_name) {
               $parent['menu_name'] = $menu_name;
             }
diff --git a/core/includes/path.inc b/core/includes/path.inc
index 533dcd9a7b67..975dbe8b6327 100644
--- a/core/includes/path.inc
+++ b/core/includes/path.inc
@@ -5,7 +5,6 @@
  * Functions to handle paths in Drupal.
  */
 
-use Drupal\Core\Routing\RequestHelper;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -196,23 +195,9 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) {
   global $menu_admin;
   // We indicate that a menu administrator is running the menu access check.
   $menu_admin = TRUE;
-  /** @var $route_provider \Drupal\Core\Routing\RouteProviderInterface */
-  $route_provider = \Drupal::service('router.route_provider');
-
-  if ($dynamic_allowed && preg_match('/\/\%/', $path)) {
-    $router_path = '/' . str_replace('%', '{}', $path);
-  }
-  else {
-    $router_path = $path;
-  }
-
   if ($path == '<front>' || url_is_external($path)) {
     $item = array('access' => TRUE);
   }
-  elseif (($collection = $route_provider->getRoutesByPattern('/' . $router_path)) && $collection->count() > 0) {
-    $routes = $collection->all();
-    $route_name = key($routes);
-  }
   elseif ($dynamic_allowed && preg_match('/\/\%/', $path)) {
     // Path is dynamic (ie 'user/%'), so check directly against menu_router table.
     if ($item = db_query("SELECT * FROM {menu_router} where path = :path", array(':path' => $path))->fetchAssoc()) {
@@ -221,22 +206,16 @@ function drupal_valid_path($path, $dynamic_allowed = FALSE) {
       $item['external']   = FALSE;
       $item['options'] = '';
       _menu_link_translate($item);
-      $route_name = $item['route_name'];
     }
   }
   else {
     $item = menu_get_item($path);
-    $route_name = $item['route_name'];
   }
   // Check the new routing system.
-  if (!empty($route_name)) {
+  if (!empty($item['route_name'])) {
     $map = array();
-    $route = \Drupal::service('router.route_provider')->getRouteByName($route_name);
-    $request = RequestHelper::duplicate(\Drupal::request(), '/' . $path);
-    $request->attributes->set('_system_path', $path);
-    $request->attributes->set('_menu_admin', TRUE);
-
-    $item['access'] = menu_item_route_access($route, $path, $map, $request);
+    $route = \Drupal::service('router.route_provider')->getRouteByName($item['route_name']);
+    $item['access'] = menu_item_route_access($route, $path, $map);
   }
   $menu_admin = FALSE;
   return $item && $item['access'];
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index f7af4331c990..d14f7f8874c6 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -2137,14 +2137,6 @@ function template_preprocess_html(&$variables) {
       'name' => String::checkPlain($site_config->get('name')),
     );
   }
-  // @todo Remove once views is not bypassing the view subscriber anymore.
-  //   @see http://drupal.org/node/2068471
-  elseif (drupal_is_front_page()) {
-    $head_title = array(
-      'title' => t('Home'),
-      'name' => String::checkPlain($site_config->get('name')),
-    );
-  }
   else {
     $head_title = array('name' => String::checkPlain($site_config->get('name')));
     if ($site_config->get('slogan')) {
diff --git a/core/modules/action/action.module b/core/modules/action/action.module
index 6c4b60187694..29237533a766 100644
--- a/core/modules/action/action.module
+++ b/core/modules/action/action.module
@@ -56,20 +56,6 @@ function action_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function action_menu_link_defaults() {
-  $links['action.admin.actions'] = array(
-    'link_title' => 'Actions',
-    'description' => 'Manage the actions defined for your site.',
-    'route_name' => 'action.admin',
-    'parent' => 'system.admin.config.system',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_entity_info().
  */
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 39b214e30e18..6fb629f7045d 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -127,30 +127,6 @@ function aggregator_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function aggregator_menu_link_defaults() {
-  $links = array();
-  $links['aggregator.admin_overview'] = array(
-    'link_title' => 'Feed aggregator',
-    'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.",
-    'route_name' => 'aggregator.admin_overview',
-    'weight' => 10,
-  );
-  $links['aggregator'] = array(
-    'link_title' => 'Feed aggregator',
-    'weight' => 5,
-    'route_name' => 'aggregator.page_last',
-  );
-  $links['aggregator.sources'] = array(
-    'link_title' => 'Sources',
-    'route_name' => 'aggregator.sources',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/ban/ban.module b/core/modules/ban/ban.module
index 7d09e7c64ffe..0dce1d97f2e9 100644
--- a/core/modules/ban/ban.module
+++ b/core/modules/ban/ban.module
@@ -53,18 +53,3 @@ function ban_menu() {
   );
   return $items;
 }
-
-/**
- * Implements hook_menu_link_defaults().
- */
-function ban_menu_link_defaults() {
-  $links['ban.admin.config.people'] = array(
-    'link_title' => 'IP address bans',
-    'description' => 'Manage banned IP addresses.',
-    'route_name' => 'ban.admin_page',
-    'weight' => 10,
-    'parent' => 'user.admin.config.people',
-  );
-
-  return $links;
-}
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index ae8ced672b29..dd3aa6f6b775 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -116,20 +116,6 @@ function block_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function block_menu_link_defaults() {
-  $links['block.admin.structure'] = array(
-    'link_title' => 'Blocks',
-    'parent' => 'system.admin.structure',
-    'description' => 'Configure what block content appears in your site\'s sidebars and other regions.',
-    'route_name' => 'block.admin_display',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_page_build().
  *
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 9bc7b72da217..0457488e1485 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -180,26 +180,6 @@ function book_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function book_menu_link_defaults() {
-  $links['book.admin.outlines'] = array(
-    'link_title' => 'Books',
-    'description' => "Manage your site's book outlines.",
-    'parent' => 'system.admin.structure',
-    'route_name' => 'book.admin',
-  );
-  $links['book'] = array(
-    'link_title' => 'Books',
-    'route_name' => 'book.render',
-    // @todo what to do about MENU_SUGGESTED_ITEM, maybe specify no menu_name?
-    'type' => MENU_SUGGESTED_ITEM,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_admin_paths().
  */
@@ -669,7 +649,7 @@ function template_preprocess_book_navigation(&$variables) {
     $build = array();
 
     if ($prev = book_prev($book_link)) {
-      $prev_href = url($prev['link_path']);
+      $prev_href = url($prev['href']);
       $build['#attached']['drupal_add_html_head_link'][][] = array(
         'rel' => 'prev',
         'href' => $prev_href,
@@ -689,7 +669,7 @@ function template_preprocess_book_navigation(&$variables) {
     }
 
     if ($next = book_next($book_link)) {
-      $next_href = url($next['link_path']);
+      $next_href = url($next['href']);
       $build['#attached']['drupal_add_html_head_link'][][] = array(
         'rel' => 'next',
         'href' => $next_href,
diff --git a/core/modules/book/lib/Drupal/book/BookManager.php b/core/modules/book/lib/Drupal/book/BookManager.php
index 01ae28361123..9b415a8d017f 100644
--- a/core/modules/book/lib/Drupal/book/BookManager.php
+++ b/core/modules/book/lib/Drupal/book/BookManager.php
@@ -131,7 +131,7 @@ public function getLinkDefaults($nid) {
       'menu_name' => '',
       'nid' => $nid,
       'bid' => 0,
-      'link_path' => 'node/%',
+      'router_path' => 'node/%',
       'plid' => 0,
       'mlid' => 0,
       'has_children' => 0,
@@ -211,7 +211,7 @@ public function addFormElements(array $form, array &$form_state, NodeInterface $
       ),
       '#tree' => TRUE,
     );
-    foreach (array('menu_name', 'mlid', 'nid', 'link_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) {
+    foreach (array('menu_name', 'mlid', 'nid', 'router_path', 'has_children', 'options', 'module', 'original_bid', 'parent_depth_limit') as $key) {
       $form['book'][$key] = array(
         '#type' => 'value',
         '#value' => $node->book[$key],
@@ -660,7 +660,7 @@ public function bookTreeOutput(array $tree) {
       $element['#theme'] = 'menu_link__' . strtr($data['link']['menu_name'], '-', '_');
       $element['#attributes']['class'] = $class;
       $element['#title'] = $data['link']['title'];
-      $element['#href'] = $data['link']['link_path'];
+      $element['#href'] = $data['link']['href'];
       $element['#localized_options'] = !empty($data['link']['localized_options']) ? $data['link']['localized_options'] : array();
       $element['#below'] = $data['below'] ? $this->bookTreeOutput($data['below']) : $data['below'];
       $element['#original_link'] = $data['link'];
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index f02c70071f4e..c5b8141ff070 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -196,20 +196,6 @@ function comment_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function comment_menu_link_defaults() {
-  $links['comment.admin.content'] = array(
-    'link_title' => 'Comments',
-    'route_name' => 'comment.admin',
-    'parent' => 'node.admin.content',
-    'description' => 'List and edit site comments and the comment approval queue.',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_menu_alter().
  */
diff --git a/core/modules/config/config.module b/core/modules/config/config.module
index 3cb71e6bbdac..76deec4df1e8 100644
--- a/core/modules/config/config.module
+++ b/core/modules/config/config.module
@@ -69,17 +69,3 @@ function config_menu() {
 
   return $items;
 }
-
-/**
- * Implements hook_menu_link_defaults().
- */
-function config_menu_link_defaults() {
-  $links['config.admin.management'] = array(
-    'link_title' => 'Configuration management',
-    'description' => 'Import, export, or synchronize your site configuration.',
-    'route_name' => 'config.sync',
-    'parent' => 'system.admin.config.development',
-  );
-
-  return $links;
-}
diff --git a/core/modules/config/tests/config_test/config_test.routing.yml b/core/modules/config/tests/config_test/config_test.routing.yml
index b3b216f41eeb..7af61cd8ed1a 100644
--- a/core/modules/config/tests/config_test/config_test.routing.yml
+++ b/core/modules/config/tests/config_test/config_test.routing.yml
@@ -2,7 +2,6 @@ config_test.list_page:
   path: '/admin/structure/config_test'
   defaults:
     _entity_list: 'config_test'
-    _title: 'Test configuration'
   requirements:
     _access: 'TRUE'
 
diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module
index da894479438c..8e91d303d87a 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -45,21 +45,6 @@ function config_translation_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function config_translation_menu_link_defaults() {
-  $links['config_translation.admin.config-translation'] = array(
-    'link_title' => 'Configuration translation',
-    'parent' => 'system.admin.config.regional',
-    'description' => 'Translate the configuration.',
-    'route_name' => 'config_translation.mapper_list',
-    'weight' => 30,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 779b3da0fc89..9b06fd991bc1 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -79,26 +79,6 @@ function contact_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function contact_menu_link_defaults() {
-  $links['contact.admin.categories'] = array(
-    'link_title' => 'Contact form categories',
-    'parent' => 'system.admin.structure',
-    'description' => 'Create a system contact form and set up categories for the form to use.',
-    'route_name' => 'contact.category_list',
-  );
-
-  $links['contact'] = array(
-    'link_title' => 'Contact',
-    'route_name' => 'contact.site_page',
-    'menu_name' => 'footer',
-    'type' => MENU_SUGGESTED_ITEM,
-  );
-  return $links;
-}
-
 /**
  * Implements hook_entity_bundle_info().
  */
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index b210091c2e4c..a6f7987ef35f 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -293,15 +293,6 @@ function content_translation_menu_alter(array &$items) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults_alter().
- */
-function content_translation_menu_link_defaults_alter(array &$links) {
-  // Clarify where translation settings are located.
-  $items['admin.config.regional.language.content_settings_page']['link_title'] = 'Content language and translation';
-  $items['admin.config.regional.language.content_settings_page']['description'] = 'Configure language and translation support for content.';
-}
-
 /**
  * Convert an entity canonical link to a router path.
  *
diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module
index 4060d63981b4..0f269b07728b 100644
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -72,42 +72,6 @@ function dblog_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function dblog_menu_link_defaults() {
-  $links['dblog.admin.reports.dblog'] = array(
-    'link_title' => 'Recent log messages',
-    'parent' => 'system.admin.reports',
-    'description' => 'View events that have recently been logged.',
-    'route_name' => 'dblog.overview',
-    'weight' => -1,
-  );
-  $links['dblog.admin.reports.page-not-found'] = array(
-    'link_title' => "Top 'page not found' errors",
-    'route_name' => 'dblog.page_not_found',
-    'parent' => 'system.admin.reports',
-    'description' => "View 'page not found' errors (404s).",
-  );
-  $links['dblog.admin.reports.access-denied'] = array(
-    'link_title' => "Top 'access denied' errors",
-    'route_name' => 'dblog.access_denied',
-    'description' => "View 'access denied' errors (403s).",
-    'parent' => 'system.admin.reports',
-  );
-
-  if (\Drupal::moduleHandler()->moduleExists('search')) {
-    $links['dblog.admin.reports.search'] = array(
-      'link_title' => 'Top search phrases',
-      'route_name' => 'dblog.search',
-      'description' => 'View most popular search phrases.',
-      'parent' => 'system.admin.reports',
-    );
-  }
-
-  return $links;
-}
-
 /**
  * Implements hook_page_build().
  */
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 3d9ec7c97c47..4d91c300273c 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -46,14 +46,6 @@ function editor_menu_alter(&$items) {
   $items['admin/config/content/formats']['description'] = 'Configure how user-contributed content is filtered and formatted, as well as the text editor user interface (WYSIWYGs or toolbars).';
 }
 
-/**
- * Implements hook_menu_link_defaults_alter().
- */
-function editor_menu_link_defaults_alter(array &$links) {
-  $links['admin.config.content.formats']['link_title'] = 'Text formats and editors';
-  $links['admin.config.content.formats']['description'] = 'Configure how user-contributed content is filtered and formatted, as well as the text editor user interface (WYSIWYGs or toolbars).';
-}
-
 /**
  * Implements hook_element_info().
  *
diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index 5b3f414c3041..73839207e282 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -95,36 +95,6 @@ function entity_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function entity_menu_link_defaults() {
-  $links['entity.admin.structure.display_modes'] = array(
-    'link_title' => 'Display modes',
-    'description' => 'Configure what displays are available for your content and forms.',
-    'route_name' => 'entity.display_mode',
-    'parent' => 'system.admin.structure',
-  );
-
-  // View modes.
-  $links['entity.admin.structure.display_modes.view'] = array(
-    'link_title' => 'View modes',
-    'description' => 'Manage custom view modes.',
-    'route_name' => 'entity.view_mode_list',
-    'parent' => 'entity.admin.structure.display_modes',
-  );
-
-  // Form modes.
-  $links['entity.admin.structure.display_modes.form'] = array(
-    'link_title' => 'Form modes',
-    'description' => 'Manage custom form modes.',
-    'route_name' => 'entity.form_mode_list',
-    'parent' => 'entity.admin.structure.display_modes',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_entity_bundle_rename().
  */
diff --git a/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php b/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php
index 4d3ddc396869..608c4cc113c4 100644
--- a/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php
+++ b/core/modules/entity/lib/Drupal/entity/Controller/EntityDisplayModeController.php
@@ -54,7 +54,7 @@ public function viewModeTypeSelection() {
       if ($entity_info->isFieldable() && $entity_info->hasViewBuilderClass()) {
         $entity_types[$entity_type] = array(
           'title' => $entity_info->getLabel(),
-          'link_path' => 'admin/structure/display-modes/view/add/' . $entity_type,
+          'href' => 'admin/structure/display-modes/view/add/' . $entity_type,
           'localized_options' => array(),
         );
       }
@@ -77,7 +77,7 @@ public function formModeTypeSelection() {
       if ($entity_info->isFieldable() && $entity_info->hasFormClasses()) {
         $entity_types[$entity_type] = array(
           'title' => $entity_info->getLabel(),
-          'link_path' => 'admin/structure/display-modes/form/add/' . $entity_type,
+          'href' => 'admin/structure/display-modes/form/add/' . $entity_type,
           'localized_options' => array(),
         );
       }
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index b8b2e12c893e..9461296b05dd 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -66,20 +66,6 @@ function field_ui_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function field_ui_menu_link_defaults() {
-  $links['field_ui.admin.reports.fields'] = array(
-    'link_title' => 'Field list',
-    'description' => 'Overview of fields on all entity types.',
-    'route_name' => 'field_ui.list',
-    'parent' => 'system.admin.reports',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 7a752d267bbc..ecd4e58fc8e4 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -149,26 +149,6 @@ function filter_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function filter_menu_link_defaults() {
-  $links['filter.tips'] = array(
-    'link_title' => 'Compose tips',
-    'type' => MENU_SUGGESTED_ITEM,
-    'route_name' => 'filter.tips_all',
-  );
-
-  $links['filter.admin.formats'] = array(
-    'link_title' => 'Text formats',
-    'parent' => 'system.admin.config.content',
-    'description' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.',
-    'route_name' => 'filter.admin_overview',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 679b52a338f5..011e37e90a13 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -131,24 +131,6 @@ function forum_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function forum_menu_link_defaults() {
-  $items['forum'] = array(
-    'link_title' => 'Forums',
-    'route_name' => 'forum.index',
-    'menu_name' => 'tools',
-  );
-  $items['forum.admin.overview'] = array(
-    'link_title' => 'Forums',
-    'parent' => 'system.admin.structure',
-    'description' => 'Control forum hierarchy settings.',
-    'route_name' => 'forum.overview',
-  );
-  return $items;
-}
-
 /**
  * Implements hook_menu_local_tasks().
  */
diff --git a/core/modules/forum/forum.routing.yml b/core/modules/forum/forum.routing.yml
index aa21336c2de6..3a0b6b932c85 100644
--- a/core/modules/forum/forum.routing.yml
+++ b/core/modules/forum/forum.routing.yml
@@ -26,7 +26,6 @@ forum.page:
   path: '/forum/{taxonomy_term}'
   defaults:
     _content: '\Drupal\forum\Controller\ForumController::forumPage'
-    _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle'
   requirements:
     _permission: 'access content'
 
diff --git a/core/modules/help/help.module b/core/modules/help/help.module
index 6d074056c340..9ccef10dcdaf 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -24,21 +24,6 @@ function help_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function help_menu_link_defaults() {
-  $links['help.main'] = array(
-    'link_title' => 'Help',
-    'description' => 'Reference for usage, configuration, and modules.',
-    'route_name' => 'help.main',
-    'weight' => 9,
-    'parent' => 'system.admin',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_help().
  */
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 3751b8cf73aa..13aef890f9de 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -119,20 +119,6 @@ function image_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function image_menu_link_defaults() {
-  $links['image.admin.media.image-styles'] = array(
-    'link_title' => 'Image styles',
-    'description' => 'Configure styles that can be used for resizing or adjusting images on display.',
-    'parent' => 'system.admin.config.media',
-    'route_name' => 'image.style_list',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_theme().
  */
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 37944095daa2..883ab881742e 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -90,29 +90,6 @@ function language_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function language_menu_link_defaults() {
-  // Base language management and configuration.
-  $links['language.admin.language.admin_overview'] = array(
-    'link_title' => 'Languages',
-    'description' => 'Configure languages for content and the user interface.',
-    'route_name' => 'language.admin_overview',
-    'parent' => 'system.admin.config.regional',
-  );
-  // Content language settings.
-  $links['language.admin.language.content_settings_page'] = array(
-    'link_title' => 'Content language',
-    'description' => 'Configure language support for content.',
-    'route_name' => 'language.content_settings_page',
-    'parent' => 'system.admin.config.regional',
-    'weight' => 10,
-  );
-
-  return $links;
-}
-
 /**
  * Editing or deleting locked languages should not be possible.
  *
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 8605e27543be..7a7574abacee 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -187,29 +187,6 @@ function locale_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function locale_menu_link_defaults() {
-  // Translation functionality.
-  $links['locale.admin.config.regional.translate'] = array(
-    'link_title' => 'User interface translation',
-    'description' => 'Translate the built-in user interface.',
-    'route_name' => 'locale.translate_page',
-    'parent' => 'system.admin.config.regional',
-    'weight' => 15,
-  );
-
-  $links['locale.admin.reports.translations'] = array(
-    'link_title' => 'Available translation updates',
-    'route_name' => 'locale.translate_status',
-    'description' => 'Get a status report about available interface translations for your installed modules and themes.',
-    'parent' => 'system.admin.reports',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php b/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php
index 57e45c1fa23e..35d074d8f426 100644
--- a/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php
+++ b/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php
@@ -105,8 +105,8 @@ public function submit(array $form, array &$form_state) {
     }
 
     // Reset all the menu links defined by the system via hook_menu().
-    // @todo Convert this to an EFQ.
-    $result = $this->connection->query("SELECT mlid FROM {menu_links} WHERE menu_name = :menu AND module = 'system' ORDER BY depth ASC", array(':menu' => $this->entity->id()), array('fetch' => \PDO::FETCH_ASSOC))->fetchCol();
+    // @todo Convert this to an EFQ once we figure out 'ORDER BY m.number_parts'.
+    $result = $this->connection->query("SELECT mlid FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.menu_name = :menu AND ml.module = 'system' ORDER BY m.number_parts ASC", array(':menu' => $this->entity->id()), array('fetch' => \PDO::FETCH_ASSOC))->fetchCol();
     $menu_links = $this->storageController->loadMultiple($result);
     foreach ($menu_links as $link) {
       $link->reset();
diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
index c9f821c632e0..a1701857f52e 100644
--- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
+++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
@@ -752,7 +752,7 @@ private function getStandardMenuLink() {
     // the front page.
     $query = \Drupal::entityQuery('menu_link')
       ->condition('module', 'system')
-      ->condition('link_path', 'user/logout');
+      ->condition('router_path', 'user/logout');
     $result = $query->execute();
     if (!empty($result)) {
       $mlid = reset($result);
diff --git a/core/modules/menu/menu.install b/core/modules/menu/menu.install
index 3415cdcf8d88..b7dc67d65435 100644
--- a/core/modules/menu/menu.install
+++ b/core/modules/menu/menu.install
@@ -14,13 +14,12 @@ function menu_install() {
   // Add a link for each custom menu.
   \Drupal::service('router.builder')->rebuild();
   menu_router_rebuild();
-  menu_link_rebuild_defaults();
   $system_link = entity_load_multiple_by_properties('menu_link', array('link_path' => 'admin/structure/menu', 'module' => 'system'));
   $system_link = reset($system_link);
 
   $base_link = entity_create('menu_link', array(
     'menu_name' => $system_link->menu_name,
-    'link_path' => 'admin/structure/menu/manage/%',
+    'router_path' => 'admin/structure/menu/manage/%',
     'module' => 'menu',
   ));
 
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index d4a885742b8d..1ffddee0fb74 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -92,19 +92,6 @@ function menu_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function menu_menu_link_defaults() {
-  $links['menu.admin.overview'] = array(
-    'link_title' => 'Menus',
-    'description' => 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.',
-    'route_name' => 'menu.overview_page',
-    'parent' => 'system.admin.structure',
-  );
-  return $links;
-}
-
 /**
  * Implements hook_entity_info().
  */
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
index 36362c8b3a1a..ba0397e82ff5 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
@@ -8,7 +8,9 @@
 namespace Drupal\menu_link\Entity;
 
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Routing\UrlMatcher;
 use Drupal\menu_link\MenuLinkInterface;
 use Symfony\Component\Routing\Route;
 
@@ -62,13 +64,6 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
    */
   public $mlid;
 
-  /**
-   * An optional machine name if defined via hook_menu_link_defaults().
-   *
-   * @var string
-   */
-  public $machine_name;
-
   /**
    * The menu link UUID.
    *
@@ -90,6 +85,14 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
    */
   public $link_path;
 
+  /**
+   * For links corresponding to a Drupal path (external = 0), this connects the
+   * link to a {menu_router}.path for joins.
+   *
+   * @var string
+   */
+  public $router_path;
+
   /**
    * The entity label.
    *
@@ -250,7 +253,7 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
    *
    * @var array
    */
-  public $route_parameters = array();
+  public $route_parameters;
 
   /**
    * The route object associated with this menu link, if any.
@@ -357,17 +360,13 @@ public function setRouteObject(Route $route) {
    */
   public function reset() {
     // To reset the link to its original values, we need to retrieve its
-    // definition from hook_menu_link_defaults(). Otherwise, for example, the
-    // link's menu would not be reset, because properties like the original
-    // 'menu_name' are not stored anywhere else. Since resetting a link happens
-    // rarely and this is a one-time operation, retrieving the full set of
-    // default menu links does little harm.
-    $all_links = menu_link_get_defaults();
-    $original = $all_links[$this->machine_name];
-    $original['machine_name'] = $this->machine_name;
-    /** @var \Drupal\menu_link\MenuLinkStorageControllerInterface $storage_controller */
-    $storage_controller = \Drupal::entityManager()->getStorageController($this->entityType);
-    $new_link = $storage_controller->createFromDefaultLink($original);
+    // definition from hook_menu(). Otherwise, for example, the link's menu
+    // would not be reset, because properties like the original 'menu_name' are
+    // not stored anywhere else. Since resetting a link happens rarely and this
+    // is a one-time operation, retrieving the full menu router does no harm.
+    $menu = menu_get_router();
+    $router_item = $menu[$this->router_path];
+    $new_link = self::buildFromRouterItem($router_item);
     // Merge existing menu link's ID and 'has_children' property.
     foreach (array('mlid', 'has_children') as $key) {
       $new_link->{$key} = $this->{$key};
@@ -376,6 +375,30 @@ public function reset() {
     return $new_link;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function buildFromRouterItem(array $item) {
+    // Suggested items are disabled by default.
+    if ($item['type'] == MENU_SUGGESTED_ITEM) {
+      $item['hidden'] = 1;
+    }
+    // Hide all items that are not visible in the tree.
+    elseif (!($item['type'] & MENU_VISIBLE_IN_TREE)) {
+      $item['hidden'] = -1;
+    }
+    // Note, we set this as 'system', so that we can be sure to distinguish all
+    // the menu links generated automatically from entries in {menu_router}.
+    $item['module'] = 'system';
+    $item += array(
+      'link_title' => $item['title'],
+      'link_path' => $item['path'],
+      'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
+    );
+    return \Drupal::entityManager()
+      ->getStorageController('menu_link')->create($item);
+  }
+
   /**
    * Implements ArrayAccess::offsetExists().
    */
@@ -459,11 +482,12 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
     parent::preSave($storage_controller);
 
     // This is the easiest way to handle the unique internal path '<front>',
-    // since a path marked as external does not need to match a route.
+    // since a path marked as external does not need to match a router path.
     $this->external = (url_is_external($this->link_path) || $this->link_path == '<front>') ? 1 : 0;
 
     // Try to find a parent link. If found, assign it and derive its menu.
-    $parent = $this->findParent($storage_controller);
+    $parent_candidates = !empty($this->parentCandidates) ? $this->parentCandidates : array();
+    $parent = $this->findParent($storage_controller, $parent_candidates);
     if ($parent) {
       $this->plid = $parent->id();
       $this->menu_name = $parent->menu_name;
@@ -502,7 +526,17 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
     if (isset($this->original) && ($this->plid != $this->original->plid || $this->menu_name != $this->original->menu_name)) {
       $storage_controller->moveChildren($this);
     }
-
+    // Find the router_path.
+    if (empty($this->router_path) || empty($this->original) || (isset($this->original) && $this->original->link_path != $this->link_path)) {
+      if ($this->external) {
+        $this->router_path = '';
+      }
+      else {
+        // Find the router path which will serve this path.
+        $this->parts = explode('/', $this->link_path, MENU_MAX_PARTS);
+        $this->router_path = _menu_find_router_path($this->link_path);
+      }
+    }
     // Find the route_name.
     if (!isset($this->route_name)) {
       if ($result = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->link_path)) {
@@ -513,9 +547,6 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
         $this->route_parameters = array();
       }
     }
-    elseif (empty($this->link_path)) {
-      $this->link_path = \Drupal::urlGenerator()->getPathFromRoute($this->route_name, $this->route_parameters);
-    }
   }
 
   /**
@@ -547,6 +578,9 @@ public static function postLoad(EntityStorageControllerInterface $storage_contro
       $menu_link->options = unserialize($menu_link->options);
       $menu_link->route_parameters = unserialize($menu_link->route_parameters);
 
+      // Use the weight property from the menu link.
+      $menu_link->router_item['weight'] = $menu_link->weight;
+
       // By default use the menu_name as type.
       $menu_link->bundle = $menu_link->menu_name;
 
@@ -572,7 +606,7 @@ public static function postLoad(EntityStorageControllerInterface $storage_contro
   /**
    * {@inheritdoc}
    */
-  protected function setParents(MenuLinkInterface $parent) {
+  public function setParents(EntityInterface $parent) {
     $i = 1;
     while ($i < $this->depth) {
       $p = 'p' . $i++;
@@ -590,7 +624,7 @@ protected function setParents(MenuLinkInterface $parent) {
   /**
    * {@inheritdoc}
    */
-  protected function findParent(EntityStorageControllerInterface $storage_controller) {
+  public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array()) {
     $parent = FALSE;
 
     // This item is explicitely top-level, skip the rest of the parenting.
@@ -613,30 +647,26 @@ protected function findParent(EntityStorageControllerInterface $storage_controll
     }
 
     foreach ($candidates as $mlid) {
-      $parent = $storage_controller->load($mlid);
+      if (isset($parent_candidates[$mlid])) {
+        $parent = $parent_candidates[$mlid];
+      }
+      else {
+        $parent = $storage_controller->load($mlid);
+      }
       if ($parent) {
-        break;
+        return $parent;
       }
     }
+
+    // If everything else failed, try to derive the parent from the path
+    // hierarchy. This only makes sense for links derived from menu router
+    // items (ie. from hook_menu()).
+    if ($this->module == 'system') {
+      $parent = $storage_controller->getParentFromHierarchy($this);
+    }
+
     return $parent;
   }
 
-  /**
-   * Builds and returns the renderable array for this menu link.
-   *
-   * @return array
-   *   A renderable array representing the content of the link.
-   */
-  public function build() {
-    $build = array(
-      '#type' => 'link',
-      '#title' => $this->title,
-      '#href' => $this->href,
-      '#route_name' => $this->route_name ? $this->route_name : NULL,
-      '#route_parameters' => $this->route_parameters,
-      '#options' => !empty($this->localized_options) ? $this->localized_options : array(),
-    );
-    return $build;
-  }
 
 }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php
index 9ecb3212987b..d81b1f7f6ccd 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php
@@ -2,13 +2,15 @@
 
 /**
  * @file
- * Contains \Drupal\menu_link\MenuLinkInterface.
+ * Contains \Drupal\menu_link\Entity\MenuLinkInterface.
  */
 
 namespace Drupal\menu_link;
 
-use Drupal\Core\Entity\EntityInterface;
 use Symfony\Component\Routing\Route;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
 
 /**
  * Provides an interface defining a menu link entity.
@@ -37,9 +39,49 @@ public function setRouteObject(Route $route);
   /**
    * Resets a system-defined menu link.
    *
-   * @return \Drupal\menu_link\MenuLinkInterface
+   * @return \Drupal\Core\Entity\EntityInterface
    *   A menu link entity.
    */
   public function reset();
 
+  /**
+   * Builds a menu link entity from a router item.
+   *
+   * @param array $item
+   *   A menu router item.
+   *
+   * @return \Drupal\menu_link\MenuLinkInterface
+   *   A menu link entity.
+   */
+  public static function buildFromRouterItem(array $item);
+
+  /**
+   * Sets the p1 through p9 properties for a menu link entity being saved.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $parent
+   *   A menu link entity.
+   */
+  public function setParents(EntityInterface $parent);
+
+  /**
+   * Finds a possible parent for a given menu link entity.
+   *
+   * Because the parent of a given link might not exist anymore in the database,
+   * we apply a set of heuristics to determine a proper parent:
+   *
+   *  - use the passed parent link if specified and existing.
+   *  - else, use the first existing link down the previous link hierarchy
+   *  - else, for system menu links (derived from hook_menu()), reparent
+   *    based on the path hierarchy.
+   *
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
+   *   Storage controller object.
+   * @param array $parent_candidates
+   *   An array of menu link entities keyed by mlid.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|false
+   *   A menu link entity structure of the possible parent or FALSE if no valid
+   *   parent has been found.
+   */
+  public function findParent(EntityStorageControllerInterface $storage_controller, array $parent_candidates = array());
 }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index b43d2656b459..0e95f42353bb 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -26,18 +26,18 @@
 class MenuLinkStorageController extends DatabaseStorageController implements MenuLinkStorageControllerInterface {
 
   /**
-   * Contains all {menu_router} fields without weight.
+   * Indicates whether the delete operation should re-parent children items.
    *
-   * @var array
+   * @var bool
    */
-  protected static $routerItemFields;
+  protected $preventReparenting = FALSE;
 
   /**
-   * Indicates whether the delete operation should re-parent children items.
+   * Holds an array of router item schema fields.
    *
-   * @var bool
+   * @var array
    */
-  protected $preventReparenting = FALSE;
+  protected static $routerItemFields = array();
 
   /**
    * The route provider service.
@@ -98,7 +98,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
   protected function buildQuery($ids, $revision_id = FALSE) {
     $query = parent::buildQuery($ids, $revision_id);
     // Specify additional fields from the {menu_router} table.
-    $query->leftJoin('menu_router', 'm', 'base.link_path = m.path');
+    $query->leftJoin('menu_router', 'm', 'base.router_path = m.path');
     $query->fields('m', static::$routerItemFields);
     return $query;
   }
@@ -360,23 +360,4 @@ public function getParentFromHierarchy(EntityInterface $entity) {
     return $parent;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function createFromDefaultLink(array $item) {
-    // Suggested items are disabled by default.
-    $item += array(
-      'type' => MENU_NORMAL_ITEM,
-      'hidden' => 0,
-      'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
-    );
-    if ($item['type'] == MENU_SUGGESTED_ITEM) {
-      $item['hidden'] = 1;
-    }
-    // Note, we set this as 'system', so that we can be sure to distinguish all
-    // the menu links generated automatically from hook_menu_link_defaults().
-    $item['module'] = 'system';
-    return $this->create($item);
-  }
-
 }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php
index b219834ebce1..3fb408f4739a 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageControllerInterface.php
@@ -33,6 +33,20 @@ public function setPreventReparenting($value = FALSE);
    */
   public function getPreventReparenting();
 
+  /**
+   * Loads updated and customized menu links for specific router paths.
+   *
+   * Note that this is a low-level method and it doesn't return fully populated
+   * menu link entities. (e.g. no fields are attached)
+   *
+   * @param array $router_paths
+   *   An array of router paths.
+   *
+   * @return array
+   *   An array of menu link objects indexed by their ids.
+   */
+  public function loadUpdatedCustomized(array $router_paths);
+
   /**
    * Loads system menu link as needed by system_get_module_admin_tasks().
    *
@@ -93,18 +107,4 @@ public function countMenuLinks($menu_name);
    */
   public function getParentFromHierarchy(EntityInterface $entity);
 
-  /**
-   * Builds a menu link entity from a default item.
-   *
-   * This function should only be called for link data from
-   * hook_menu_link_defaults().
-   *
-   * @param array $item
-   *   An item returned from menu_links_get_defaults().
-   *
-   * @return \Drupal\menu_link\MenuLinkInterface
-   *   A menu link entity.
-   */
-  public function createFromDefaultLink(array $item);
-
 }
diff --git a/core/modules/menu_link/menu_link.api.php b/core/modules/menu_link/menu_link.api.php
index 2541eb54086d..197aee8dfa89 100644
--- a/core/modules/menu_link/menu_link.api.php
+++ b/core/modules/menu_link/menu_link.api.php
@@ -14,10 +14,11 @@
  * Alter a menu link after it has been translated and before it is rendered.
  *
  * This hook is invoked from _menu_link_translate() after a menu link has been
- * translated; i.e., after the user access to the link's target page has
- * been checked. It is only invoked if $menu_link['options']['alter'] has been
- * set to a non-empty value (e.g. TRUE). This flag should be set using
- * hook_menu_link_presave().
+ * translated; i.e., after dynamic path argument placeholders (%) have been
+ * replaced with actual values, the user access to the link's target page has
+ * been checked, and the link has been localized. It is only invoked if
+ * $menu_link['options']['alter'] has been set to a non-empty value (e.g. TRUE).
+ * This flag should be set using hook_menu_link_presave().
  *
  * Implementations of this hook are able to alter any property of the menu link.
  * For example, this hook may be used to add a page-specific query string to all
@@ -28,6 +29,8 @@
  *
  * @param \Drupal\menu_link\Entity\MenuLink $menu_link
  *   A menu link entity.
+ * @param array $map
+ *   Associative array containing the menu $map (path parts and/or objects).
  *
  * @see hook_menu_link_alter()
  */
diff --git a/core/modules/menu_link/menu_link.install b/core/modules/menu_link/menu_link.install
index c789d34ef822..976653f1523d 100644
--- a/core/modules/menu_link/menu_link.install
+++ b/core/modules/menu_link/menu_link.install
@@ -31,12 +31,6 @@ function menu_link_schema() {
         'length' => 128,
         'not null' => FALSE,
       ),
-      'machine_name' => array(
-        'description' => 'Unique machine name: Optional human-readable ID for this link.',
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => FALSE,
-      ),
       'plid' => array(
         'description' => 'The parent link ID (plid) is the mlid of the link above in the hierarchy, or zero if the link is at the top level in its menu.',
         'type' => 'int',
@@ -51,6 +45,13 @@ function menu_link_schema() {
         'not null' => TRUE,
         'default' => '',
       ),
+      'router_path' => array(
+        'description' => 'For links corresponding to a Drupal path (external = 0), this connects the link to a {menu_router}.path for joins.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
       'langcode' => array(
         'description' => 'The {language}.langcode of this link.',
         'type' => 'varchar',
@@ -59,7 +60,7 @@ function menu_link_schema() {
         'default' => '',
       ),
       'link_title' => array(
-        'description' => 'The text displayed for the link.',
+        'description' => 'The text displayed for the link, which may be modified by a title callback stored in {menu_router}.',
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
@@ -213,6 +214,7 @@ function menu_link_schema() {
       'path_menu' => array(array('link_path', 128), 'menu_name'),
       'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'),
       'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'),
+      'router_path' => array(array('router_path', 128)),
     ),
     'primary key' => array('mlid'),
   );
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index ec0cd7cb7171..b457cb9b4914 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -973,30 +973,6 @@ function node_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function node_menu_link_defaults() {
-  $links['node.admin.content'] = array(
-    'link_title' => 'Content',
-    'route_name' => 'node.content_overview',
-    'parent' => 'system.admin',
-    'description' => 'Find and manage content.',
-  );
-
-  $links['node.admin.structure.types'] = array(
-    'link_title' => 'Content types',
-    'parent' => 'system.admin.structure',
-    'description' => 'Manage content types, including default status, front page promotion, comment settings, etc.',
-    'route_name' => 'node.overview_types',
-  );
-  $links['node.add'] = array(
-    'link_title' => 'Add content',
-    'route_name' => 'node.add_page',
-  );
-  return $links;
-}
-
 /**
  * Implements hook_menu_local_tasks().
  */
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index 80eeace53cdc..8e96610ff371 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -75,21 +75,6 @@ function path_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function path_menu_link_defaults() {
-  $links['path.admin.overview'] = array(
-    'link_title' => 'URL aliases',
-    'description' => "Change your site's URL paths by aliasing them.",
-    'route_name' => 'path.admin_overview',
-    'parent' => 'system.admin.config.search',
-    'weight' => -5,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_form_BASE_FORM_ID_alter() for node_form().
  *
diff --git a/core/modules/picture/picture.module b/core/modules/picture/picture.module
index 9e5e4a36d257..d494f09c714c 100644
--- a/core/modules/picture/picture.module
+++ b/core/modules/picture/picture.module
@@ -71,21 +71,6 @@ function picture_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function picture_menu_link_defaults() {
-  $links['picture.admin.config.picturemapping'] = array(
-    'link_title' => 'Picture Mappings',
-    'description' => 'Manage picture mappings',
-    'weight' => 10,
-    'route_name' => 'picture.mapping_page',
-    'parent' => 'system.admin.config.media',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_library_info().
  */
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 11a5e00e1700..25f982575210 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -167,26 +167,6 @@ function search_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function search_menu_link_defaults() {
-  $links['search'] = array(
-    'link_title' => 'Search',
-    'route_name' => 'search.view',
-    'type' => MENU_SUGGESTED_ITEM,
-  );
-  $links['search.admin.settings'] = array(
-    'link_title' => 'Search settings',
-    'parent' => 'system.admin.config.search',
-    'description' => 'Configure relevance settings for search and other indexing options.',
-    'route_name' => 'search.settings',
-    'weight' => -10,
-  );
-
-  return $links;
-}
-
 /**
  * Clears either a part of, or the entire search index.
  *
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index a988cf47a57d..8c7c8695e99e 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -86,20 +86,6 @@ function shortcut_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function shortcut_menu_link_defaults() {
-  $links['shortcut.admin.config.user-interface.shortcut'] = array(
-    'link_title' => 'Shortcuts',
-    'description' => 'Add and modify shortcut sets.',
-    'route_name' => 'shortcut.set_admin',
-    'parent' => 'system.admin.config.user-interface',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_admin_paths().
  */
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 6efecef52513..bd5067626011 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -46,21 +46,6 @@ function simpletest_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function simpletest_menu_link_defaults() {
-  $links['simpletest.admin.config.development.testing'] = array(
-    'link_title' => 'Testing',
-    'description' => 'Run tests against Drupal core and your modules. These tests help assure that your site code is working as designed.',
-    'route_name' => 'simpletest.test_form',
-    'parent' => 'system.admin.config.development',
-    'weight' => -5,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index a8a0105eda25..a5d4c479b233 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -86,25 +86,12 @@ function statistics_menu() {
     'title' => 'Statistics',
     'description' => 'Control details about what and how your site logs content statistics.',
     'route_name' => 'statistics.settings',
+    'access arguments' => array('administer statistics'),
     'weight' => -15,
   );
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function statistics_menu_link_defaults() {
-  $links['statistics.admin.config.system.statistics'] = array(
-    'link_title' => 'Statistics',
-    'description' => 'Control details about what and how your site logs content statistics.',
-    'route_name' => 'statistics.settings',
-    'parent' => 'system.admin.config.system',
-    'weight' => -15,
-  );
-  return $links;
-}
-
 /**
  * Implements hook_cron().
  */
diff --git a/core/modules/system/lib/Drupal/system/SystemManager.php b/core/modules/system/lib/Drupal/system/SystemManager.php
index fd8f77e0e367..07e8c0a5dabe 100644
--- a/core/modules/system/lib/Drupal/system/SystemManager.php
+++ b/core/modules/system/lib/Drupal/system/SystemManager.php
@@ -194,15 +194,10 @@ public function getAdminBlock($item) {
     }
 
     if (!isset($item['mlid'])) {
-      $menu_links = $this->menuLinkStorage->loadByProperties(array('link_path' => $item['path'], 'module' => 'system'));
-      if ($menu_links) {
-        $menu_link = reset($menu_links);
-        $item['mlid'] = $menu_link->id();
-        $item['menu_name'] = $menu_link->menu_name;
-      }
-      else {
-        return array();
-      }
+      $menu_links = $this->menuLinkStorage->loadByProperties(array('router_path' => $item['path'], 'module' => 'system'));
+      $menu_link = reset($menu_links);
+      $item['mlid'] = $menu_link->id();
+      $item['menu_name'] = $menu_link->menu_name;
     }
 
     if (isset($this->menuItems[$item['mlid']])) {
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
index 6d7699a1aa29..9ab9103dcab5 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
@@ -338,7 +338,10 @@ function testBreadCrumbs() {
     $trail += array(
       'user/' . $this->web_user->id() => $this->web_user->getUsername(),
     );
-    $this->assertBreadcrumb('user/' . $this->web_user->id() . '/edit', $trail, $this->web_user->getUsername());
+    $tree = array(
+      'user' => t('My account'),
+    );
+    $this->assertBreadcrumb('user/' . $this->web_user->id() . '/edit', $trail, $this->web_user->getUsername(), $tree);
 
     // Create an only slightly privileged user being able to access site reports
     // but not administration pages.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php
index cc8c4e08bb54..bcf7c9629bd4 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php
@@ -183,7 +183,7 @@ function testMenuLinkReparenting($module = 'menu_test') {
   }
 
   /**
-   * Tests automatic reparenting of menu links derived from hook_menu_link_defaults.
+   * Test automatic reparenting of menu links derived from menu routers.
    */
   function testMenuLinkRouterReparenting() {
     // Run all the standard parenting tests on menu links derived from
@@ -224,7 +224,8 @@ function testMenuLinkRouterReparenting() {
     $this->assertMenuLinkParents($links, $expected_hierarchy);
 
     // Now delete 'child-2' directly from the database, simulating a database
-    // crash. 'child-1-2' will get reparented to the top.
+    // crash. 'child-1-2' will get reparented under 'child-1' based on its
+    // path.
     // Don't do that at home.
     db_delete('menu_links')
       ->condition('mlid', $links['child-2']['mlid'])
@@ -232,7 +233,7 @@ function testMenuLinkRouterReparenting() {
     $expected_hierarchy = array(
       'child-1' => FALSE,
       'child-1-1' => 'child-1',
-      'child-1-2' => FALSE,
+      'child-1-2' => 'child-1',
     );
     $this->assertMenuLinkParents($links, $expected_hierarchy);
   }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
index 3c1290381de6..ccc327dcba38 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
@@ -62,8 +62,10 @@ function setUp() {
    */
   public function testMenuIntegration() {
     $this->doTestTitleMenuCallback();
+    $this->doTestMenuSetItem();
     $this->doTestMenuOptionalPlaceholders();
     $this->doTestMenuOnRoute();
+    $this->doTestMenuHidden();
     $this->doTestMenuGetItemNoAncestors();
     $this->doTestMenuName();
     $this->doTestMenuItemTitlesCases();
@@ -176,7 +178,7 @@ protected function doTestMenuName() {
     $admin_user = $this->drupalCreateUser(array('administer site configuration'));
     $this->drupalLogin($admin_user);
 
-    $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu_name_test'));
+    $menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test'));
     $menu_link = reset($menu_links);
     $this->assertEqual($menu_link->menu_name, 'original', 'Menu name is "original".');
 
@@ -186,7 +188,7 @@ protected function doTestMenuName() {
     \Drupal::service('router.builder')->rebuild();
     menu_router_rebuild();
 
-    $menu_links = entity_load_multiple_by_properties('menu_link', array('link_path' => 'menu_name_test'));
+    $menu_links = entity_load_multiple_by_properties('menu_link', array('router_path' => 'menu_name_test'));
     $menu_link = reset($menu_links);
     $this->assertEqual($menu_link->menu_name, 'changed', 'Menu name was successfully changed after rebuild.');
   }
@@ -206,6 +208,87 @@ protected function doTestMenuHierarchy() {
     $this->assertEqual($unattached_child_link['plid'], $parent_link['mlid'], 'The parent of a non-directly attached child is correct.');
   }
 
+  /**
+   * Tests menu link depth and parents of local tasks and menu callbacks.
+   */
+  protected function doTestMenuHidden() {
+    // Verify links for one dynamic argument.
+    $query = \Drupal::entityQuery('menu_link')
+      ->condition('router_path', 'menu-test/hidden/menu', 'STARTS_WITH')
+      ->sort('router_path');
+    $result = $query->execute();
+    $menu_links = menu_link_load_multiple($result);
+
+    $links = array();
+    foreach ($menu_links as $menu_link) {
+      $links[$menu_link->router_path] = $menu_link;
+    }
+
+    $parent = $links['menu-test/hidden/menu'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/menu/list'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/settings'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $parent = $links['menu-test/hidden/menu/manage/%'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/menu/manage/%/list'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%/edit'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/menu/manage/%/delete'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    // Verify links for two dynamic arguments.
+    $query = \Drupal::entityQuery('menu_link')
+      ->condition('router_path', 'menu-test/hidden/block', 'STARTS_WITH')
+      ->sort('router_path');
+    $result = $query->execute();
+    $menu_links = menu_link_load_multiple($result);
+
+    $links = array();
+    foreach ($menu_links as $menu_link) {
+      $links[$menu_link->router_path] = $menu_link;
+    }
+
+    $parent = $links['menu-test/hidden/block'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/block/list'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $link = $links['menu-test/hidden/block/manage/%/%'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+
+    $parent = $links['menu-test/hidden/block/manage/%/%'];
+    $depth = $parent['depth'] + 1;
+    $plid = $parent['mlid'];
+
+    $link = $links['menu-test/hidden/block/manage/%/%/delete'];
+    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
+    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
+  }
+
   /**
    * Test menu_get_item() with empty ancestors.
    */
@@ -214,6 +297,23 @@ protected function doTestMenuGetItemNoAncestors() {
     $this->drupalGet('');
   }
 
+  /**
+   * Test menu_set_item().
+   */
+  protected function doTestMenuSetItem() {
+    $item = menu_get_item('test-page');
+
+    $this->assertEqual($item['path'], 'test-page', "Path from menu_get_item('test-page') is equal to 'test-page'", 'menu');
+
+    // Modify the path for the item then save it.
+    $item['path'] = 'test-page-test';
+    $item['href'] = 'test-page-test';
+
+    menu_set_item('test-page', $item);
+    $compare_item = menu_get_item('test-page');
+    $this->assertEqual($compare_item, $item, 'Modified menu item is equal to newly retrieved menu item.', 'menu');
+  }
+
   /**
    * Test menu maintenance hooks.
    */
@@ -264,8 +364,9 @@ protected function doTestMenuItemTitlesCases() {
     // Build array with string overrides.
     $test_data = array(
       1 => array('Example title - Case 1' => 'Alternative example title - Case 1'),
-      2 => array('Example title' => 'Alternative example title'),
+      2 => array('Example @sub1 - Case @op2' => 'Alternative example @sub1 - Case @op2'),
       3 => array('Example title' => 'Alternative example title'),
+      4 => array('Example title' => 'Alternative example title'),
     );
 
     foreach ($test_data as $case_no => $override) {
@@ -322,6 +423,7 @@ protected function doTestMenuOnRoute() {
     $this->assertLinkByHref('menu-title-test/case1');
     $this->assertLinkByHref('menu-title-test/case2');
     $this->assertLinkByHref('menu-title-test/case3');
+    $this->assertLinkByHref('menu-title-test/case4');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php
new file mode 100644
index 000000000000..409f3aae38ec
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TrailTest.php
@@ -0,0 +1,136 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Menu\TrailTest.
+ */
+
+namespace Drupal\system\Tests\Menu;
+
+/**
+ * Tests active menu trails.
+ */
+class TrailTest extends MenuTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('block', 'menu_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Active trail',
+      'description' => 'Tests active menu trails and alteration functionality.',
+      'group' => 'Menu',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->admin_user = $this->drupalCreateUser(array('administer site configuration', 'access administration pages', 'administer blocks'));
+    $this->drupalLogin($this->admin_user);
+
+    // This test puts menu links in the Tools and Administration menus and then
+    // tests for their presence on the page.
+    $this->drupalPlaceBlock('system_menu_block:tools');
+    $this->drupalPlaceBlock('system_menu_block:admin');
+  }
+
+  /**
+   * Tests that the active trail works correctly on custom 403 and 404 pages.
+   */
+  function testCustom403And404Pages() {
+    // Set the custom 403 and 404 pages we will use.
+    \Drupal::config('system.site')
+      ->set('page.403', 'menu-test/custom-403-page')
+      ->set('page.404', 'menu-test/custom-404-page')
+      ->save();
+
+    // Define the paths we'll visit to trigger 403 and 404 responses during
+    // this test, and the expected active trail for each case.
+    $paths = array(
+      403 => 'admin/config',
+      404 => $this->randomName(),
+    );
+    // For the 403 page, the initial trail during the Drupal bootstrap should
+    // include the page that the user is trying to visit, while the final trail
+    // should reflect the custom 403 page that the user was redirected to.
+    $expected_trail[403]['initial'] = array(
+      '<front>' => 'Home',
+      'admin/config' => 'Configuration',
+    );
+    $expected_trail[403]['final'] = array(
+      '<front>' => 'Home',
+      'menu-test' => 'Menu test root',
+      'menu-test/custom-403-page' => 'Custom 403 page',
+    );
+    // For the 404 page, the initial trail during the Drupal bootstrap should
+    // only contain the link back to "Home" (since the page the user is trying
+    // to visit doesn't have any menu items associated with it), while the
+    // final trail should reflect the custom 404 page that the user was
+    // redirected to.
+    $expected_trail[404]['initial'] = array(
+      '<front>' => 'Home',
+    );
+    $expected_trail[404]['final'] = array(
+      '<front>' => 'Home',
+      'menu-test' => 'Menu test root',
+      'menu-test/custom-404-page' => 'Custom 404 page',
+    );
+
+    // Visit each path as an anonymous user so that we will actually get a 403
+    // on admin/config.
+    $this->drupalLogout();
+    foreach (array(403, 404) as $status_code) {
+      // Before visiting the page, trigger the code in the menu_test module
+      // that will record the active trail (so we can check it in this test).
+      \Drupal::state()->set('menu_test.record_active_trail', TRUE);
+      $this->drupalGet($paths[$status_code]);
+      $this->assertResponse($status_code);
+
+      // Check that the initial trail (during the Drupal bootstrap) matches
+      // what we expect.
+      $initial_trail = \Drupal::state()->get('menu_test.active_trail_initial') ?: array();
+      $this->assertEqual(count($initial_trail), count($expected_trail[$status_code]['initial']), format_string('The initial active trail for a @status_code page contains the expected number of items (expected: @expected, found: @found).', array(
+        '@status_code' => $status_code,
+        '@expected' => count($expected_trail[$status_code]['initial']),
+        '@found' => count($initial_trail),
+      )));
+      foreach (array_keys($expected_trail[$status_code]['initial']) as $index => $path) {
+        $this->assertEqual($initial_trail[$index]['href'], $path, format_string('Element number @number of the initial active trail for a @status_code page contains the correct path (expected: @expected, found: @found)', array(
+          '@number' => $index + 1,
+          '@status_code' => $status_code,
+          '@expected' => $path,
+          '@found' => $initial_trail[$index]['href'],
+        )));
+      }
+
+      // Check that the final trail (after the user has been redirected to the
+      // custom 403/404 page) matches what we expect.
+      $final_trail = \Drupal::state()->get('menu_test.active_trail_final') ?: array();
+      $this->assertEqual(count($final_trail), count($expected_trail[$status_code]['final']), format_string('The final active trail for a @status_code page contains the expected number of items (expected: @expected, found: @found).', array(
+        '@status_code' => $status_code,
+        '@expected' => count($expected_trail[$status_code]['final']),
+        '@found' => count($final_trail),
+      )));
+      foreach (array_keys($expected_trail[$status_code]['final']) as $index => $path) {
+        $this->assertEqual($final_trail[$index]['href'], $path, format_string('Element number @number of the final active trail for a @status_code page contains the correct path (expected: @expected, found: @found)', array(
+          '@number' => $index + 1,
+          '@status_code' => $status_code,
+          '@expected' => $path,
+          '@found' => $final_trail[$index]['href'],
+        )));
+      }
+
+      // Check that the breadcrumb displayed on the final custom 403/404 page
+      // matches what we expect. (The last item of the active trail represents
+      // the current page, which is not supposed to appear in the breadcrumb,
+      // so we need to remove it from the array before checking.)
+      array_pop($expected_trail[$status_code]['final']);
+      $this->assertBreadcrumb(NULL, $expected_trail[$status_code]['final']);
+    }
+  }
+}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
index d6927181657c..78a00aa77a03 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
@@ -43,21 +43,21 @@ function testMenuTreeData() {
     // @todo Prettify this tree buildup code, it's very hard to read.
     $this->tree_data = array(
       '1'=> array(
-        'link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 1, 'hidden' => 0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access' => 1, 'link_path' => 'a', 'localized_options' => array('attributes' => array('title' =>'')))),
+        'link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 1, 'hidden' => 0, 'has_children' => 1, 'title' => 'Item 1', 'in_active_trail' => 1, 'access' => 1, 'href' => 'a', 'localized_options' => array('attributes' => array('title' =>'')))),
         'below' => array(
-          '2' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 2, 'hidden' => 0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access' => 1, 'link_path' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')))),
+          '2' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 2, 'hidden' => 0, 'has_children' => 1, 'title' => 'Item 2', 'in_active_trail' => 1, 'access' => 1, 'href' => 'a/b', 'localized_options' => array('attributes' => array('title' =>'')))),
             'below' => array(
-              '3' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 3, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access' => 1, 'link_path' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')))),
+              '3' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 3, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 3', 'in_active_trail' => 0, 'access' => 1, 'href' => 'a/b/c', 'localized_options' => array('attributes' => array('title' =>'')))),
                 'below' => array() ),
-              '4' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 4, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access' => 1, 'link_path' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')))),
+              '4' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 4, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 4', 'in_active_trail' => 0, 'access' => 1, 'href' => 'a/b/d', 'localized_options' => array('attributes' => array('title' =>'')))),
                 'below' => array() )
               )
             )
           )
         ),
-      '5' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 5, 'hidden' => 1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access' => 1, 'link_path' => 'e', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array()),
-      '6' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 6, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access' => 0, 'link_path' => 'f', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array()),
-      '7' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 7, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access' => 1, 'link_path' => 'g', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array())
+      '5' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 5, 'hidden' => 1, 'has_children' => 0, 'title' => 'Item 5', 'in_active_trail' => 0, 'access' => 1, 'href' => 'e', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array()),
+      '6' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 6, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 6', 'in_active_trail' => 0, 'access' => 0, 'href' => 'f', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array()),
+      '7' => array('link' => $storage_controller->create(array('menu_name' => 'main-menu', 'mlid' => 7, 'hidden' => 0, 'has_children' => 0, 'title' => 'Item 7', 'in_active_trail' => 0, 'access' => 1, 'href' => 'g', 'localized_options' => array('attributes' => array('title' =>'')))), 'below' => array())
     );
 
     $output = menu_tree_output($this->tree_data);
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 679fcdb91590..1ad9b4b691ea 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -149,7 +149,7 @@ function theme_admin_block_content($variables) {
     }
     $output .= '<dl class="' . $class . '">';
     foreach ($content as $item) {
-      $output .= '<dt>' . l($item['title'], $item['link_path'], $item['localized_options']) . '</dt>';
+      $output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
       if (!$compact && isset($item['description'])) {
         $output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
       }
@@ -582,4 +582,3 @@ function theme_system_themes_page($variables) {
 
   return $output;
 }
-
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index ed33a05eec32..1de6b1532d77 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -488,72 +488,6 @@ function hook_menu_get_item_alter(&$router_item, $path, $original_map) {
   }
 }
 
-/**
- * Define links for menus.
- *
- * @return array
- *   An array of default menu links. Each link has a key that is the machine
- *   name, which must be unique. The corresponding array value is an
- *   associative array that may contain the following key-value pairs:
- *   - link_title: (required) The untranslated title of the menu item.
- *   - description: The untranslated description of the link.
- *   - route_name: (optional) The route name to be used to build the path.
- *     Either a route_name or a link_path must be provided.
- *   - route_parameters: (optional) The route parameters to build the path.
- *   - link_path: (optional) If you have an external link use link_path instead
- *     of providing a route_name.
- *   - parent: (optional) The machine name of the link that is this link's menu
- *     parent.
- *   - weight: (optional) An integer that determines the relative position of
- *     items in the menu; higher-weighted items sink. Defaults to 0. Menu items
- *     with the same weight are ordered alphabetically.
- *   - menu_name: (optional) The machine name of a menu to put the link in, if
- *     not the default Tools menu.
- *   - expanded: (optional) If set to TRUE, and if a menu link is provided for
- *     this menu item (as a result of other properties), then the menu link is
- *     always expanded, equivalent to its 'always expanded' checkbox being set
- *     in the UI.
- *   - type: (optional) A bitmask of flags describing properties of the menu
- *     item. The following two bitmasks are provided as constants in menu.inc:
- *     - MENU_NORMAL_ITEM: Normal menu items show up in the menu tree and can be
- *       moved/hidden by the administrator.
- *     - MENU_SUGGESTED_ITEM: Modules may "suggest" menu items that the
- *       administrator may enable.
- *     If the "type" element is omitted, MENU_NORMAL_ITEM is assumed.
- *   - options: (optional) An array of options to be passed to l() when
- *     generating a link from this menu item.
- *
- * @see hook_menu_link_defaults_alter()
- */
-function hook_menu_link_defaults() {
-  $links['user'] = array(
-    'link_title' => 'My account',
-    'weight' => -10,
-    'route_name' => 'user.page',
-    'menu_name' => 'account',
-  );
-
-  $links['user.logout'] = array(
-    'link_title' => 'Log out',
-    'route_name' => 'user.logout',
-    'weight' => 10,
-    'menu_name' => 'account',
-  );
-
-  return $links;
-}
-
-/**
- * Alter links for menus.
- *
- * @see hook_menu_link_defaults()
- */
-function hook_menu_link_defaults_alter(&$links) {
-  // Change the weight and title of the user.logout link.
-  $links['user.logout']['weight'] = -10;
-  $links['user.logout']['link_title'] = t('Logout');
-}
-
 /**
  * Define links for menus.
  *
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index ed0efba83565..c2cf62012f6f 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -9,12 +9,10 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Utility\ModuleInfo;
-use Drupal\menu_link\MenuLinkInterface;
 use Drupal\user\UserInterface;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\Request;
 use Guzzle\Http\Exception\BadResponseException;
 use Guzzle\Http\Exception\RequestException;
 
@@ -817,206 +815,6 @@ function system_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function system_menu_link_defaults() {
-  $items['system.admin'] = array(
-    'link_title' => 'Administration',
-    'route_name' => 'system.admin',
-    'weight' => 9,
-    'menu_name' => 'admin',
-  );
-
-  // Menu items that are basically just menu blocks.
-  $items['system.admin.structure'] = array(
-    'route_name' => 'system.admin_structure',
-    'parent' => 'system.admin',
-    'description' => 'Administer blocks, content types, menus, etc.',
-    'link_title' => 'Structure',
-    'weight' => -8,
-  );
-  // Appearance.
-  $items['system.admin.appearance'] = array(
-    'route_name' => 'system.themes_page',
-    'link_title' => 'Appearance',
-    'parent' => 'system.admin',
-    'weight' => -6,
-  );
-  // Modules.
-  $items['system.admin.modules'] = array(
-    'link_title' => 'Extend',
-    'parent' => 'system.admin',
-    'route_name' => 'system.modules_list',
-    'weight' => -2,
-  );
-  // Configuration.
-  $items['system.admin.config'] = array(
-    'link_title' => 'Configuration',
-    'parent' => 'system.admin',
-    'description' => 'Administer settings.',
-    'route_name' => 'system.admin_config',
-  );
-
-  // Media settings.
-  $items['system.admin.config.media'] = array(
-    'route_name' => 'system.admin_config_media',
-    'parent' => 'system.admin.config',
-    'link_title' => 'Media',
-    'weight' => -10,
-  );
-  $items['system.admin.config.media.file-system'] = array(
-    'link_title' => 'File system',
-    'description' => 'Tell Drupal where to store uploaded files and how they are accessed.',
-    'parent' => 'system.admin.config.media',
-    'route_name' => 'system.file_system_settings',
-  );
-  $items['system.admin.config.media.image-toolkit'] = array(
-    'link_title' => 'Image toolkit',
-    'parent' => 'system.admin.config.media',
-    'route_name' => 'system.image_toolkit_settings',
-    'description' => 'Choose which image toolkit to use if you have installed optional toolkits.',
-    'weight' => 20,
-  );
-
-  // Service settings.
-  $items['system.admin.config.services'] = array(
-    'link_title' => 'Web services',
-    'parent' => 'system.admin.config',
-    'route_name' => 'system.admin_config_services',
-  );
-  $items['system.admin.config.services.rss-publishing'] = array(
-    'link_title' => 'RSS publishing',
-    'parent' => 'system.admin.config.services',
-    'description' => 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.',
-    'route_name' => 'system.rss_feeds_settings',
-  );
-
-  // Development settings.
-  $items['system.admin.config.development'] = array(
-    'route_name' => 'system.admin_config_development',
-    'parent' => 'system.admin.config',
-    'link_title' => 'Development',
-    'description' => 'Development tools.',
-    'weight' => -10,
-  );
-  $items['system.admin.config.development.maintenance'] = array(
-    'link_title' => 'Maintenance mode',
-    'parent' => 'system.admin.config.development',
-    'description' => 'Take the site offline for maintenance or bring it back online.',
-    'route_name' => 'system.site_maintenance_mode',
-    'weight' => -10,
-  );
-  $items['system.admin.config.development.performance'] = array(
-    'link_title' => 'Performance',
-    'parent' => 'system.admin.config.development',
-    'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.',
-    'route_name' => 'system.performance_settings',
-    'weight' => -20,
-  );
-  $items['system.admin.config.development.logging'] = array(
-    'link_title' => 'Logging and errors',
-    'parent' => 'system.admin.config.development',
-    'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destinations, such as syslog, database, email, etc.",
-    'route_name' => 'system.logging_settings',
-    'weight' => -15,
-  );
-
-  // Regional and date settings.
-  $items['system.admin.config.regional'] = array(
-    'route_name' => 'system.admin_config_regional',
-    'link_title' => 'Regional and language',
-    'parent' => 'system.admin.config',
-    'description' => 'Regional settings, localization and translation.',
-    'weight' => -5,
-  );
-  $items['system.admin.config.regional.settings'] = array(
-    'link_title' => 'Regional settings',
-    'parent' => 'system.admin.config.regional',
-    'description' => "Settings for the site's default time zone and country.",
-    'route_name' => 'system.regional_settings',
-    'weight' => -20,
-  );
-  $items['system.admin.config.regional.date-time'] = array(
-    'link_title' => 'Date and time formats',
-    'parent' => 'system.admin.config.regional',
-    'description' => 'Configure display format strings for date and time.',
-    'route_name' => 'system.date_format_list',
-    'weight' => -9,
-  );
-
-  // Search settings.
-  $items['system.admin.config.search'] = array(
-    'link_title' => 'Search and metadata',
-    'route_name' => 'system.admin_config_search',
-    'parent' => 'system.admin.config',
-    'description' => 'Local site search, metadata and SEO.',
-    'weight' => -10,
-  );
-
-  // System settings.
-  $items['system.admin.config.system'] = array(
-    'link_title' => 'System',
-    'route_name' => 'system.admin_config_system',
-    'parent' => 'system.admin.config',
-    'description' => 'General system related configuration.',
-    'weight' => -20,
-  );
-  $items['system.admin.config.system.site-information'] = array(
-    'link_title' => 'Site information',
-    'parent' => 'system.admin.config.system',
-    'description' => 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.',
-    'route_name' => 'system.site_information_settings',
-    'weight' => -20,
-  );
-  $items['system.admin.config.system.cron'] = array(
-    'link_title' => 'Cron',
-    'parent' => 'system.admin.config.system',
-    'description' => 'Manage automatic site maintenance tasks.',
-    'route_name' => 'system.cron_settings',
-    'weight' => 20,
-  );
-  // Additional categories
-  $items['system.admin.config.user-interface'] = array(
-    'link_title' => 'User interface',
-    'route_name' => 'system.admin_config_ui',
-    'parent' => 'system.admin.config',
-    'description' => 'Tools that enhance the user interface.',
-    'weight' => -15,
-  );
-  $items['system.admin.config.workflow'] = array(
-    'link_title' => 'Workflow',
-    'route_name' => 'system.admin_config_workflow',
-    'parent' => 'system.admin.config',
-    'description' => 'Content workflow, editorial workflow tools.',
-    'weight' => 5,
-  );
-  $items['system.admin.config.content'] = array(
-    'link_title' => 'Content authoring',
-    'route_name' => 'system.admin_config_content',
-    'parent' => 'system.admin.config',
-    'description' => 'Settings related to formatting and authoring content.',
-    'weight' => -15,
-  );
-
-  // Reports.
-  $items['system.admin.reports'] = array(
-    'link_title' => 'Reports',
-    'route_name' => 'system.admin_reports',
-    'parent' => 'system.admin',
-    'description' => 'View reports, updates, and errors.',
-    'weight' => 5,
-  );
-  $items['system.admin.reports.status'] = array(
-    'link_title' => 'Status report',
-    'parent' => 'system.admin.reports',
-    'description' => "Get a status report about your site's operation and any detected problems.",
-    'route_name' => 'system.status',
-  );
-
-  return $items;
-}
-
 /**
  * Implements hook_theme_suggestions_HOOK().
  */
@@ -2861,17 +2659,17 @@ function system_get_module_admin_tasks($module, $info) {
     foreach ($menu_links as $link) {
       _menu_link_translate($link);
       if ($link['access']) {
-        $links[$link['machine_name']] = $link;
+        $links[$link['router_path']] = $link;
       }
     }
   }
 
   $admin_tasks = array();
   $titles = array();
-  if ($menu = module_invoke($module, 'menu_link_defaults')) {
-    foreach ($menu as $machine_name => $item) {
-      if (isset($links[$machine_name])) {
-        $task = $links[$machine_name];
+  if ($menu = module_invoke($module, 'menu')) {
+    foreach ($menu as $path => $item) {
+      if (isset($links[$path])) {
+        $task = $links[$path];
         // The link description, either derived from 'description' in
         // hook_menu() or customized via menu module is used as title attribute.
         if (!empty($task['localized_options']['attributes']['title'])) {
@@ -2894,10 +2692,10 @@ function system_get_module_admin_tasks($module, $info) {
           }
         }
         else {
-          $titles[$machine_name] = $task['title'];
+          $titles[$path] = $task['title'];
         }
 
-        $admin_tasks[$machine_name] = $task;
+        $admin_tasks[$path] = $task;
       }
     }
   }
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 9be827e419e0..4d27aa6c1810 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -288,7 +288,6 @@ system.admin_index:
   path: '/admin/index'
   defaults:
     _content: 'Drupal\system\Controller\AdminController::index'
-    _title: 'Administration'
   requirements:
     _permission: 'access administration pages'
 
@@ -342,8 +341,6 @@ system.theme_settings_theme:
 
 '<front>':
   path: '/'
-  defaults:
-    _title: Home
   requirements:
     _access: 'TRUE'
 
diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Controller/MenuTestController.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Controller/MenuTestController.php
index 46b2c8000c60..d315e6d42aa5 100644
--- a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Controller/MenuTestController.php
+++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Controller/MenuTestController.php
@@ -19,23 +19,6 @@ public function menuTestCallback() {
     return menu_test_callback();
   }
 
-
-  /**
-   * A title callback method for test routes.
-   *
-   * @param array $_title_arguments
-   *   Optional array from the route defaults.
-   * @param string $_title
-   *   Optional _title string from the route defaults.
-   *
-   * @return string
-   *   The route title.
-   */
-  public function titleCallback(array $_title_arguments = array(), $_title = '') {
-    $_title_arguments += array('case_number' => '2', 'title' => $_title);
-    return t($_title_arguments['title']) . ' - Case ' . $_title_arguments['case_number'];
-  }
-
   /**
    * @todo Remove menu_test_custom_403_404_callback().
    */
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index 5eb3098aef5c..caf216bc6379 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -231,15 +231,24 @@ function menu_test_menu() {
     'route_name' => 'menu_test.title_test_case1',
   );
   $items['menu-title-test/case2'] = array(
-    'title' => 'Example title',
-    'title callback' => 'menu_test_title_callback',
+    'title' => 'Example @sub1 - Case @op2',
+    // If '2' is not in quotes, the argument becomes arg(2).
+    'title arguments' => array('@sub1' => 'title', '@op2' => '2'),
     'route_name' => 'menu_test.title_test_case2',
   );
   $items['menu-title-test/case3'] = array(
-    // Title gets completely ignored. Good thing, too.
-    'title' => 'Bike sheds full of blue smurfs WRONG',
+    'title' => 'Example title',
+    'title callback' => 'menu_test_title_callback',
     'route_name' => 'menu_test.title_test_case3',
   );
+  $items['menu-title-test/case4'] = array(
+    // Title gets completely ignored. Good thing, too.
+    'title' => 'Bike sheds full of blue smurfs',
+    'title callback' => 'menu_test_title_callback',
+    // If '4' is not in quotes, the argument becomes arg(4).
+    'title arguments' => array('Example title', '4'),
+    'route_name' => 'menu_test.title_test_case4',
+  );
 
   // Parent page for controller-based local tasks.
   $items['foo/%'] = array(
@@ -286,134 +295,6 @@ function menu_test_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function menu_test_menu_link_defaults() {
-  // The name of the menu changes during the course of the test. Using a $_GET.
-  $items['menu_test.menu_name_test'] = array(
-    'link_title' => 'Test menu_name router item',
-    'route_name' => 'menu_test.menu_name_test',
-    'menu_name' => menu_test_menu_name(),
-  );
-  // This item uses SystemController::systemAdminMenuBlockPage() to list child
-  // items.
-  $items['menu_test.menu_callback_description'] = array(
-    'link_title' => 'Menu item title',
-    'description' => 'Menu item description parent',
-    'route_name' => 'menu_test.callback_description',
-  );
-  // This item tests the description key.
-  $items['menu_test.menu_callback_description.description-plain'] = array(
-    'link_title' => 'Menu item with a regular description',
-    'description' => 'Menu item description text',
-    'route_name' => 'menu_test.callback_description_plain',
-    'parent' => 'menu_test.menu_callback_description',
-  );
-  // This item tests using a description callback.
-  $items['menu_callback_description.description-callback'] = array(
-    'link_title' => 'Menu item with a description set with a callback',
-    'route_name' => 'menu_test.callback_description_callback',
-    'parent' => 'menu_test.menu_callback_description',
-  );
-
-  $items['menu_test.menu_no_title_callback'] = array(
-    'link_title' => 'A title with @placeholder',
-    'route_name' => 'menu_test.menu_no_title_callback',
-  );
-
-  // Hierarchical tests.
-  $items['menu_test.hierarchy_parent'] = array(
-    'link_title' => 'Parent menu router',
-    'route_name' => 'menu_test.hierarchy_parent',
-  );
-  $items['menu_test.hierarchy_parent.child'] = array(
-    'link_title' => 'Child menu router',
-    'route_name' => 'menu_test.hierarchy_parent_child',
-    'parent' => 'menu_test.hierarchy_parent',
-  );
-  $items['menu_test.hierarchy_parent.child2.child'] = array(
-    'link_title' => 'Unattached subchild router',
-    'route_name' => 'menu_test.hierarchy_parent_child2',
-    'parent' => 'menu_test.hierarchy_parent.child',
-  );
-  // Path containing "exotic" characters.
-  $exotic = " -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
-    "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
-    "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
-  $items['menu_test.exotic_path'] = array(
-    'link_title' => '"Exotic" path',
-    'route_name' => 'menu_test.exotic_path',
-    'route_parameters' => array('exotic' => $exotic),
-  );
-
-  // Hidden tests; base parents.
-  // Same structure as in Menu and Block modules. Since those structures can
-  // change, we need to simulate our own in here.
-  $items['menu_test'] = array(
-    'link_title' => 'Menu test root',
-    'route_name' => 'menu_test.menu_test',
-  );
-  $items['menu_test.hidden'] = array(
-    'link_title' => 'Hidden test root',
-    'route_name' => 'menu_test.hidden',
-    'parent' => 'menu_test',
-  );
-
-  // Hidden tests; one dynamic argument.
-  $items['menu_test.hidden.menu'] = array(
-    'link_title' => 'Menus',
-    'route_name' => 'menu_test.hidden_menu',
-    'parent' => 'menu_test.hidden',
-  );
-
-  // Hidden tests; two dynamic arguments.
-  $items['menu_test.hidden.block'] = array(
-    'link_title' => 'Blocks',
-    'route_name' => 'menu_test.hidden_block',
-    'parent' => 'menu_test.hidden',
-  );
-
-  // Menu trail tests.
-  // @see MenuTrailTestCase
-  $items['menu_test.menu-trail'] = array(
-    'link_title' => 'Menu trail - Case 1',
-    'route_name' => 'menu_test.menu_trail',
-    'parent' => 'menu_test',
-  );
-  $items['menu_test.admin.config.development.menu-trail'] = array(
-    'link_title' => 'Menu trail - Case 2',
-    'description' => 'Tests menu_tree_set_path()',
-    'route_name' => 'menu_test.menu_trail_admin',
-    'parent' => 'system.admin.config.development',
-  );
-  $items['menu_test.custom-403-page'] = array(
-    'link_title' => 'Custom 403 page',
-    'route_name' => 'menu_test.custom_403',
-    'parent' => 'menu_test',
-  );
-  $items['menu_test.custom-404-page'] = array(
-    'link_title' => 'Custom 404 page',
-    'route_name' => 'menu_test.custom_404',
-    'parent' => 'menu_test',
-  );
-  // Test the access key.
-  $items['menu_test.menu-title-test.case1'] = array(
-    'link_title' => 'Example title - Case 1',
-    'route_name' => 'menu_test.title_test_case1',
-  );
-  $items['menu_test.menu-title-test.case2'] = array(
-    'link_title' => 'Example title',
-    'route_name' => 'menu_test.title_test_case2',
-  );
-  $items['menu_test.menu-title-test.case3'] = array(
-    // Title gets completely ignored. Good thing, too.
-    'link_title' => 'Bike sheds full of blue smurfs',
-    'route_name' => 'menu_test.title_test_case3',
-  );
-  return $items;
-}
-
 /**
  * Implements hook_menu_local_tasks().
  *
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml
index a76eddabbcf5..bc8a73682c8c 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml
@@ -519,7 +519,6 @@ menu_test.theme_callback_inheritance:
 menu_test.title_test_case1:
   path: '/menu-title-test/case1'
   defaults:
-    _title: 'Example title - Case 1'
     _content: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback'
   requirements:
     _access: 'TRUE'
@@ -527,8 +526,6 @@ menu_test.title_test_case1:
 menu_test.title_test_case2:
   path: '/menu-title-test/case2'
   defaults:
-    _title: 'Example title'
-    _title_callback: '\Drupal\menu_test\Controller\MenuTestController::titleCallback'
     _content: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback'
   requirements:
     _access: 'TRUE'
@@ -536,11 +533,13 @@ menu_test.title_test_case2:
 menu_test.title_test_case3:
   path: '/menu-title-test/case3'
   defaults:
-    _title: 'Bike sheds full of blue smurfs' # this should be ignored
-    _title_callback: '\Drupal\menu_test\Controller\MenuTestController::titleCallback'
-    _title_arguments:
-      title: 'Example title'
-      case_number: '3'
+    _content: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback'
+  requirements:
+    _access: 'TRUE'
+
+menu_test.title_test_case4:
+  path: '/menu-title-test/case4'
+  defaults:
     _content: '\Drupal\menu_test\Controller\MenuTestController::menuTestCallback'
   requirements:
     _access: 'TRUE'
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php
index 7685cd46f174..e79c37ce59fb 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php
@@ -56,19 +56,6 @@ public function termTitle(TermInterface $taxonomy_term) {
     return Xss::filter($taxonomy_term->label());
   }
 
-  /**
-   * Route title callback.
-   *
-   * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
-   *   The taxonomy term.
-   *
-   * @return string
-   *   The term label.
-   */
-  public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) {
-    return Xss::filter($taxonomy_vocabulary->label());
-  }
-
   /**
    * @todo Remove taxonomy_term_feed().
    */
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 3d199939d825..e41b46770f48 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -261,20 +261,6 @@ function taxonomy_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function taxonomy_menu_link_defaults() {
-  $links['taxonomy.admin.structure.vocabulary'] = array(
-    'link_title' => 'Taxonomy',
-    'parent' => 'system.admin.structure',
-    'description' => 'Manage tagging, categorization, and classification of your content.',
-    'route_name' => 'taxonomy.vocabulary_list',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_admin_paths().
  */
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index b5f3c2e96e40..804086001a0e 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -79,7 +79,6 @@ taxonomy.overview_terms:
   path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}'
   defaults:
     _form: 'Drupal\taxonomy\Form\OverviewTerms'
-    _title_callback: 'Drupal\taxonomy\Controller\TaxonomyController::vocabularyTitle'
   requirements:
     _entity_access: 'taxonomy_vocabulary.view'
 
diff --git a/core/modules/toolbar/lib/Drupal/toolbar/Tests/ToolbarAdminMenuTest.php b/core/modules/toolbar/lib/Drupal/toolbar/Tests/ToolbarAdminMenuTest.php
index b18ad958c5b1..b3f08ccda3e9 100644
--- a/core/modules/toolbar/lib/Drupal/toolbar/Tests/ToolbarAdminMenuTest.php
+++ b/core/modules/toolbar/lib/Drupal/toolbar/Tests/ToolbarAdminMenuTest.php
@@ -73,7 +73,6 @@ function setUp() {
       'bypass node access',
       'administer themes',
       'administer nodes',
-      'access content overview',
       'administer blocks',
       'administer menu',
       'administer modules',
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index bcb895410094..86ef5ee5d885 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -489,7 +489,7 @@ function toolbar_get_rendered_subtrees() {
         $subtree = '';
       }
 
-      $id = str_replace(array('/', '<', '>'), array('-', '', ''), $item['link_path']);
+      $id = str_replace(array('/', '<', '>'), array('-', '', ''), $item['href']);
       $subtrees[$id] = $subtree;
     }
   }
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index c5f0c0992547..b1be0414a3e0 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -31,18 +31,6 @@ function tracker_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function tracker_menu_link_defaults() {
-  $links['tracker'] = array(
-    'link_title' => 'Recent content',
-    'route_name' => 'tracker.page',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_cron().
  *
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 84357cafa0a8..af8143b11a3d 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -177,21 +177,6 @@ function update_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function update_menu_link_defaults() {
-  $links['update.admin.reports.updates'] = array(
-    'link_title' => 'Available updates',
-    'description' => 'Get a status report about available updates for your installed modules and themes.',
-    'route_name' => 'update.status',
-    'parent' => 'system.admin.reports',
-    'weight' => -50,
-  );
-
-  return $links;
-}
-
 /**
  * Access callback: Resolves if the current user can access updater menu items.
  *
diff --git a/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php b/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php
index 01d7a6cbeac9..af52c1536103 100644
--- a/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php
+++ b/core/modules/user/lib/Drupal/user/Access/LoginStatusCheck.php
@@ -21,7 +21,7 @@ class LoginStatusCheck implements AccessInterface {
    * {@inheritdoc}
    */
   public function access(Route $route, Request $request, AccountInterface $account) {
-    return ($request->attributes->get('_menu_admin') || $account->isAuthenticated()) ? static::ALLOW : static::DENY;
+    return $account->isAuthenticated() ? static::ALLOW : static::DENY;
   }
 
 }
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php b/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php
index bbe5a71ed189..24e7244623fb 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserAccountLinksTests.php
@@ -129,8 +129,11 @@ function testAccountPageTitles() {
     // Check the page title for registered users is "My Account" in menus.
     $this->drupalLogin($this->drupalCreateUser());
     // After login, the client is redirected to /user.
-    $this->assertLink(t('My account'), 0, "Page title of /user is 'My Account' in menus for registered users");
-    $this->assertLinkByHref(\Drupal::urlGenerator()->generate('user.page'), 0);
+    $link = $this->xpath('//a[contains(@class, :class)]', array(
+        ':class' => 'active-trail',
+      )
+    );
+    $this->assertEqual((string) $link[0], 'My account', "Page title of /user is 'My Account' in menus for registered users");
   }
 
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 0b13fc5b9915..88862d510a2e 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -789,62 +789,6 @@ function user_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function user_menu_link_defaults() {
-  // Registration and login pages.
-  $links['user'] = array(
-    'link_title' => 'My account',
-    'weight' => -10,
-    'route_name' => 'user.page',
-    'menu_name' => 'account',
-  );
-
-  $links['user.logout'] = array(
-    'link_title' => 'Log out',
-    'route_name' => 'user.logout',
-    'weight' => 10,
-    'menu_name' => 'account',
-  );
-
-  // User listing pages.
-  $links['user.admin.people'] = array(
-    'link_title' => 'People',
-    'route_name' => 'user.admin_account',
-    'description' => 'Manage user accounts, roles, and permissions.',
-    'parent' => 'system.admin',
-    'weight' => -4,
-  );
-  // Permissions and role forms.
-  $links['user.admin.people.permissions'] = array(
-    'link_title' => 'Permissions',
-    'parent' => 'user.admin.people',
-    'description' => 'Determine access to features by selecting permissions for roles.',
-    'route_name' => 'user.admin_permissions',
-  );
-
-  // Administration pages.
-  $links['user.admin.config.people'] = array(
-   'link_title' => 'People',
-   'route_name' => 'user.admin_index',
-   'parent' => 'system.admin.config',
-   'description' => 'Configure user accounts.',
-   'position' => 'left',
-   'weight' => -20,
-  );
-
-  $links['user.admin.config.people.accounts'] = array(
-    'link_title' => 'Account settings',
-    'parent' => 'user.admin.config.people',
-    'description' => 'Configure default behavior of users, including registration requirements, e-mails, and fields.',
-    'weight' => -10,
-    'route_name' => 'user.account_settings',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_menu_link_presave().
  */
@@ -853,9 +797,15 @@ function user_menu_link_presave(MenuLink $menu_link) {
   // for authenticated users. Authenticated users should see "My account", but
   // anonymous users should not see it at all. Therefore, invoke
   // user_menu_link_load() to conditionally hide the link.
-  if ($menu_link->machine_name == 'user' && $menu_link->module == 'system') {
+  if ($menu_link->link_path == 'user' && $menu_link->module == 'system') {
     $menu_link->options['alter'] = TRUE;
   }
+
+  // Force the Logout link to appear on the top-level of 'account' menu by
+  // default (i.e., unless it has been customized).
+  if ($menu_link->link_path == 'user/logout' && $menu_link->module == 'system' && empty($menu_link->customized)) {
+    $menu_link->plid = 0;
+  }
 }
 
 /**
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 540e2941d1cb..83e226f2244e 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -2366,21 +2366,6 @@ public function renderMoreLink() {
     }
   }
 
-  /**
-   * Creates menu links, if this display provides some.
-   *
-   * @param array $existing_links
-   *   An array of already existing menu items provided by drupal.
-   *
-   * @return array
-   *   The menu links registers for this display.
-   *
-   * @see hook_menu_link_defaults()
-   */
-  public function executeHookDefaultMenuLinks(array &$existing_links) {
-    return array();
-  }
-
   /**
    * If this display creates a page with a menu item, implement it here.
    *
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
index 0baa2dea23fb..a84d603d75df 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
@@ -271,70 +271,6 @@ public function alterRoutes(RouteCollection $collection) {
     return $view_route_names;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function executeHookDefaultMenuLinks(array &$existing_links) {
-    $links = array();
-
-    // Replace % with the link to our standard views argument loader
-    // views_arg_load -- which lives in views.module.
-
-    $bits = explode('/', $this->getOption('path'));
-    $page_arguments = array($this->view->storage->id(), $this->display['id']);
-    $this->view->initHandlers();
-    $view_arguments = $this->view->argument;
-
-    // Replace % with %views_arg for menu autoloading and add to the
-    // page arguments so the argument actually comes through.
-    foreach ($bits as $pos => $bit) {
-      if ($bit == '%') {
-        // If a view requires any arguments we cannot create a static menu link.
-        return array();
-      }
-    }
-
-    $view_route_names = $this->state->get('views.view_route_names') ?: array();
-
-    $path = implode('/', $bits);
-    $menu_link_id = 'views.' . str_replace('/', '.', $path);
-    $links[$menu_link_id] = array();
-
-    if ($path) {
-      // Some views might override existing paths, so we have to set the route
-      // name based upon the altering.
-      $view_id_display =  "{$this->view->storage->id()}.{$this->display['id']}";
-      $links[$menu_link_id] = array(
-        'route_name' => isset($view_route_names[$view_id_display]) ? $view_route_names[$view_id_display] : "view.$view_id_display",
-        // Identify URL embedded arguments and correlate them to a handler.
-        'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
-      );
-
-      $menu = $this->getOption('menu');
-      if (empty($menu)) {
-        $menu = array('type' => 'none');
-      }
-      // Set the title and description if we have one.
-      if ($menu['type'] != 'none') {
-        $links[$menu_link_id]['link_title'] = $menu['title'];
-        $links[$menu_link_id]['description'] = $menu['description'];
-      }
-
-      if (isset($menu['weight'])) {
-        $links[$menu_link_id]['weight'] = intval($menu['weight']);
-      }
-
-      switch ($menu['type']) {
-        case 'normal':
-          // Insert item into the proper menu.
-          $links[$menu_link_id]['menu_name'] = $menu['name'];
-          break;
-      }
-    }
-
-    return $links;
-  }
-
   /**
    * Overrides \Drupal\views\Plugin\views\display\DisplayPluginBase::executeHookMenu().
    */
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index d24b10df014e..09346d3408c0 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -1532,30 +1532,6 @@ public function executeHookMenu($display_id = NULL, &$callbacks = array()) {
     }
   }
 
-  /**
-   * Returns default menu links from the view and the named display handler.
-   *
-   * @param string $display_id
-   *   A display ID.
-   * @param array $links
-   *   An array of default menu link items passed from
-   *   views_menu_link_defaults_alter().
-   *
-   * @return array|bool
-   */
-  public function executeHookDefaultMenuLinks($display_id = NULL, &$links = array()) {
-    // Prepare the view with the information we have. This was probably already
-    // called, but it's good to be safe.
-    if (!$this->setDisplay($display_id)) {
-      return FALSE;
-    }
-
-    // Execute the hook.
-    if (isset($this->display_handler)) {
-      return $this->display_handler->executeHookDefaultMenuLinks($links);
-    }
-  }
-
   /**
    * Determine if the given user has access to the view. Note that
    * this sets the display handler if it hasn't been.
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 5f1bc7131814..fe427b062d27 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -315,23 +315,6 @@ function views_menu_alter(&$callbacks) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults_alter().
- */
-function views_menu_link_defaults_alter(array &$links) {
-  // @todo Decide what to do with all the crazy logic in views_menu_alter() in
-  // https://drupal.org/node/2107533.
-  $views = Views::getApplicableViews('uses_hook_menu');
-  foreach ($views as $data) {
-    /** @var \Drupal\views\ViewExecutable $view */
-    list($view, $display_id) = $data;
-    $result = $view->executeHookDefaultMenuLinks($display_id, $links);
-    foreach ($result as $link_id => $link) {
-      $links[$link_id] = $link;
-    }
-  }
-}
-
 /**
  * Implements hook_page_alter().
  */
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 348372d591be..f90508c009c9 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -60,31 +60,6 @@ function views_ui_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function views_ui_menu_link_defaults() {
-  $links = array();
-
-  // Top-level Views module pages (not tied to a particular View).
-  $links['views_ui.admin.structure.views'] = array(
-    'link_title' => 'Views',
-    'parent' => 'system.admin.structure',
-    'description' => 'Manage customized lists of content.',
-    'route_name' => 'views_ui.list',
-  );
-
-  // A page in the Reports section to show usage of plugins in all views.
-  $links['views_ui.admin.reports.views-plugins'] = array(
-    'link_title' => 'Views plugins',
-    'parent' => 'system.admin.reports',
-    'description' => 'Overview of plugins used in all views.',
-    'route_name' => 'views_ui.reports_plugins',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_entity_info().
  */
diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme
index 2427bb72bc84..fa993245818f 100644
--- a/core/themes/seven/seven.theme
+++ b/core/themes/seven/seven.theme
@@ -121,7 +121,7 @@ function seven_admin_block_content($variables) {
       if (isset($item['description']) && !system_admin_compact_mode()) {
         $content .= '<div class="description">' . filter_xss_admin($item['description']) . '</div>';
       }
-      $output .= l($content, $item['link_path'], $options);
+      $output .= l($content, $item['href'], $options);
       $output .= '</li>';
     }
     $output .= '</ul>';
-- 
GitLab