diff --git a/core/includes/schema.inc b/core/includes/schema.inc
index 45b4b23..d0fd30b 100644
--- a/core/includes/schema.inc
+++ b/core/includes/schema.inc
@@ -534,7 +534,7 @@ function drupal_schema_get_field_value(array $info, $value) {
     elseif ($info['type'] == 'float') {
       $value = (float) $value;
     }
-    else {
+    elseif (!is_array($value)) {
       $value = (string) $value;
     }
   }
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
new file mode 100644
index 0000000..957b0e8
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldType\MapItem.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\FieldItemBase;
+
+/**
+ * Defines the 'map' entity field type.
+ *
+ * @FieldType(
+ *   id = "map",
+ *   label = @Translation("Map"),
+ *   description = @Translation("An entity field containing a map value."),
+ *   configurable = FALSE
+ * )
+ */
+class MapItem extends FieldItemBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($values, $notify = TRUE) {
+    $this->values = array();
+    if (!isset($values)) {
+      return;
+    }
+
+    if (!is_array($values)) {
+      if ($values instanceof MapItem) {
+        $values = $values->getValue();
+      }
+      else {
+        $values = unserialize($values);
+      }
+    }
+
+    $this->values = $values;
+
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __get($name) {
+    if (!isset($this->values[$name])) {
+      $this->values[$name] = array();
+    }
+
+    return $this->values[$name];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __set($name, $value) {
+    if (isset($value)) {
+      $this->values[$name] = $value;
+    }
+    else {
+      unset($this->values[$name]);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Routing/UrlMatcher.php b/core/lib/Drupal/Core/Routing/UrlMatcher.php
index 61aef98..9e973d4 100644
--- a/core/lib/Drupal/Core/Routing/UrlMatcher.php
+++ b/core/lib/Drupal/Core/Routing/UrlMatcher.php
@@ -33,4 +33,33 @@ public function finalMatch(RouteCollection $collection, Request $request) {
     return $this->match('/' . $request->attributes->get('_system_path'));
   }
 
+  /**
+   * Returns the route_name and route parameters matching a system path.
+   *
+   * @todo Find a better place for this method in
+   *   https://drupal.org/node/2153891.
+   *
+   * @param string $link_path
+   *   The link path to find a route name for.
+   *
+   * @return array
+   *   Returns an array with both the route name and parameters, or an empty
+   *   array if no route was matched.
+   */
+  public function findRouteNameParameters($link_path) {
+    // Look up the route_name used for the given path.
+    $request = Request::create('/' . $link_path);
+    $request->attributes->set('_system_path', $link_path);
+    try {
+      $result = \Drupal::service('router')->matchRequest($request);
+      $return = array();
+      $return[] = isset($result['_route']) ? $result['_route'] : '';
+      $return[] = $result['_raw_variables']->all();
+      return $return;
+    }
+    catch (\Exception $e) {
+      return array();
+    }
+  }
+
 }
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 9c4e3d5..64340e0 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
@@ -7,16 +7,12 @@
 
 namespace Drupal\menu_link\Entity;
 
-use Drupal\Core\Language\Language;
-use Drupal\menu_link\MenuLinkInterface;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\HttpFoundation\Request;
-
-use Drupal\Core\Entity\EntityStorageException;
+use Drupal\Core\Entity\Entity;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
-use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\Entity;
+use Drupal\Core\Routing\UrlMatcher;
+use Drupal\menu_link\MenuLinkInterface;
+use Symfony\Component\Routing\Route;
 
 /**
  * Defines the menu link entity class.
@@ -547,7 +543,7 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
     }
     // Find the route_name.
     if (!isset($this->route_name)) {
-      if ($result = static::findRouteNameParameters($this->link_path)) {
+      if ($result = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->link_path)) {
         list($this->route_name, $this->route_parameters) = $result;
       }
       else {
@@ -614,25 +610,6 @@ public static function postLoad(EntityStorageControllerInterface $storage_contro
   /**
    * {@inheritdoc}
    */
-  public static function findRouteNameParameters($link_path) {
-    // Look up the route_name used for the given path.
-    $request = Request::create('/' . $link_path);
-    $request->attributes->set('_system_path', $link_path);
-    try {
-      $result = \Drupal::service('router')->matchRequest($request);
-      $return = array();
-      $return[] = isset($result['_route']) ? $result['_route'] : '';
-      $return[] = $result['_raw_variables']->all();
-      return $return;
-    }
-    catch (\Exception $e) {
-      return array();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function setParents(EntityInterface $parent) {
     $i = 1;
     while ($i < $this->depth) {
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 fcf5415..d81b1f7 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkInterface.php
@@ -56,18 +56,6 @@ public function reset();
   public static function buildFromRouterItem(array $item);
 
   /**
-   * Returns the route_name and route parameters matching a system path.
-   *
-   * @param string $link_path
-   *   The link path to find a route name for.
-   *
-   * @return array
-   *   Returns an array with both the route name and parameters, or an empty
-   *   array if no route was matched.
-   */
-  public static function findRouteNameParameters($link_path);
-
-  /**
    * Sets the p1 through p9 properties for a menu link entity being saved.
    *
    * @param \Drupal\Core\Entity\EntityInterface $parent
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php b/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php
deleted file mode 100644
index b928877..0000000
--- a/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\shortcut\Access\LinkAccessCheck.
- */
-
-namespace Drupal\shortcut\Access;
-
-use Drupal\Core\Access\StaticAccessCheckInterface;
-use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Provides an access check for shortcut link delete routes.
- */
-class LinkAccessCheck implements StaticAccessCheckInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function appliesTo() {
-    return array('_access_shortcut_link');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function access(Route $route, Request $request, AccountInterface $account) {
-    $menu_link = $request->attributes->get('menu_link');
-    $set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
-    if ($shortcut_set = shortcut_set_load($set_name)) {
-      return shortcut_set_edit_access($shortcut_set) ? static::ALLOW : static::DENY;
-    }
-    return static::DENY;
-  }
-
-}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutController.php b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutController.php
new file mode 100644
index 0000000..a7b0d42
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutController.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Controller\ShortcutController.
+ */
+
+namespace Drupal\shortcut\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\shortcut\ShortcutSetInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides route responses for taxonomy.module.
+ */
+class ShortcutController extends ControllerBase {
+
+  /**
+   * Returns a rendered edit form to create a new shortcut associated to the
+   * given shortcut set.
+   *
+   * @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
+   *   The shortcut set this shortcut will be added to.
+   *
+   * @return array
+   *   The shortcut add form.
+   */
+  public function addForm(ShortcutSetInterface $shortcut_set) {
+    $shortcut = $this->entityManager()->getStorageController('shortcut')->create(array('shortcut_set' => $shortcut_set->id()));
+    if ($this->moduleHandler()->moduleExists('language')) {
+      $shortcut->langcode = language_get_default_langcode('shortcut', $shortcut_set->id());
+    }
+    return $this->entityManager()->getForm($shortcut, 'add');
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php
index d2f38eb..f83a92e 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Controller/ShortcutSetController.php
@@ -34,21 +34,22 @@ class ShortcutSetController extends ControllerBase {
    */
   public function addShortcutLinkInline(ShortcutSetInterface $shortcut_set, Request $request) {
     $link = $request->query->get('link');
+    $name = $request->query->get('name');
     if (shortcut_valid_link($link)) {
-      $item = menu_get_item($link);
-      $title = ($item && $item['title']) ? $item['title'] : $link;
-      $link = array(
-        'link_title' => $title,
-        'link_path' => $link,
-      );
-      $this->moduleHandler()->loadInclude('shortcut', 'admin.inc');
-      shortcut_admin_add_link($link, $shortcut_set);
-      if ($shortcut_set->save() == SAVED_UPDATED) {
-        drupal_set_message(t('Added a shortcut for %title.', array('%title' => $link['link_title'])));
+      $shortcut = $this->entityManager()->getStorageController('shortcut')->create(array(
+        'title' => $name,
+        'shortcut_set' => $shortcut_set->id(),
+        'path' => $link,
+      ));
+
+      try {
+        $shortcut->save();
+        drupal_set_message($this->t('Added a shortcut for %title.', array('%title' => $shortcut->label())));
       }
-      else {
-        drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title'])));
+      catch (\Exception $e) {
+        drupal_set_message($this->t('Unable to add a shortcut for %path.', array('%path' => $shortcut->path->value)));
       }
+
       return $this->redirect('<front>');
     }
 
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
new file mode 100644
index 0000000..59f8287
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
@@ -0,0 +1,190 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Plugin\Core\Entity\Shortcut.
+ */
+
+namespace Drupal\shortcut\Entity;
+
+use Drupal\Core\Entity\ContentEntityBase;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Field\FieldDefinition;
+use Drupal\Core\Routing\UrlMatcher;
+use Drupal\shortcut\ShortcutInterface;
+
+/**
+ * Defines the shortcut entity class.
+ *
+ * @EntityType(
+ *   id = "shortcut",
+ *   label = @Translation("Shortcut link"),
+ *   module = "shortcut",
+ *   controllers = {
+ *     "storage" = "Drupal\Core\Entity\FieldableDatabaseStorageController",
+ *     "access" = "Drupal\shortcut\ShortcutAccessController",
+ *     "form" = {
+ *       "default" = "Drupal\shortcut\ShortcutFormController",
+ *       "add" = "Drupal\shortcut\ShortcutFormController",
+ *       "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
+ *     },
+ *     "translation" = "Drupal\content_translation\ContentTranslationController"
+ *   },
+ *   base_table = "shortcut",
+ *   data_table = "shortcut_field_data",
+ *   translatable = TRUE,
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid",
+ *     "bundle" = "shortcut_set",
+ *     "label" = "title"
+ *   },
+ *   links = {
+ *     "edit-form" = "/admin/config/user-interface/shortcut/link/{shortcut}"
+ *   }
+ * )
+ */
+class Shortcut extends ContentEntityBase implements ShortcutInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTitle() {
+    return $this->get('title')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setTitle($link_title) {
+    $this->set('title', $link_title);
+   return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getWeight() {
+    return $this->get('weight')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setWeight($weight) {
+    $this->set('weight', $weight);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteName() {
+    return $this->get('route_name')->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRouteName($route_name) {
+    $this->set('route_name', $route_name);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteParams() {
+    $value = $this->get('route_parameters')->getValue();
+    return reset($value);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRouteParams($route_parameters) {
+    $this->set('route_parameters', array('value' => $route_parameters));
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
+    parent::preCreate($storage_controller, $values);
+
+    if (!isset($values['shortcut_set'])) {
+      $values['shortcut_set'] = 'default';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function preSave(EntityStorageControllerInterface $storage_controller) {
+    parent::preSave($storage_controller);
+
+    if ($route_info = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->path->value)) {
+      $this->setRouteName($route_info[0]);
+      $this->setRouteParams($route_info[1]);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function baseFieldDefinitions($entity_type) {
+    $fields['id'] = FieldDefinition::create('integer')
+      ->setLabel(t('ID'))
+      ->setDescription(t('The ID of the shortcut.'))
+      ->setReadOnly(TRUE);
+
+    $fields['uuid'] = FieldDefinition::create('uuid')
+      ->setLabel(t('UUID'))
+      ->setDescription(t('The UUID of the shortcut.'))
+      ->setReadOnly(TRUE);
+
+    $fields['shortcut_set'] = FieldDefinition::create('entity_reference')
+      ->setLabel(t('Shortcut set'))
+      ->setDescription(t('The bundle of the shortcut.'))
+      ->setSetting('target_type', 'shortcut_set')
+      ->setRequired(TRUE);
+
+    $fields['title'] = FieldDefinition::create('string')
+      ->setLabel(t('Title'))
+      ->setDescription(t('The name of the shortcut.'))
+      ->setTranslatable(TRUE);
+
+    $fields['weight'] = FieldDefinition::create('integer')
+      ->setLabel(t('Weight'))
+      ->setDescription(t('Weight among shortcuts in the same shortcut set.'));
+
+    $fields['route_name'] = FieldDefinition::create('string')
+      ->setLabel(t('Route name'))
+      ->setDescription(t('The machine name of a defined Route this shortcut represents.'));
+
+    $fields['route_parameters'] = FieldDefinition::create('map')
+      ->setLabel(t('Route parameters'))
+      ->setDescription(t('A serialized array of route parameters of this shortcut.'));
+
+    $fields['langcode'] = FieldDefinition::create('language')
+      ->setLabel(t('Language code'))
+      ->setDescription(t('The language code of the shortcut.'));
+
+    $fields['default_langcode'] = FieldDefinition::create('boolean')
+      ->setLabel(t('Default language'))
+      ->setDescription(t('Flag to indicate whether this is the default language.'));
+
+    $fields['path'] = FieldDefinition::create('string')
+      ->setLabel(t('Path'))
+      ->setDescription(t('The computed shortcut path.'))
+      ->setComputed(TRUE);
+
+    $item_definition = $fields['path']->getItemDefinition();
+    $item_definition->setClass('\Drupal\shortcut\ShortcutPath');
+    $fields['path']->setItemDefinition($item_definition);
+
+    return $fields;
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php
index 0378e72..ef2c480 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/ShortcutSet.php
@@ -8,9 +8,8 @@
 namespace Drupal\shortcut\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
-use Drupal\Core\Entity\Annotation\EntityType;
-use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\shortcut\ShortcutInterface;
 use Drupal\shortcut\ShortcutSetInterface;
 
 /**
@@ -66,13 +65,6 @@ class ShortcutSet extends ConfigEntityBase implements ShortcutSetInterface {
   public $label;
 
   /**
-   * An array of menu links.
-   *
-   * @var array
-   */
-  public $links = array();
-
-  /**
    * {@inheritdoc}
    */
   public function postCreate(EntityStorageControllerInterface $storage_controller) {
@@ -82,18 +74,11 @@ public function postCreate(EntityStorageControllerInterface $storage_controller)
     if (!$this->getOriginalId()) {
       // Save a new shortcut set with links copied from the user's default set.
       $default_set = shortcut_default_set();
-      // Generate a name to have no collisions with menu.
-      // Size of menu_name is 32 so id could be 23 = 32 - strlen('shortcut-').
-      $id = substr($this->id(), 0, 23);
-      $this->set('id', $id);
-      if ($default_set->id() != $id) {
-        foreach ($default_set->links as $link) {
-          $link = $link->createDuplicate();
-          $link->enforceIsNew();
-          $link->menu_name = $id;
-          $link->save();
-          $this->links[$link->uuid()] = $link;
-        }
+      foreach ($default_set->getShortcuts() as $shortcut) {
+        $shortcut = $shortcut->createDuplicate();
+        $shortcut->enforceIsNew();
+        $shortcut->shortcut_set->target_id = $this->id();
+        $shortcut->save();
       }
     }
   }
@@ -101,57 +86,41 @@ public function postCreate(EntityStorageControllerInterface $storage_controller)
   /**
    * {@inheritdoc}
    */
-  public static function postLoad(EntityStorageControllerInterface $storage_controller, array &$entities) {
-    parent::postLoad($storage_controller, $entities);
-    foreach ($entities as $id => $entity) {
-      $links = menu_load_links('shortcut-' . $id);
-      foreach ($links as $menu_link) {
-        $entity->links[$menu_link->uuid()] = $menu_link;
-      }
-    }
-  }
+  public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
+    parent::preDelete($storage_controller, $entities);
 
-  /**
-   * {@inheritdoc}
-   */
-  public function preSave(EntityStorageControllerInterface $storage_controller) {
-    parent::preSave($storage_controller);
+    foreach ($entities as $entity) {
+      $storage_controller->deleteAssignedShortcutSets($entity);
+
+      // Next, delete the shortcuts for this set.
+      $shortcut_ids = \Drupal::entityQuery('shortcut')
+        ->condition('shortcut_set', $entity->id(), '=')
+        ->execute();
 
-    // Just store the UUIDs.
-    foreach ($this->links as $uuid => $link) {
-      $this->links[$uuid] = $uuid;
+      $controller = \Drupal::entityManager()->getStorageController('shortcut');
+      $entities = $controller->loadMultiple($shortcut_ids);
+      $controller->delete($entities);
     }
   }
 
   /**
    * {@inheritdoc}
    */
-  public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
-    parent::postSave($storage_controller, $update);
-
-    foreach ($this->links as $uuid) {
-      if ($menu_link = entity_load_by_uuid('menu_link', $uuid)) {
-        // Do not specifically associate these links with the shortcut module,
-        // since other modules may make them editable via the menu system.
-        // However, we do need to specify the correct menu name.
-        $menu_link->menu_name = 'shortcut-' . $this->id();
-        $menu_link->plid = 0;
-        $menu_link->save();
-      }
+  public function resetLinkWeights() {
+    $weight = -50;
+    foreach ($this->getShortcuts() as $shortcut) {
+      $shortcut->weight->value = ++$weight;
+      $shortcut->save();
     }
+
+    return $this;
   }
 
   /**
    * {@inheritdoc}
    */
-  public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
-    parent::preDelete($storage_controller, $entities);
-
-    foreach ($entities as $entity) {
-      $storage_controller->deleteAssignedShortcutSets($entity);
-      // Next, delete the menu links for this set.
-      menu_delete_links('shortcut-' . $entity->id());
-    }
+  public function getShortcuts() {
+    return \Drupal::entityManager()->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $this->id()));
   }
 
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/LinkDelete.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/LinkDelete.php
deleted file mode 100644
index 5f50096..0000000
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/LinkDelete.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\shortcut\Form\LinkDelete.
- */
-
-namespace Drupal\shortcut\Form;
-
-use Drupal\Core\Form\ConfirmFormBase;
-use Drupal\menu_link\Entity\MenuLink;
-
-/**
- * Builds the shortcut link deletion form.
- */
-class LinkDelete extends ConfirmFormBase {
-
-  /**
-   * The menu link to delete.
-   *
-   * @var \Drupal\menu_link\Entity\MenuLink
-   */
-  protected $menuLink;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'shortcut_link_delete';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return t('Are you sure you want to delete the shortcut %title?', array('%title' => $this->menuLink->link_title));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelRoute() {
-    return array(
-      'route_name' => 'shortcut.set_customize',
-      'route_parameters' => array(
-        'shortcut_set' => str_replace('shortcut-', '', $this->menuLink->menu_name),
-      ),
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildForm(array $form, array &$form_state, MenuLink $menu_link = NULL) {
-    $this->menuLink = $menu_link;
-
-    return parent::buildForm($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, array &$form_state) {
-    menu_link_delete($this->menuLink->mlid);
-    $set_name = str_replace('shortcut-', '' , $this->menuLink->menu_name);
-    $form_state['redirect_route'] = array(
-      'route_name' => 'shortcut.set_customize',
-      'route_parameters' => array(
-        'shortcut_set' => $set_name,
-      ),
-    );
-    drupal_set_message(t('The shortcut %title has been deleted.', array('%title' => $this->menuLink->link_title)));
-  }
-
-}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
index 29da4a8..ac9c37d 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/SetCustomize.php
@@ -8,6 +8,7 @@
 namespace Drupal\shortcut\Form;
 
 use Drupal\Core\Entity\EntityFormController;
+use Drupal\Core\Entity\EntityManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -16,6 +17,32 @@
 class SetCustomize extends EntityFormController {
 
   /**
+   * The shortcut storage controller.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * Constructs a SetCustomize object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entity_manager
+   *   The entity manager.
+   */
+  public function __construct(EntityManager $entity_manager) {
+    $this->storageController = $entity_manager->getStorageController('shortcut');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function form(array $form, array &$form_state) {
@@ -28,35 +55,36 @@ public function form(array $form, array &$form_state) {
     $form['shortcuts']['links'] = array(
       '#type' => 'table',
       '#header' => array(t('Name'), t('Weight'), t('Operations')),
-      '#empty' => t('No shortcuts available. @link', array('@link' => l(t('Add a shortcut'), 'admin/config/user-interface/shortcut/' . $this->entity->id() . '/add-link'))),
+      '#empty' => $this->t('No shortcuts available. <a href="@link">Add a shortcut</a>', array('@link' => $this->urlGenerator()->generateFromRoute('shortcut.link_add', array('shortcut_set' => $this->entity->id())))),
       '#attributes' => array('id' => 'shortcuts'),
       '#tabledrag' => array(
         array('order', 'sibling', 'shortcut-weight'),
       ),
     );
 
-    foreach ($this->entity->links as $link) {
-      $mlid = $link->id();
-      $form['shortcuts']['links'][$mlid]['#attributes']['class'][] = 'draggable';
-      $form['shortcuts']['links'][$mlid]['name']['#markup'] = l($link->link_title, $link->link_path);
-      $form['shortcuts']['links'][$mlid]['#weight'] = $link->weight;
-      $form['shortcuts']['links'][$mlid]['weight'] = array(
+    $shortcuts = $this->storageController->loadByProperties(array('shortcut_set' => $this->entity->id()));
+    foreach ($shortcuts as $shortcut) {
+      $id = $shortcut->id();
+      $form['shortcuts']['links'][$id]['#attributes']['class'][] = 'draggable';
+      $form['shortcuts']['links'][$id]['name']['#markup'] = l($shortcut->title->value, $shortcut->path->value);
+      $form['shortcuts']['links'][$id]['#weight'] = $shortcut->weight->value;
+      $form['shortcuts']['links'][$id]['weight'] = array(
         '#type' => 'weight',
-        '#title' => t('Weight for @title', array('@title' => $link->link_title)),
+        '#title' => t('Weight for @title', array('@title' => $shortcut->title->value)),
         '#title_display' => 'invisible',
-        '#default_value' => $link->weight,
+        '#default_value' => $shortcut->weight->value,
         '#attributes' => array('class' => array('shortcut-weight')),
       );
 
       $links['edit'] = array(
         'title' => t('Edit'),
-        'href' => "admin/config/user-interface/shortcut/link/$mlid",
+        'href' => "admin/config/user-interface/shortcut/link/$id",
       );
       $links['delete'] = array(
         'title' => t('Delete'),
-        'href' => "admin/config/user-interface/shortcut/link/$mlid/delete",
+        'href' => "admin/config/user-interface/shortcut/link/$id/delete",
       );
-      $form['shortcuts']['links'][$mlid]['operations'] = array(
+      $form['shortcuts']['links'][$id]['operations'] = array(
         '#type' => 'operations',
         '#links' => $links,
       );
@@ -74,7 +102,7 @@ protected function actions(array $form, array &$form_state) {
     return array(
       'submit' => array(
         '#value' => t('Save changes'),
-        '#access' => !empty($this->entity->links),
+        '#access' => (bool) element_get_visible_children($form['shortcuts']['links']),
         '#submit' => array(
           array($this, 'submit'),
           array($this, 'save'),
@@ -87,9 +115,10 @@ protected function actions(array $form, array &$form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, array &$form_state) {
-    foreach ($this->entity->links as $link) {
-      $link->weight = $form_state['values']['shortcuts']['links'][$link->mlid]['weight'];
-      $link->save();
+    $shortcuts = $this->storageController->loadByProperties(array('shortcut_set' => $this->entity->id()));
+    foreach ($shortcuts as $shortcut) {
+      $shortcut->weight->value = $form_state['values']['shortcuts']['links'][$shortcut->id()]['weight'];
+      $shortcut->save();
     }
     drupal_set_message(t('The shortcut set has been updated.'));
   }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutDeleteForm.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutDeleteForm.php
new file mode 100644
index 0000000..b5bbabe
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutDeleteForm.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Form\ShortcutDeleteForm.
+ */
+
+namespace Drupal\shortcut\Form;
+
+use Drupal\Core\Entity\ContentEntityConfirmFormBase;
+
+/**
+ * Builds the shortcut link deletion form.
+ */
+class ShortcutDeleteForm extends ContentEntityConfirmFormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'shortcut_confirm_delete';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    return $this->t('Are you sure you want to delete the shortcut %title?', array('%title' => $this->entity->title->value));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelRoute() {
+    return array(
+      'route_name' => 'shortcut.set_customize',
+      'route_parameters' => array(
+        'shortcut_set' => $this->entity->bundle(),
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfirmText() {
+    return $this->t('Delete');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submit(array $form, array &$form_state) {
+    $this->entity->delete();
+    $form_state['redirect_route'] = array(
+      'route_name' => 'shortcut.set_customize',
+      'route_parameters' => array(
+        'shortcut_set' => $this->entity->bundle(),
+      ),
+    );
+    drupal_set_message($this->t('The shortcut %title has been deleted.', array('%title' => $this->entity->title->value)));
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php b/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php
index fc5fec2..9c2cf5e 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php
@@ -1,4 +1,5 @@
 <?php
+
 /**
  * @file
  * Contains \Drupal\shortcut\Form\ShortcutForm.
@@ -6,8 +7,6 @@
 
 namespace Drupal\shortcut\Form;
 
-use Drupal\menu_link\MenuLinkInterface;
-use Drupal\shortcut\ShortcutSetInterface;
 use Drupal\user\UserInterface;
 
 /**
@@ -16,26 +15,6 @@
 class ShortcutForm {
 
   /**
-   * Wraps shortcut_link_edit().
-   *
-   * @todo Remove shortcut_link_edit().
-   */
-  public function edit(MenuLinkInterface $menu_link) {
-    module_load_include('admin.inc', 'shortcut');
-    return drupal_get_form('shortcut_link_edit', $menu_link);
-  }
-
-  /**
-   * Wraps shortcut_link_add().
-   *
-   * @todo Remove shortcut_link_add().
-   */
-  public function add(ShortcutSetInterface $shortcut_set) {
-    module_load_include('admin.inc', 'shortcut');
-    return drupal_get_form('shortcut_link_add', $shortcut_set);
-  }
-
-  /**
    * Wraps shortcut_set_switch().
    *
    * @todo Remove shortcut_set_switch().
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
new file mode 100644
index 0000000..4dd0aea
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutAccessController.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutAccessController.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Entity\EntityAccessController;
+use Drupal\Core\Entity\EntityControllerInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Defines the access controller for the test entity type.
+ */
+class ShortcutAccessController extends EntityAccessController implements EntityControllerInterface {
+
+  /**
+   * The shortcut_set storage controller.
+   *
+   * @var \Drupal\shortcut\ShortcutSetStorageController
+   */
+  protected $shortcutSetStorage;
+
+  /**
+   * Constructs a ShortcutAccessController object.
+   *
+   * @param string $entity_type
+   *   The entity type of the access controller instance.
+   * @param array $entity_info
+   *   An array of entity info for the entity type.
+   * @param \Drupal\shortcut\ShortcutSetStorageController $shortcut_set_storage
+   *   The shortcut_set storage controller.
+   */
+  public function __construct($entity_type, array $entity_info, ShortcutSetStorageController $shortcut_set_storage) {
+    parent::__construct($entity_type, $entity_info);
+    $this->shortcutSetStorage = $shortcut_set_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static(
+      $entity_type,
+      $entity_info,
+      $container->get('entity.manager')->getStorageController('shortcut_set')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
+    if ($shortcut_set = $this->shortcutSetStorage->load($entity->bundle())) {
+      return shortcut_set_edit_access($shortcut_set, $account);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    if ($shortcut_set = $this->shortcutSetStorage->load($entity_bundle)) {
+      return shortcut_set_edit_access($shortcut_set, $account);
+    }
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php
new file mode 100644
index 0000000..7179886
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutFormController.php
@@ -0,0 +1,180 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutFormController.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Entity\ContentEntityFormController;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Path\AliasManagerInterface;
+use Drupal\Core\Routing\UrlGeneratorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form controller for the shortcut entity forms.
+ */
+class ShortcutFormController extends ContentEntityFormController {
+
+  /**
+   * The path alias manager.
+   *
+   * @var \Drupal\Core\Path\AliasManagerInterface
+   */
+  protected $aliasManager;
+
+  /**
+   * The URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
+   * Constructs a new action form.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager
+   *   The path alias manager.
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
+   *   The URL generator.
+   * @param \Drupal\Core\Form\FormBuilderInterface $form_builder
+   *   The form builder.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, AliasManagerInterface $alias_manager, UrlGeneratorInterface $url_generator, FormBuilderInterface $form_builder) {
+    $this->entityManager = $entity_manager;
+    $this->aliasManager = $alias_manager;
+    $this->urlGenerator = $url_generator;
+    $this->formBuilder = $form_builder;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.manager'),
+      $container->get('path.alias_manager'),
+      $container->get('url_generator'),
+      $container->get('form_builder')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, array &$form_state) {
+    $form = parent::form($form, $form_state);
+    $entity = $this->entity;
+
+    $form['title'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Name'),
+      '#default_value' => $entity->title->value,
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#required' => TRUE,
+      '#weight' => -10,
+    );
+
+    $form['path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Path'),
+      '#size' => 40,
+      '#maxlength' => 255,
+      '#field_prefix' => $this->urlGenerator->generateFromRoute('<front>', array(), array('absolute' => TRUE)),
+      '#default_value' => $entity->path->value,
+    );
+
+    $form['langcode'] = array(
+      '#title' => t('Language'),
+      '#type' => 'language_select',
+      '#default_value' => $entity->getUntranslated()->language()->id,
+      '#languages' => Language::STATE_ALL,
+    );
+
+    $form['shortcut_set'] = array(
+      '#type' => 'value',
+      '#value' => $entity->bundle(),
+    );
+    $form['route_name'] = array(
+      '#type' => 'value',
+      '#value' => $entity->getRouteName(),
+    );
+    $form['route_parameters'] = array(
+      '#type' => 'value',
+      '#value' => $entity->getRouteParams(),
+    );
+
+    return $form;
+  }
+
+  /**
+   * Overrides EntityFormController::buildEntity().
+   */
+  public function buildEntity(array $form, array &$form_state) {
+    $entity = parent::buildEntity($form, $form_state);
+
+    // Set the computed 'path' value so it can used in the preSave() method to
+    // derive the route name and parameters.
+    $entity->path->value = $form_state['values']['path'];
+
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate(array $form, array &$form_state) {
+    if (!shortcut_valid_link($form_state['values']['path'])) {
+      $this->formBuilder->setErrorByName('path', $form_state, $this->t('The shortcut must correspond to a valid path on the site.'));
+    }
+
+    parent::validate($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, array &$form_state) {
+    $entity = $this->entity;
+    $entity->save();
+
+    if ($entity->isNew()) {
+      $message = $this->t('The shortcut %link has been updated.', array('%link' => $entity->title->value));
+    }
+    else {
+      $message = $this->t('Added a shortcut for %title.', array('%title' => $entity->title->value));
+    }
+    drupal_set_message($message);
+
+    $form_state['redirect_route'] = array(
+      'route_name' => 'shortcut.set_customize',
+      'route_parameters' => array('shortcut_set' => $entity->bundle()),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete(array $form, array &$form_state) {
+    $form_state['redirect_route'] = array(
+      'route_name' => 'shortcut.link_delete',
+      'route_parameters' => array('shortcut' => $this->entity->id()),
+    );
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php
new file mode 100644
index 0000000..7c44cba
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutInterface.php
@@ -0,0 +1,93 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutInterface.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+
+/**
+ * Provides an interface defining a shortcut entity.
+ */
+interface ShortcutInterface extends ContentEntityInterface {
+
+  /**
+   * Returns the title of this shortcut.
+   *
+   * @return string
+   *   The title of this shortcut.
+   */
+  public function getTitle();
+
+  /**
+   * Sets the title of this shortcut.
+   *
+   * @param string $title
+   *   The title of this shortcut.
+   *
+   * @return \Drupal\shortcut\ShortcutInterface
+   *   The called shortcut entity.
+   */
+  public function setTitle($title);
+
+  /**
+   * Returns the weight among shortcuts with the same depth.
+   *
+   * @return int
+   *   The shortcut weight.
+   */
+  public function getWeight();
+
+  /**
+   * Sets the weight among shortcuts with the same depth.
+   *
+   *Â·@param int $weight
+   *   The shortcut weight.
+   *
+   * @return \Drupal\shortcut\ShortcutInterface
+   *   The called shortcut entity.
+   */
+  public function setWeight($weight);
+
+  /**
+   * Returns the route name associated with this shortcut, if any.
+   *
+   * @return string|null
+   *   The route name of this shortcut.
+   */
+  public function getRouteName();
+
+  /**
+   * Sets the route name associated with this shortcut.
+   *
+   * @param string|null $route_name
+   *   The route name associated with this shortcut.
+   *
+   * @return \Drupal\shortcut\ShortcutInterface
+   *   The called shortcut entity.
+   */
+  public function setRouteName($route_name);
+
+  /**
+   * Returns the route parameters associated with this shortcut, if any.
+   *
+   * @return array
+   *   The route parameters of this shortcut.
+   */
+  public function getRouteParams();
+
+  /**
+   * Sets the route parameters associated with this shortcut.
+   *
+   * @param array $route_parameters
+   *   The route parameters associated with this shortcut.
+   *
+   * @return \Drupal\shortcut\ShortcutInterface
+   *   The called shortcut entity.
+  */
+  public function setRouteParams($route_parameters);
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPath.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPath.php
new file mode 100644
index 0000000..885d304
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPath.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutPath.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\Field\Plugin\Field\FieldType\StringItem;
+use Drupal\Core\TypedData\DataDefinition;
+
+/**
+ * The field item for the 'path' field.
+ */
+class ShortcutPath extends StringItem {
+
+  /**
+   * Definitions of the contained properties.
+   *
+   * @see self::getPropertyDefinitions()
+   *
+   * @var array
+   */
+  static $propertyDefinitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['value'] = DataDefinition::create('string')
+        ->setLabel(t('String value'))
+        ->setComputed(TRUE)
+        ->setClass('\Drupal\shortcut\ShortcutPathValue');
+    }
+    return static::$propertyDefinitions;
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathValue.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathValue.php
new file mode 100644
index 0000000..2c8b4e5
--- /dev/null
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutPathValue.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\ShortcutPathValue.
+ */
+
+namespace Drupal\shortcut;
+
+use Drupal\Core\TypedData\TypedData;
+
+/**
+ * A computed property for the string value of the path field.
+ */
+class ShortcutPathValue extends TypedData {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue() {
+    if (!isset($this->value)) {
+      if (!isset($this->parent)) {
+        throw new \InvalidArgumentException('Computed properties require context for computation.');
+      }
+
+      $entity = $this->parent->getEntity();
+      if ($route_name = $entity->getRouteName()) {
+        $path = \Drupal::urlGenerator()->getPathFromRoute($route_name, $entity->getRouteParams());
+        $this->value = trim($path, '/');
+      }
+      else {
+        $this->value = NULL;
+      }
+    }
+    return $this->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    // Normalize the path in case it is an alias.
+    $value = \Drupal::service('path.alias_manager')->getSystemPath($value);
+    if (empty($value)) {
+      $value = '<front>';
+    }
+
+    parent::setValue($value, $notify);
+  }
+
+}
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php
index 5fa358a..4289f78 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\shortcut\Entity\ShortcutSetInterface.
+ * Contains \Drupal\shortcut\ShortcutSetInterface.
  */
 
 namespace Drupal\shortcut;
@@ -14,4 +14,24 @@
  */
 interface ShortcutSetInterface extends ConfigEntityInterface {
 
+  /**
+   * Resets the link weights in a shortcut set to match their current order.
+   *
+   * This function can be used, for example, when a new shortcut link is added
+   * to the set. If the link is added to the end of the array and this function
+   * is called, it will force that link to display at the end of the list.
+   *
+   * @return \Drupal\shortcut\ShortcutSetInterface
+   *   The shortcut set.
+   */
+  public function resetLinkWeights();
+
+  /**
+   * Returns all the shortcuts from a shortcut set.
+   *
+   * @return \Drupal\shortcut\ShortcutInterface[]
+   *   An array of shortcut entities.
+   */
+  public function getShortcuts();
+
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php
index 6215a87..179992c 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetListController.php
@@ -31,7 +31,7 @@ public function getOperations(EntityInterface $entity) {
     $uri = $entity->uri();
 
     if (isset($operations['edit'])) {
-      $operations['edit']['title'] = t('Edit menu');
+      $operations['edit']['title'] = t('Edit shortcut set');
       $operations['edit']['href'] = $uri['path'] . '/edit';
     }
 
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php
index 7c0dee5..76f9582 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/ShortcutSetStorageController.php
@@ -8,10 +8,9 @@
 namespace Drupal\shortcut;
 
 use Drupal\Core\Config\Entity\ConfigStorageController;
-use Drupal\shortcut\ShortcutSetInterface;
 
 /**
- * Defines a storage controller for shortcut entities.
+ * Defines a storage controller for shortcut_set entities.
  */
 class ShortcutSetStorageController extends ConfigStorageController implements ShortcutSetStorageControllerInterface {
 
@@ -63,4 +62,5 @@ public function getAssignedToUser($account) {
   public function countAssignedUsers(ShortcutSetInterface $shortcut_set) {
     return db_query('SELECT COUNT(*) FROM {shortcut_set_users} WHERE set_name = :name', array(':name' => $shortcut_set->id()))->fetchField();
   }
+
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
index bd2d194..832ca64 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php
@@ -17,7 +17,7 @@ class ShortcutLinksTest extends ShortcutTestBase {
    *
    * @var array
    */
-  public static $modules = array('router_test');
+  public static $modules = array('router_test', 'views');
 
   public static function getInfo() {
     return array(
@@ -30,7 +30,7 @@ public static function getInfo() {
   /**
    * Tests that creating a shortcut works properly.
    */
-  function testShortcutLinkAdd() {
+  public function testShortcutLinkAdd() {
     $set = $this->set;
 
     // Create an alias for the node so we can test aliases.
@@ -55,15 +55,14 @@ function testShortcutLinkAdd() {
     foreach ($test_cases as $test) {
       $title = $this->randomName();
       $form_data = array(
-        'shortcut_link[link_title]' => $title,
-        'shortcut_link[link_path]'  => $test['path'],
+        'title' => $title,
+        'path' => $test['path'],
       );
       $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
       $this->assertResponse(200);
       $saved_set = shortcut_set_load($set->id());
-      $paths = $this->getShortcutInformation($saved_set, 'link_path');
-      $test_path = empty($test['path']) ? '<front>' : $test['path'];
-      $this->assertTrue(in_array($this->container->get('path.alias_manager')->getSystemPath($test_path), $paths), 'Shortcut created: '. $test['path']);
+      $paths = $this->getShortcutInformation($saved_set, 'path');
+      $this->assertTrue(in_array($this->container->get('path.alias_manager')->getSystemPath($test['path']), $paths), 'Shortcut created: ' . $test['path']);
       $this->assertLink($title, 0, 'Shortcut link found on the page.');
     }
   }
@@ -71,30 +70,32 @@ function testShortcutLinkAdd() {
   /**
    * Tests that the "add to shortcut" link changes to "remove shortcut".
    */
-  function testShortcutQuickLink() {
+  public function testShortcutQuickLink() {
     theme_enable(array('seven'));
     \Drupal::config('system.theme')->set('admin', 'seven')->save();
     $this->container->get('config.factory')->get('node.settings')->set('use_admin_theme', '1')->save();
 
-    $link = reset($this->set->links);
+    $shortcuts = $this->set->getShortcuts();
+    $shortcut = reset($shortcuts);
 
-    $this->drupalGet($link->link_path);
+    $this->drupalGet($shortcut->path->value);
     $this->assertRaw(t('Remove from %title shortcuts', array('%title' => $this->set->label())), '"Add to shortcuts" link properly switched to "Remove from shortcuts".');
   }
 
   /**
    * Tests that shortcut links can be renamed.
    */
-  function testShortcutLinkRename() {
+  public function testShortcutLinkRename() {
     $set = $this->set;
 
     // Attempt to rename shortcut link.
     $new_link_name = $this->randomName();
 
-    $link = reset($set->links);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $link->mlid, array('shortcut_link[link_title]' => $new_link_name, 'shortcut_link[link_path]' => $link->link_path), t('Save'));
+    $shortcuts = $set->getShortcuts();
+    $shortcut = reset($shortcuts);
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title' => $new_link_name, 'path' => $shortcut->path->value), t('Save'));
     $saved_set = shortcut_set_load($set->id());
-    $titles = $this->getShortcutInformation($saved_set, 'link_title');
+    $titles = $this->getShortcutInformation($saved_set, 'title');
     $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
     $this->assertLink($new_link_name, 0, 'Renamed shortcut link appears on the page.');
   }
@@ -102,36 +103,49 @@ function testShortcutLinkRename() {
   /**
    * Tests that changing the path of a shortcut link works.
    */
-  function testShortcutLinkChangePath() {
+  public function testShortcutLinkChangePath() {
     $set = $this->set;
 
     // Tests changing a shortcut path.
     $new_link_path = 'admin/config';
 
-    $link = reset($set->links);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $link->mlid, array('shortcut_link[link_title]' => $link->link_title, 'shortcut_link[link_path]' => $new_link_path), t('Save'));
+    $shortcuts = $set->getShortcuts();
+    $shortcut = reset($shortcuts);
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title' => $shortcut->title->value, 'path' => $new_link_path), t('Save'));
     $saved_set = shortcut_set_load($set->id());
-    $paths = $this->getShortcutInformation($saved_set, 'link_path');
+    $paths = $this->getShortcutInformation($saved_set, 'path');
     $this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
     $this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.');
   }
 
   /**
+   * Tests that changing the route of a shortcut link works.
+   */
+  public function testShortcutLinkChangeRoute() {
+    $this->drupalLogin($this->root_user);
+    $this->drupalGet('admin/content');
+    $this->assertResponse(200);
+    // Disable the view.
+    entity_load('view', 'content')->disable()->save();
+    $this->drupalGet('admin/content');
+    $this->assertResponse(200);
+  }
+
+  /**
    * Tests deleting a shortcut link.
    */
-  function testShortcutLinkDelete() {
+  public function testShortcutLinkDelete() {
     $set = $this->set;
 
-    $link = reset($set->links);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $link->mlid . '/delete', array(), 'Delete');
+    $shortcuts = $set->getShortcuts();
+    $shortcut = reset($shortcuts);
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id() . '/delete', array(), 'Delete');
     $saved_set = shortcut_set_load($set->id());
-    $mlids = $this->getShortcutInformation($saved_set, 'mlid');
-    $this->assertFalse(in_array($link->mlid, $mlids), 'Successfully deleted a shortcut.');
+    $ids = $this->getShortcutInformation($saved_set, 'id');
+    $this->assertFalse(in_array($shortcut->id(), $ids), 'Successfully deleted a shortcut.');
 
-    // Delete all the remaining shortcut menu links.
-    foreach (array_filter($mlids) as $mlid) {
-      menu_link_delete($mlid);
-    }
+    // Delete all the remaining shortcut links.
+    entity_delete_multiple('shortcut', array_filter($ids));
 
     // Get the front page to check that no exceptions occur.
     $this->drupalGet('');
@@ -143,7 +157,7 @@ function testShortcutLinkDelete() {
    * Tests that the "Add to shortcuts" link is not displayed on a page not
    * found or a page the user does not have access to.
    */
-  function testNoShortcutLink() {
+  public function testNoShortcutLink() {
     // Change to a theme that displays shortcuts.
     \Drupal::config('system.theme')
       ->set('default', 'seven')
@@ -158,7 +172,8 @@ function testNoShortcutLink() {
 
     // Verify that the testing mechanism works by verifying the shortcut
     // link appears on admin/content/node.
-    $this->drupalGet('admin/content/node');
-    $this->assertRaw('add-shortcut', 'Add to shortcuts link was shown on a page the user does have access to.');
+    $this->drupalGet('admin/content');
+    $this->assertRaw('remove-shortcut', 'Remove from shortcuts link was shown on a page the user does have access to.');
   }
+
 }
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php
index 470227a..06493ec 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutSetsTest.php
@@ -89,23 +89,6 @@ function testShortcutSetSwitchNoSetName() {
   }
 
   /**
-   * Tests that save() correctly updates existing links.
-   */
-  function testShortcutSetSave() {
-    $set = $this->set;
-    $old_mlids = $this->getShortcutInformation($set, 'mlid');
-
-    $menu_link = $this->generateShortcutLink('admin', $this->randomName());
-    $menu_link->save();
-    $set->links[$menu_link->uuid()] = $menu_link;
-    $set->save();
-    $saved_set = shortcut_set_load($set->id());
-
-    $new_mlids = $this->getShortcutInformation($saved_set, 'mlid');
-    $this->assertTrue(count(array_intersect($old_mlids, $new_mlids)) == count($old_mlids), 'Shortcut::save() did not inadvertently change existing mlids.');
-  }
-
-  /**
    * Tests renaming a shortcut set.
    */
   function testShortcutSetRename() {
@@ -113,7 +96,7 @@ function testShortcutSetRename() {
 
     $new_label = $this->randomName();
     $this->drupalGet('admin/config/user-interface/shortcut');
-    $this->clickLink(t('Edit menu'));
+    $this->clickLink(t('Edit shortcut set'));
     $this->drupalPostForm(NULL, array('label' => $new_label), t('Save'));
     $set = shortcut_set_load($set->id());
     $this->assertTrue($set->label() == $new_label, 'Shortcut set has been successfully renamed.');
@@ -168,7 +151,7 @@ function testShortcutSetDeleteDefault() {
    */
   function testShortcutSetCreateWithSetName() {
     $random_name = $this->randomName();
-    $new_set = $this->generateShortcutSet($random_name, $random_name, TRUE);
+    $new_set = $this->generateShortcutSet($random_name, $random_name);
     $sets = entity_load_multiple('shortcut_set');
     $this->assertTrue(isset($sets[$random_name]), 'Successfully created a shortcut set with a defined set name.');
     $this->drupalGet('user/' . $this->admin_user->id() . '/shortcuts');
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
index 76d8fb9..64e5370 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\shortcut\Tests;
 
+use Drupal\shortcut\ShortcutSetInterface;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -38,6 +39,8 @@
 
   /**
    * Site-wide default shortcut set.
+   *
+   * @var \Drupal\shortcut\ShortcutSetInterface
    */
   protected $set;
 
@@ -50,22 +53,21 @@ function setUp() {
       $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
 
       // Populate the default shortcut set.
-      $shortcut_set = shortcut_set_load('default');
-      $menu_link = entity_create('menu_link', array(
-        'link_path' => 'node/add',
-        'link_title' => t('Add content'),
+      $shortcut = entity_create('shortcut', array(
+        'set' => 'default',
+        'title' => t('Add content'),
         'weight' => -20,
+        'path' => 'node/add',
       ));
-      $menu_link->save();
-      $shortcut_set->links[$menu_link->uuid()] = $menu_link;
-      $menu_item = entity_create('menu_link', array(
-        'link_path' => 'admin/content',
-        'link_title' => t('All content'),
+      $shortcut->save();
+
+      $shortcut = entity_create('shortcut', array(
+        'set' => 'default',
+        'title' => t('All content'),
         'weight' => -19,
+        'path' => 'admin/content',
       ));
-      $menu_item->save();
-      $shortcut_set->links[$menu_item->uuid()] = $menu_item;
-      $shortcut_set->save();
+      $shortcut->save();
     }
 
     // Create users.
@@ -84,53 +86,36 @@ function setUp() {
   /**
    * Creates a generic shortcut set.
    */
-  function generateShortcutSet($label = '', $id = NULL, $default_links = TRUE) {
+  function generateShortcutSet($label = '', $id = NULL) {
     $set = entity_create('shortcut_set', array(
       'id' => isset($id) ? $id : strtolower($this->randomName()),
       'label' => empty($label) ? $this->randomString() : $label,
     ));
-    if ($default_links) {
-      $menu_link = $this->generateShortcutLink('node/add');
-      $set->links[$menu_link->uuid()] = $menu_link;
-      $menu_link = $this->generateShortcutLink('admin/content');
-      $set->links[$menu_link->uuid()] = $menu_link;
-    }
     $set->save();
     return $set;
   }
 
   /**
-   * Creates a generic shortcut link.
-   */
-  function generateShortcutLink($path, $title = '') {
-    $link = entity_create('menu_link', array(
-      'link_path' => $path,
-      'link_title' => !empty($title) ? $title : $this->randomName(),
-    ));
-    $link->save();
-
-    return $link;
-  }
-
-  /**
    * Extracts information from shortcut set links.
    *
-   * @param object $set
+   * @param \Drupal\shortcut\ShortcutSetInterface $set
    *   The shortcut set object to extract information from.
    * @param string $key
    *   The array key indicating what information to extract from each link:
-   *    - 'link_path': Extract link paths.
-   *    - 'link_title': Extract link titles.
-   *    - 'mlid': Extract the menu link item ID numbers.
+   *    - 'title': Extract shortcut titles.
+   *    - 'path': Extract shortcut paths.
+   *    - 'id': Extract the shortcut ID.
    *
    * @return array
    *   Array of the requested information from each link.
    */
-  function getShortcutInformation($set, $key) {
+  function getShortcutInformation(ShortcutSetInterface $set, $key) {
     $info = array();
-    foreach ($set->links as $link) {
-      $info[] = $link->{$key};
+    \Drupal::entityManager()->getStorageController('shortcut')->resetCache();
+    foreach ($set->getShortcuts() as $shortcut) {
+      $info[] = $shortcut->{$key}->value;
     }
     return $info;
   }
+
 }
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index db98d32..876c9f8 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -5,9 +5,6 @@
  * Administrative page callbacks for the shortcut module.
  */
 
-use Symfony\Component\HttpFoundation\RedirectResponse;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
 /**
  * Form callback: builds the form for switching shortcut sets.
  *
@@ -142,7 +139,6 @@ function shortcut_set_switch_submit($form, &$form_state) {
     $set = entity_create('shortcut_set', array(
       'id' => $form_state['values']['id'],
       'label' => $form_state['values']['label'],
-      'links' => $default_set->links,
     ));
     $set->save();
     $replacements = array(
@@ -178,192 +174,3 @@ function shortcut_set_switch_submit($form, &$form_state) {
   // Assign the shortcut set to the provided user account.
   shortcut_set_assign_user($set, $account);
 }
-
-/**
- * Form callback: builds the form for adding a new shortcut link.
- *
- * @param $form
- *   An associative array containing the structure of the form.
- * @param $form_state
- *   An associative array containing the current state of the form.
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
- *   An object representing the shortcut set to which the link will be added.
- *
- * @return
- *   An array representing the form definition.
- *
- * @ingroup forms
- * @see shortcut_link_edit_validate()
- * @see shortcut_link_add_submit()
- *
- * @deprecated Use \Drupal\shortcut\Form\ShortcutForm::add()
- */
-function shortcut_link_add($form, &$form_state, $shortcut_set) {
-  $form['#title'] = t('Add new shortcut');
-  $form['shortcut_set'] = array(
-    '#type' => 'value',
-    '#value' => $shortcut_set,
-  );
-  $form += _shortcut_link_form_elements();
-  return $form;
-}
-
-/**
- * Form callback: builds the form for editing a shortcut link.
- *
- * @param $form
- *   An associative array containing the structure of the form.
- * @param $form_state
- *   An associative array containing the current state of the form.
- * @param $shortcut_link
- *   An array representing the link that is being edited.
- *
- * @return
- *   An array representing the form definition.
- *
- * @ingroup forms
- * @see shortcut_link_edit_validate()
- * @see shortcut_link_edit_submit()
- *
- * @deprecated Use \Drupal\shortcut\Form\ShortcutForm::edit()
- */
-function shortcut_link_edit($form, &$form_state, $shortcut_link) {
-  $form['#title'] = t('Editing @shortcut', array('@shortcut' => $shortcut_link['link_title']));
-  $form['original_shortcut_link'] = array(
-    '#type' => 'value',
-    '#value' => $shortcut_link,
-  );
-  $form += _shortcut_link_form_elements($shortcut_link);
-  return $form;
-}
-
-/**
- * Helper function for building a form for adding or editing shortcut links.
- *
- * @param $shortcut_link
- *   (optional) An array representing the shortcut link that will be edited. If
- *   not provided, a new link will be created.
- *
- * @return
- *   An array of form elements.
- */
-function _shortcut_link_form_elements($shortcut_link = NULL) {
-  if (!isset($shortcut_link)) {
-    $shortcut_link = entity_create('menu_link', array(
-      'link_title' => '',
-      'link_path' => ''
-    ));
-  }
-  else {
-    $shortcut_link['link_path'] = ($shortcut_link['link_path'] == '<front>') ? '' : \Drupal::service('path.alias_manager')->getPathAlias($shortcut_link['link_path']);
-  }
-
-  $form['shortcut_link']['#tree'] = TRUE;
-  $form['shortcut_link']['link_title'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Name'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#default_value' => $shortcut_link['link_title'],
-    '#required' => TRUE,
-  );
-
-  $form['shortcut_link']['link_path'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Path'),
-    '#size' => 40,
-    '#maxlength' => 255,
-    '#field_prefix' => url(NULL, array('absolute' => TRUE)),
-    '#default_value' => $shortcut_link['link_path'],
-  );
-
-  $form['#validate'][] = 'shortcut_link_edit_validate';
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Validation handler for the shortcut link add and edit forms.
- */
-function shortcut_link_edit_validate($form, &$form_state) {
-  if (!shortcut_valid_link($form_state['values']['shortcut_link']['link_path'])) {
-    form_set_error('shortcut_link][link_path', $form_state, t('The link must correspond to a valid path on the site.'));
-  }
-}
-
-/**
- * Submit handler for shortcut_link_edit().
- */
-function shortcut_link_edit_submit($form, &$form_state) {
-  // Normalize the path in case it is an alias.
-  $shortcut_path = \Drupal::service('path.alias_manager')->getSystemPath($form_state['values']['shortcut_link']['link_path']);
-  if (empty($shortcut_path)) {
-    $shortcut_path = '<front>';
-  }
-  $form_state['values']['shortcut_link']['link_path'] = $shortcut_path;
-
-  $shortcut_link = $form_state['values']['original_shortcut_link'];
-  foreach ($form_state['values']['shortcut_link'] as $key => $value) {
-   $shortcut_link[$key] = $value;
-  }
-
-  menu_link_save($shortcut_link);
-  $set_name = str_replace('shortcut-', '' , $shortcut_link['menu_name']);
-  $form_state['redirect_route'] = array(
-    'route_name' => 'shortcut.set_customize',
-    'route_parameters' => array(
-      'shortcut_set' => $set_name,
-    ),
-  );
-  drupal_set_message(t('The shortcut %link has been updated.', array('%link' => $shortcut_link['link_title'])));
-}
-
-/**
- * Submit handler for shortcut_link_add().
- */
-function shortcut_link_add_submit($form, &$form_state) {
-  // Add the shortcut link to the set.
-  $shortcut_set = $form_state['values']['shortcut_set'];
-  $shortcut_link = $form_state['values']['shortcut_link'];
-  $shortcut_link['menu_name'] = $shortcut_set->id();
-  shortcut_admin_add_link($shortcut_link, $shortcut_set);
-  $shortcut_set->save();
-  $form_state['redirect_route'] = array(
-    'route_name' => 'shortcut.set_customize',
-    'route_parameters' => array(
-      'shortcut_set' => $shortcut_set->id(),
-    ),
-  );
-  drupal_set_message(t('Added a shortcut for %title.', array('%title' => $shortcut_link['link_title'])));
-}
-
-/**
- * Adds a link to the end of a shortcut set, keeping within a prescribed limit.
- *
- * @param $link
- *   An array representing a shortcut link.
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
- *   An object representing the shortcut set which the link will be added to.
- *   The links in the shortcut set will be re-weighted so that the new link is
- *   at the end, and some existing links may be disabled (if the $limit
- *   parameter is provided).
- */
-function shortcut_admin_add_link($shortcut_link, &$shortcut_set) {
-  // Normalize the path in case it is an alias.
-  $shortcut_link['link_path'] = \Drupal::service('path.alias_manager')->getSystemPath($shortcut_link['link_path']);
-  if (empty($shortcut_link['link_path'])) {
-    $shortcut_link['link_path'] = '<front>';
-  }
-  $menu_link = entity_create('menu_link', $shortcut_link);
-  $menu_link->save();
-
-  // Add the link to the end of the list.
-  $shortcut_set->links[$menu_link->uuid()] = $menu_link;
-  shortcut_set_reset_link_weights($shortcut_set);
-}
diff --git a/core/modules/shortcut/shortcut.info.yml b/core/modules/shortcut/shortcut.info.yml
index a5dc32d..dde0e9c 100644
--- a/core/modules/shortcut/shortcut.info.yml
+++ b/core/modules/shortcut/shortcut.info.yml
@@ -4,6 +4,4 @@ description: 'Allows users to manage customizable lists of shortcut links.'
 package: Core
 version: VERSION
 core: 8.x
-dependencies:
-  - menu_link
 configure: shortcut.set_admin
diff --git a/core/modules/shortcut/shortcut.install b/core/modules/shortcut/shortcut.install
index 420d8c2..3a00775 100644
--- a/core/modules/shortcut/shortcut.install
+++ b/core/modules/shortcut/shortcut.install
@@ -5,23 +5,104 @@
  * Install, update and uninstall functions for the shortcut module.
  */
 
-use Drupal\Component\Uuid\Uuid;
-
-/**
- * Implements hook_uninstall().
- */
-function shortcut_uninstall() {
-  // Delete the menu links associated with each shortcut set.
-  // @todo find a way to clean-up associated menu links.
-  /*foreach (entity_load_multiple('shortcut') as $shortcut_set) {
-    menu_delete_links('shortcut-' . $shortcut_set->id());
-  }*/
-}
+use Drupal\Core\Database\Database;
+use Drupal\Core\Language\Language;
 
 /**
  * Implements hook_schema().
  */
 function shortcut_schema() {
+  $schema['shortcut'] = array(
+    'description' => 'Stores shortcut items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique shortcut ID.',
+      ),
+      'uuid' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+        'description' => 'Unique Key: Universally unique identifier for this shortcut.',
+      ),
+      'shortcut_set' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The bundle of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of the original variant of this shortcut.',
+      ),
+      'weight' => array(
+        'description' => 'Weight among shortcuts in the same shortcut set.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'route_name' => array(
+        'description' => 'The machine name of a defined Symfony Route this menu item represents.',
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+      'route_parameters' => array(
+        'description' => 'Serialized array of route parameters of this shortcut.',
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+
+  $schema['shortcut_field_data'] = array(
+    'description' => 'Stores shortcut properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {shortcut}.id of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of this variant of this shortcut.',
+      ),
+      'default_langcode' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+      ),
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => FALSE,
+        'description' => 'The title of the shortcut.',
+      ),
+    ),
+    'foreign keys' => array(
+      'shortcut' => array(
+        'table' => 'shortcut',
+        'columns' => array('id' => 'id'),
+      ),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
+
   $schema['shortcut_set_users'] = array(
     'description' => 'Maps users to shortcut sets.',
     'fields' => array(
@@ -65,7 +146,7 @@ function shortcut_schema() {
  */
 
 /**
- * Migrate shortcuts into configuration.
+ * Migrate shortcut sets into configuration.
  */
 function shortcut_update_8000() {
   $uuid = \Drupal::service('uuid');
@@ -101,6 +182,143 @@ function shortcut_update_8001() {
 }
 
 /**
+ * Create the database tables for the new 'shortcut' entity type.
+ */
+function shortcut_update_8002() {
+  $tables['shortcut'] = array(
+    'description' => 'Stores shortcut items.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'serial',
+        'not null' => TRUE,
+        'description' => 'Primary Key: Unique shortcut ID.',
+      ),
+      'uuid' => array(
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => FALSE,
+        'description' => 'Unique Key: Universally unique identifier for this shortcut.',
+      ),
+      'shortcut_set' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The bundle of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of the original variant of this shortcut.',
+      ),
+      'weight' => array(
+        'description' => 'Weight among shortcuts in the same shortcut set.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'route_name' => array(
+        'description' => 'The machine name of a defined Symfony Route this menu item represents.',
+        'type' => 'varchar',
+        'length' => 255,
+      ),
+      'route_parameters' => array(
+        'description' => 'Serialized array of route parameters of this shortcut.',
+        'type' => 'blob',
+        'size' => 'big',
+        'not null' => FALSE,
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('id'),
+    'unique keys' => array(
+      'uuid' => array('uuid'),
+    ),
+  );
+
+  $tables['shortcut_field_data'] = array(
+    'description' => 'Stores shortcut properties.',
+    'fields' => array(
+      'id' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'description' => 'The {shortcut}.id of the shortcut.',
+      ),
+      'langcode' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The {language}.langcode of this variant of this shortcut.',
+      ),
+      'default_langcode' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
+      ),
+      'title' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => FALSE,
+        'description' => 'The title of the shortcut.',
+      ),
+    ),
+    'foreign keys' => array(
+      'shortcut' => array(
+        'table' => 'shortcut',
+        'columns' => array('id' => 'id'),
+      ),
+    ),
+    'primary key' => array('id', 'langcode'),
+  );
+
+  $schema = Database::getConnection()->schema();
+  $schema->createTable('shortcut', $tables['shortcut']);
+  $schema->createTable('shortcut_field_data', $tables['shortcut_field_data']);
+}
+
+/**
+ * Migrate shortcuts into their own storage tables.
+ */
+function shortcut_update_8003() {
+  $shortcuts = db_select('menu_links', 'ml')
+    ->fields('ml')
+    ->condition('menu_name', 'shortcut-%', 'LIKE')
+    ->execute()
+    ->fetchAllAssoc('mlid');
+
+  foreach ($shortcuts as $shortcut) {
+    $record_id = db_insert('shortcut')
+      ->fields(array(
+        'uuid' => $shortcut->uuid,
+        'shortcut_set' => substr($shortcut->menu_name, 9),
+        'langcode' => Language::LANGCODE_NOT_SPECIFIED,
+        'weight' => $shortcut->weight,
+        'route_name' => $shortcut->route_name,
+        'route_parameters' => $shortcut->route_parameters,
+      ))
+      ->execute();
+    db_insert('shortcut_field_data')
+      ->fields(array(
+        'id' => $record_id,
+        'langcode' => Language::LANGCODE_NOT_SPECIFIED,
+        'default_langcode' => 1,
+        'title' => $shortcut->link_title,
+      ))
+      ->execute();
+  }
+  if (!empty($shortcuts)) {
+    db_delete('menu_links')
+      ->condition('mlid', array_keys($shortcuts), 'IN')
+      ->execute();
+  }
+}
+
+/**
  * @} End of "addtogroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
  */
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index d0594b0..8c7c869 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -5,7 +5,8 @@
  * Allows users to manage customizable lists of shortcut links.
  */
 
-use Drupal\shortcut\Entity\Shortcut;
+use Drupal\Core\Routing\UrlMatcher;
+use Drupal\shortcut\ShortcutSetInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -40,29 +41,6 @@ function shortcut_help($path, $arg) {
 }
 
 /**
- * Implements hook_entity_bundle_info().
- */
-function shortcut_entity_bundle_info() {
-  $bundles['menu_link']['shortcut'] = array(
-    'label' => t('Shortcut'),
-    'translatable' => FALSE,
-  );
-  return $bundles;
-}
-
-/**
- * Implements hook_TYPE_load().
- */
-function shortcut_menu_link_load($entities) {
-  foreach ($entities as $entity) {
-    // Change the bundle of menu links related to a shortcut.
-    if (strpos($entity->menu_name, 'shortcut-') === 0) {
-      $entity->bundle = 'shortcut';
-    }
-  }
-}
-
-/**
  * Implements hook_permission().
  */
 function shortcut_permission() {
@@ -96,11 +74,11 @@ function shortcut_menu() {
     'title callback' => 'entity_page_label',
     'title arguments' => array(5),
   );
-  $items['admin/config/user-interface/shortcut/link/%menu_link'] = array(
+  $items['admin/config/user-interface/shortcut/link/%shortcut'] = array(
     'title' => 'Edit shortcut',
     'route_name' => 'shortcut.link_edit',
   );
-  $items['admin/config/user-interface/shortcut/link/%menu_link/delete'] = array(
+  $items['admin/config/user-interface/shortcut/link/%shortcut/delete'] = array(
     'title' => 'Delete shortcut',
     'route_name' => 'shortcut.link_delete',
   );
@@ -121,15 +99,15 @@ function shortcut_admin_paths() {
 /**
  * Access callback for editing a shortcut set.
  *
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
+ * @param Drupal\shortcut\ShortcutSetInterface $shortcut_set
  *   (optional) The shortcut set to be edited. If not set, the current user's
  *   shortcut set will be used.
  *
- * @return
+ * @return bool
  *   TRUE if the current user has access to edit the shortcut set, FALSE
  *   otherwise.
  */
-function shortcut_set_edit_access($shortcut_set = NULL) {
+function shortcut_set_edit_access(ShortcutSetInterface $shortcut_set = NULL) {
   $account = \Drupal::currentUser();
   // Sufficiently-privileged users can edit their currently displayed shortcut
   // set, but not other sets. Shortcut administrators can edit any set.
@@ -150,7 +128,7 @@ function shortcut_set_edit_access($shortcut_set = NULL) {
  *   permissions will be checked for switching the logged-in user's own
  *   shortcut set.
  *
- * @return
+ * @return bool
  *   TRUE if the current user has access to switch the shortcut set of the
  *   provided account, FALSE otherwise.
  */
@@ -177,41 +155,15 @@ function shortcut_set_switch_access($account = NULL) {
 }
 
 /**
- * Access callback for editing a link in a shortcut set.
- */
-function shortcut_link_access($menu_link) {
-  // The link must belong to a shortcut set that the current user has access
-  // to edit.
-  $set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
-  if ($shortcut_set = shortcut_set_load($set_name)) {
-    return shortcut_set_edit_access($shortcut_set);
-  }
-  return FALSE;
-}
-
-/**
- * Implements hook_menu_link_delete().
- */
-function shortcut_menu_link_delete($menu_link) {
-  // If the deleted menu link was in a shortcut set, remove it.
-  if (strpos($menu_link->menu_name, 'shortcut-') === 0) {
-    $shortcut = entity_load('shortcut_set', str_replace('shortcut-', '', $menu_link->menu_name));
-    unset($shortcut->links[$menu_link->uuid]);
-    $shortcut->save();
-  }
-}
-
-/**
  * Loads the data for a shortcut set.
  *
- * @param $id
+ * @param string $id
  *   The machine-name of the shortcut set to load.
  *
- * @return object
+ * @return \Drupal\shortcut\ShortcutSetInterface|null
  *   If the shortcut set exists, an object containing the following properties:
  *   - 'id': The internal name of the shortcut set.
  *   - 'label': The title of the shortcut set.
- *   - 'links': An array of links associated with this shortcut set.
  *   If the shortcut set does not exist, the function returns NULL.
  */
 function shortcut_set_load($id) {
@@ -219,24 +171,6 @@ function shortcut_set_load($id) {
 }
 
 /**
- * Resets the link weights in a shortcut set to match their current order.
- *
- * This function can be used, for example, when a new shortcut link is added to
- * the set. If the link is added to the end of the array and this function is
- * called, it will force that link to display at the end of the list.
- *
- * @param object $shortcut_set
- *   An object representing a shortcut set. The link weights of the passed-in
- *   object will be reset as described above.
- */
-function shortcut_set_reset_link_weights(&$shortcut_set) {
-  $weight = -50;
-  foreach ($shortcut_set->links as $menu_link) {
-    $menu_link->weight = ++$weight;
-  }
-}
-
-/**
  * Assigns a user to a particular shortcut set.
  *
  * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
@@ -385,19 +319,41 @@ function shortcut_valid_link($path) {
 /**
  * Returns an array of shortcut links, suitable for rendering.
  *
- * @param $shortcut_set Drupal\shortcut\Entity\Shortcut
+ * @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
  *   (optional) An object representing the set whose links will be displayed.
  *   If not provided, the user's current set will be displayed.
- * @return
+ *
+ * @return \Drupal\shortcut\ShortcutInterface[]
  *   An array of shortcut links, in the format returned by the menu system.
  *
  * @see menu_tree()
  */
 function shortcut_renderable_links($shortcut_set = NULL) {
+  $shortcut_links = array();
+
   if (!isset($shortcut_set)) {
     $shortcut_set = shortcut_current_displayed_set();
   }
-  return menu_tree('shortcut-' . $shortcut_set->id());
+
+  $shortcuts = \Drupal::entityManager()->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id()));
+  foreach ($shortcuts as $shortcut) {
+    $links[] = array(
+      'title' => $shortcut->label(),
+      'href' => $shortcut->path->value,
+    );
+  }
+
+  if (!empty($links)) {
+    $shortcut_links = array(
+      '#theme' => 'links__toolbar_shortcuts',
+      '#links' => $links,
+      '#attributes' => array(
+        'class' => array('menu'),
+      ),
+    );
+  }
+
+  return $shortcut_links;
 }
 
 /**
@@ -419,10 +375,11 @@ function shortcut_preprocess_page(&$variables) {
   // pages).
   if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) {
     $link = current_path();
-    $query_parameters = drupal_get_query_parameters();
-    if (!empty($query_parameters)) {
-     $link .= '?' . drupal_http_build_query($query_parameters);
+    if (!($route_info = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($link))) {
+      // Bail out early if we couldn't find a matching route.
+      return;
     }
+
     $query = array(
      'link' => $link,
      'name' => drupal_get_title(),
@@ -432,13 +389,14 @@ function shortcut_preprocess_page(&$variables) {
     $shortcut_set = shortcut_current_displayed_set();
 
     // Check if $link is already a shortcut and set $link_mode accordingly.
-    foreach ($shortcut_set->links as $shortcut) {
-      if ($link == $shortcut['link_path']) {
-        $mlid = $shortcut['mlid'];
+    $shortcuts = \Drupal::entityManager()->getStorageController('shortcut')->loadByProperties(array('shortcut_set' => $shortcut_set->id()));
+    foreach ($shortcuts as $shortcut) {
+      if ($shortcut->getRouteName() == $route_info[0] && $shortcut->getRouteParams() == $route_info[1]) {
+        $shortcut_id = $shortcut->id();
         break;
       }
     }
-    $link_mode = isset($mlid) ? "remove" : "add";
+    $link_mode = isset($shortcut_id) ? "remove" : "add";
 
     if ($link_mode == "add") {
       $link_text = shortcut_set_switch_access() ? t('Add to %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->label())) : t('Add to shortcuts');
@@ -446,10 +404,10 @@ function shortcut_preprocess_page(&$variables) {
       $route_parameters = array('shortcut_set' => $shortcut_set->id());
     }
     else {
-      $query['mlid'] = $mlid;
+      $query['id'] = $shortcut_id;
       $link_text = shortcut_set_switch_access() ? t('Remove from %shortcut_set shortcuts', array('%shortcut_set' => $shortcut_set->label())) : t('Remove from shortcuts');
       $route_name = 'shortcut.link_delete';
-      $route_parameters = array('menu_link' => $mlid);
+      $route_parameters = array('shortcut' => $shortcut_id);
     }
 
     if (theme_get_setting('shortcut_module_link')) {
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index be220e7..a702470 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -1,11 +1,3 @@
-shortcut.link_delete:
-  path: '/admin/config/user-interface/shortcut/link/{menu_link}/delete'
-  defaults:
-    _form: 'Drupal\shortcut\Form\LinkDelete'
-    _title: 'Delete shortcut'
-  requirements:
-    _access_shortcut_link: 'TRUE'
-
 shortcut.set_delete:
   path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/delete'
   defaults:
@@ -57,18 +49,23 @@ shortcut.set_customize:
 shortcut.link_add:
   path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link'
   defaults:
-    _content: '\Drupal\shortcut\Form\ShortcutForm::add'
-    _title: 'Add Shortcut'
+    _content: '\Drupal\shortcut\Controller\ShortcutController::addForm'
   requirements:
-    _access_shortcut_set_edit: 'TRUE'
+    _entity_create_access: 'shortcut:{shortcut_set}'
 
 shortcut.link_edit:
-  path: '/admin/config/user-interface/shortcut/link/{menu_link}'
+  path: '/admin/config/user-interface/shortcut/link/{shortcut}'
+  defaults:
+    _entity_form: 'shortcut.default'
+  requirements:
+    _entity_access: 'shortcut.update'
+
+shortcut.link_delete:
+  path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete'
   defaults:
-    _content: '\Drupal\shortcut\Form\ShortcutForm::edit'
-    _title: 'Add Shortcut'
+    _entity_form: 'shortcut.delete'
   requirements:
-    _access_shortcut_link: 'TRUE'
+    _entity_access: 'shortcut.delete'
 
 shortcut.overview:
   path: '/user/{user}/shortcuts'
diff --git a/core/modules/shortcut/shortcut.services.yml b/core/modules/shortcut/shortcut.services.yml
index 439a0e0..3a04d99 100644
--- a/core/modules/shortcut/shortcut.services.yml
+++ b/core/modules/shortcut/shortcut.services.yml
@@ -1,9 +1,4 @@
 services:
-  access_check.shortcut.link:
-    class: Drupal\shortcut\Access\LinkAccessCheck
-    tags:
-      - { name: access_check }
-
   access_check.shortcut.shortcut_set_edit:
     class: Drupal\shortcut\Access\ShortcutSetEditAccessCheck
     tags:
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index e127a2f..35fcafc 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -59,24 +59,21 @@ function standard_install() {
   user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access site-wide contact form'));
 
   // Populate the default shortcut set.
-  $shortcut_set = shortcut_set_load('default');
-  $menu_link = entity_create('menu_link', array(
-    'link_path' => 'node/add',
-    'link_title' => t('Add content'),
+  $shortcut = entity_create('shortcut', array(
+    'set' => 'default',
+    'title' => t('Add content'),
     'weight' => -20,
+    'path' => 'node/add',
   ));
-  $menu_link->save();
-  $shortcut_set->links[$menu_link->uuid()] = $menu_link;
+  $shortcut->save();
 
-  $menu_link = entity_create('menu_link', array(
-    'link_path' => 'admin/content',
-    'link_title' => t('All content'),
+  $shortcut = entity_create('shortcut', array(
+    'set' => 'default',
+    'title' => t('All content'),
     'weight' => -19,
+    'path' => 'admin/content',
   ));
-  $menu_link->save();
-  $shortcut_set->links[$menu_link->uuid()] = $menu_link;
-
-  $shortcut_set->save();
+  $shortcut->save();
 
   // Enable the admin theme.
   theme_enable(array('seven'));
diff --git a/core/modules/menu_link/tests/Drupal/menu_link/Tests/Plugin/Core/Entity/MenuLinkTest.php b/core/tests/Drupal/Tests/Core/Routing/UrlMatcherTest.php
similarity index 66%
rename from core/modules/menu_link/tests/Drupal/menu_link/Tests/Plugin/Core/Entity/MenuLinkTest.php
rename to core/tests/Drupal/Tests/Core/Routing/UrlMatcherTest.php
index 533815e..450ae85 100644
--- a/core/modules/menu_link/tests/Drupal/menu_link/Tests/Plugin/Core/Entity/MenuLinkTest.php
+++ b/core/tests/Drupal/Tests/Core/Routing/UrlMatcherTest.php
@@ -2,13 +2,13 @@
 
 /**
  * @file
- * Contains \Drupal\menu_link\Tests\Plugin\Core\Entity\MenuLinkTest.
+ * Contains \Drupal\Tests\Core\Routing\UrlMatcherTest.
  */
 
-namespace Drupal\menu_link\Tests\Plugin\Core\Entity;
+namespace Drupal\Tests\Core\Routing;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\menu_link\Entity\MenuLink;
+use Drupal\Core\Routing\UrlMatcher;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\ParameterBag;
@@ -18,24 +18,38 @@
  * Tests the menu link entity class.
  *
  * @group Drupal
- * @group Drupal_menu
+ * @group Routing
  *
- * @see \Drupal\menu_link\Plugin\Core\Entity\MenuLink
+ * @see \Drupal\Core\Routing\UrlMatcher
  */
-class MenuLinkTest extends UnitTestCase {
+class UrlMatcherTest extends UnitTestCase {
+
+  /**
+   * The url generator to test.
+   *
+   * @var \Drupal\Core\Routing\UrlMatcher
+   */
+  protected $matcher;
 
   public static function getInfo() {
     return array(
-      'name' => 'Menu link entity',
-      'description' => 'Tests the menu link entity class.',
-      'group' => 'Menu',
+      'name' => 'UrlMatcher',
+      'description' => 'Confirm that the UrlMatcher is functioning properly.',
+      'group' => 'Routing',
     );
   }
 
   /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    $this->matcher = new UrlMatcher();
+  }
+
+  /**
    * Tests the findRouteNameParameters method.
    *
-   * @see \Drupal\menu_link\Plugin\Core\Entity\MenuLink::findRouteNameParameters()
+   * @see \Drupal\Core\Routing\UrlMatcher::findRouteNameParameters()
    */
   public function testFindRouteNameParameters() {
     $router = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
@@ -65,11 +79,11 @@ public function testFindRouteNameParameters() {
       ->method('matchRequest')
       ->will($this->throwException(new ResourceNotFoundException()));
 
-    $this->assertEquals(array('view.frontpage.page_1', array()), MenuLink::findRouteNameParameters('node'));
-    $this->assertEquals(array('node_view', array('node' => '1')), MenuLink::findRouteNameParameters('node/1'));
-    $this->assertEquals(array('node_edit', array('node' => '2')), MenuLink::findRouteNameParameters('node/2/edit'));
+    $this->assertEquals(array('view.frontpage.page_1', array()), $this->matcher->findRouteNameParameters('node'));
+    $this->assertEquals(array('node_view', array('node' => '1')), $this->matcher->findRouteNameParameters('node/1'));
+    $this->assertEquals(array('node_edit', array('node' => '2')), $this->matcher->findRouteNameParameters('node/2/edit'));
 
-    $this->assertEquals(array(), MenuLink::findRouteNameParameters('non-existing'));
+    $this->assertEquals(array(), $this->matcher->findRouteNameParameters('non-existing'));
   }
 
 }
