diff --git a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
index 03c1c40..6cbf234 100644
--- a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
+++ b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
@@ -34,9 +34,7 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ
     // If a destination is specified, that serves as the cancel link.
     if ($query->has('destination')) {
       $options = UrlHelper::parse($query->get('destination'));
-      // @todo Use Url::fromPath() once https://www.drupal.org/node/2351379 is
-      //   resolved.
-      $url = Url::fromUri('base:' . $options['path'], $options);
+      $url = Url::fromUri('user-path:' . $options['path'], $options);
     }
     // Check for a route-based cancel link.
     else {
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index 560b7f4..bd334d9 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -203,9 +203,12 @@ public static function fromRouteMatch(RouteMatchInterface $route_match) {
    * use Url::fromRoute().
    *
    * @param string $uri
-   *   The URI of the resource including the scheme. For Drupal paths that are
-   *   not handled by the routing system, use base: for the scheme. For entity
-   *   URLs you may use entity:{entity_type}/{entity_id} URIs.
+   *   The URI of the resource including the scheme. For user input
+   *   that may correspond to a Drupal route, use user-path: for the scheme.
+   *   For paths that are known not to be handled by the Drupal routing system
+   *   (such as static files), use base: for the scheme to get a link
+   *   relative to the Drupal base path (like the <base> HTML element). For
+   *   a link to an entity you may use entity:{entity_type}/{entity_id} URIs.
    * @param array $options
    *   (optional) An associative array of additional URL options, with the
    *   following elements:
@@ -223,8 +226,14 @@ public static function fromRouteMatch(RouteMatchInterface $route_match) {
    *     defined, the current scheme is used, so the user stays on HTTP or HTTPS
    *     respectively. TRUE enforces HTTPS and FALSE enforces HTTP.
    *
+   * Note: the user-path: scheme should be avoided except when processing actual
+   * user input that may or may not correspond to a Drupal route. Normally use
+   * Url::fromRoute() for code linking to any any Drupal page.
+   *
+   * You can call access() on the returned object to do access checking.
+   *
    * @return \Drupal\Core\Url
-   *   A new Url object for an unrouted (non-Drupal) URL or a routed entity URI.
+   *   A new Url object with properties depending on the URI scheme.
    *
    * @throws \InvalidArgumentException
    *   Thrown when the passed in path has no scheme.
@@ -232,20 +241,29 @@ public static function fromRouteMatch(RouteMatchInterface $route_match) {
    * @see static::fromRoute()
    */
   public static function fromUri($uri, $options = array()) {
-    if (!($scheme = parse_url($uri, PHP_URL_SCHEME))) {
-      throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base: for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
+    $uri_parts = parse_url($uri);
+    if ($uri_parts === FALSE) {
+      throw new \InvalidArgumentException(String::format('The URI "@uri" is malformed.', ['@uri' => $uri]));
+    }
+    if (empty($uri_parts['scheme'])) {
+      throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base:// for items like a static file that needs the base path.. Use user-path: for user input without a scheme. Use entity: for referencing the canonical route of a content entity.', ['@uri' => $uri]));
     }
 
-    // Special case entity: URIs. Map these to the canonical entity route.
-    if ($scheme === 'entity') {
-      return static::fromEntityUri($uri);
+    $uri_parts += ['path' => ''];
+    if ($uri_parts['scheme'] === 'entity') {
+      $url = static::fromEntityUri($uri);
     }
-    $url = new static($uri, array(), $options);
-    if ($scheme !== 'base') {
-      $url->external = TRUE;
-      $url->setOption('external', TRUE);
+    elseif ($uri_parts['scheme'] === 'user-path') {
+      $url = static::fromUserPathUri($uri, $options);
+    }
+    else {
+      $url = new static($uri, array(), $options);
+      if ($uri_parts['scheme'] !== 'base') {
+        $url->external = TRUE;
+        $url->setOption('external', TRUE);
+      }
+      $url->setUnrouted();
     }
-    $url->setUnrouted();
 
     return $url;
   }
@@ -273,6 +291,27 @@ protected static function fromEntityUri($uri) {
   }
 
   /**
+   * Create a new Url object for 'user-path:' URIs.
+   *
+   * @param string $uri
+   *   An URI of the form user-path:{path}.
+   * @param array $options
+   *   An array of options, see static::fromUri() for details.
+   *
+   * @return \Drupal\Core\Url
+   *   A new Url object for a 'user-path:' URI.
+   */
+  protected static function fromUserPathUri($uri, array $options) {
+    $uri_reference = explode(':', $uri, 2)[1];
+    $url = \Drupal::pathValidator()
+      ->getUrlIfValidWithoutAccessCheck($uri_reference) ?: static::fromUri('base:' . $uri_reference);
+    // Allow to specify additional or override options from the path.
+    $url->setOptions($options + $url->getOptions());
+
+    return $url;
+  }
+
+  /**
    * Returns the Url object matching a request.
    *
    * SECURITY NOTE: The request path is not checked to be valid and accessible
@@ -567,7 +606,10 @@ public function getInternalPath() {
    *   Returns TRUE if the user has access to the url, otherwise FALSE.
    */
   public function access(AccountInterface $account = NULL) {
-    return $this->accessManager()->checkNamedRoute($this->getRouteName(), $this->getRouteParameters(), $account);
+    if ($this->isRouted()) {
+      return $this->accessManager()->checkNamedRoute($this->getRouteName(), $this->getRouteParameters(), $account);
+    }
+    return TRUE;
   }
 
   /**
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 3470126..44828e4 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -255,11 +255,11 @@ public function eventDetails($event_id) {
         ),
         array(
           array('data' => $this->t('Location'), 'header' => TRUE),
-          $this->l($dblog->location, $dblog->location ? Url::fromUri('base:' . $dblog->location) : Url::fromRoute('<none>')),
+          $this->l($dblog->location, $dblog->location ? Url::fromUri($dblog->location) : Url::fromRoute('<none>')),
         ),
         array(
           array('data' => $this->t('Referrer'), 'header' => TRUE),
-          $this->l($dblog->referer, $dblog->referer ? Url::fromUri('base:' . $dblog->referer) : Url::fromRoute('<none>')),
+          $this->l($dblog->referer, $dblog->referer ? Url::fromUri($dblog->referer) : Url::fromRoute('<none>')),
         ),
         array(
           array('data' => $this->t('Message'), 'header' => TRUE),
diff --git a/core/modules/field_ui/src/FieldUI.php b/core/modules/field_ui/src/FieldUI.php
index c6b33ed..dcac29a 100644
--- a/core/modules/field_ui/src/FieldUI.php
+++ b/core/modules/field_ui/src/FieldUI.php
@@ -61,9 +61,7 @@ public static function getNextDestination(array $destinations) {
         $options['query']['destinations'] = $destinations;
       }
       // Redirect to any given path within the same domain.
-      // @todo Use Url::fromPath() once https://www.drupal.org/node/2351379 is
-      //   resolved.
-      $next_destination = Url::fromUri('base:' . $options['path']);
+      $next_destination = Url::fromUri('user-path:' . $options['path']);
     }
     return $next_destination;
   }
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
index 380a215..a1cbd47 100644
--- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
@@ -76,7 +76,7 @@ public function getTitle() {
    * {@inheritdoc}
    */
   public function getUrlObject() {
-    return \Drupal::pathValidator()->getUrlIfValidWithoutAccessCheck($this->link->uri) ?: Url::fromRoute('<front>');
+    return $this->link->first()->getUrl();
   }
 
   /**
@@ -261,7 +261,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
 
     $fields['description'] = BaseFieldDefinition::create('string')
       ->setLabel(t('Description'))
-      ->setDescription(t('Shown when hovering over the menu link.'))
       ->setTranslatable(TRUE)
       ->setSettings(array(
         'max_length' => 255,
@@ -291,7 +290,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
         'link_type' => LinkItemInterface::LINK_GENERIC,
         'title' => DRUPAL_DISABLED,
       ))
-    ;
+      ->setDisplayOptions('form', array(
+        'type' => 'link_default',
+        'weight' => -2,
+      ));
 
     $fields['external'] = BaseFieldDefinition::create('boolean')
       ->setLabel(t('External'))
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
index 3137257..d4180ed 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -193,7 +193,7 @@ public function extractFormValues(array &$form, FormStateInterface $form_state)
     $new_definition['route_parameters'] = [];
     $new_definition['options'] = [];
 
-    $extracted = $this->pathValidator->getUrlIfValid($form_state->getValue('url'));
+    $extracted = $this->pathValidator->getUrlIfValid($form_state->getValue(['link', 0, 'uri']));
 
     if ($extracted) {
       if ($extracted->isExternal()) {
@@ -220,17 +220,6 @@ public function extractFormValues(array &$form, FormStateInterface $form_state)
   public function form(array $form, FormStateInterface $form_state) {
     $form = parent::form($form, $form_state);
 
-    $default_value = $this->getEntity()->link->uri;
-
-    $form['url'] = array(
-      '#title' => $this->t('Link path'),
-      '#type' => 'textfield',
-      '#description' => $this->t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
-      '#default_value' => $default_value,
-      '#required' => TRUE,
-      '#weight' => -2,
-    );
-
     $default = $this->entity->getMenuName() . ':' . $this->entity->getParentId();
     $form['menu_parent'] = $this->menuParentSelector->parentSelectElement($default, $this->entity->getPluginId());
     $form['menu_parent']['#weight'] = 10;
@@ -274,8 +263,6 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
     $entity->enabled->value = (bool) $new_definition['enabled'];
     $entity->expanded->value = $new_definition['expanded'];
 
-    $entity->link->uri = $form_state->getValue('url');
-
     return $entity;
   }
 
@@ -312,10 +299,10 @@ public function save(array $form, FormStateInterface $form_state) {
    *   The current state of the form.
    */
   protected function doValidate(array $form, FormStateInterface $form_state) {
-    $extracted = $this->pathValidator->getUrlIfValid($form_state->getValue('url'));
+    $extracted = $this->pathValidator->getUrlIfValid($form_state->getValue(['link', 0, 'uri']));
 
     if (!$extracted) {
-      $form_state->setErrorByName('url', $this->t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $form_state->getValue('url'))));
+      $form_state->setErrorByName('link][0][uri', $this->t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $form_state->getValue(['link', 0, 'uri']))));
     }
   }
 
diff --git a/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php b/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php
index 20314b0..a5dc9fb 100644
--- a/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php
+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentUITest.php
@@ -57,7 +57,7 @@ protected function getAdministratorPermissions() {
    */
   protected function createEntity($values, $langcode, $bundle_name = NULL) {
     $values['menu_name'] = 'tools';
-    $values['link']['uri'] = 'admin/structure/menu';
+    $values['link']['uri'] = 'user-path:admin/structure/menu';
     $values['title'] = 'Test title';
 
     return parent::createEntity($values, $langcode, $bundle_name);
@@ -70,7 +70,7 @@ public function testTranslationLinkOnMenuEditForm() {
     $this->drupalGet('admin/structure/menu/manage/tools');
     $this->assertNoLink(t('Translate'));
 
-    $menu_link_content = MenuLinkContent::create(['menu_name' => 'tools', 'link' => ['uri' => 'admin/structure/menu']]);
+    $menu_link_content = MenuLinkContent::create(['menu_name' => 'tools', 'link' => ['uri' => 'user-path:admin/structure/menu']]);
     $menu_link_content->save();
     $this->drupalGet('admin/structure/menu/manage/tools');
     $this->assertLink(t('Translate'));
diff --git a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
index 08b1981..67411f2 100644
--- a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
@@ -76,7 +76,7 @@ function testMenuLanguage() {
     $link_title = $this->randomString();
     $edit = array(
       'title[0][value]' => $link_title,
-      'url' => $link_path,
+      'link[0][uri]' => $link_path,
     );
     $this->drupalPostForm("admin/structure/menu/manage/$menu_name/add", $edit, t('Save'));
     // Check the link was added with the correct menu link default language.
@@ -98,7 +98,7 @@ function testMenuLanguage() {
     $link_title = $this->randomString();
     $edit = array(
       'title[0][value]' => $link_title,
-      'url' => $link_path,
+      'link[0][uri]' => $link_path,
     );
     $this->drupalPostForm("admin/structure/menu/manage/$menu_name/add", $edit, t('Save'));
     // Check the link was added with the correct new menu link default language.
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index 1529eda..8763132 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -106,7 +106,7 @@ function testMenu() {
 
     foreach ($this->items as $item) {
       // Paths were set as 'node/$nid'.
-      $node = Node::load(str_replace('node/', '', $item->link->uri));
+      $node = Node::load(str_replace('user-path:node/', '', $item->link->uri));
       $this->verifyMenuLink($item, $node);
     }
 
@@ -115,8 +115,8 @@ function testMenu() {
 
     // Verify delete link exists and reset link does not exist.
     $this->drupalGet('admin/structure/menu/manage/' . $this->menu->id());
-    $this->assertLinkByHref('admin/structure/menu/item/' . $this->items[0]->id() . '/delete');
-    $this->assertNoLinkByHref(Url::fromUri('base:admin/structure/menu/link/' . $this->items[0]->getPluginId() . '/reset')->toString());
+    $this->assertLinkByHref(Url::fromRoute('entity.menu_link_content.delete_form',  ['menu_link_content' => $this->items[0]->id()])->toString());
+    $this->assertNoLinkByHref(Url::fromRoute('menu_ui.link_reset', ['menu_link_plugin' => $this->items[0]->getPluginId()])->toString());
     // Check delete and reset access.
     $this->drupalGet('admin/structure/menu/item/' . $this->items[0]->id() . '/delete');
     $this->assertResponse(200);
@@ -266,7 +266,7 @@ function doMenuTests() {
 
     $this->clickLink(t('Add link'));
     $link_title = $this->randomString();
-    $this->drupalPostForm(NULL, array('url' => '<front>', 'title[0][value]' => $link_title), t('Save'));
+    $this->drupalPostForm(NULL, array('link[0][uri]' => '<front>', 'title[0][value]' => $link_title), t('Save'));
     $this->assertUrl(Url::fromRoute('entity.menu.edit_form',  ['menu' => $menu_name]));
     // Test the 'Edit' operation.
     $this->clickLink(t('Edit'));
@@ -475,7 +475,7 @@ protected function doMenuLinkFormDefaultsTest() {
     $this->assertResponse(200);
 
     $this->assertFieldByName('title[0][value]', '');
-    $this->assertFieldByName('url', '');
+    $this->assertFieldByName('link[0][uri]', '');
 
     $this->assertNoFieldChecked('edit-expanded-value');
     $this->assertFieldChecked('edit-enabled-value');
@@ -495,13 +495,13 @@ function testMenuQueryAndFragment() {
     $item = $this->addMenuLink('', $path);
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
-    $this->assertFieldByName('url', $path, 'Path is found with both query and fragment.');
+    $this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
 
     // Now change the path to something without query and fragment.
     $path = 'test-page';
-    $this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array('url' => $path), t('Save'));
+    $this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array('link[0][uri]' => $path), t('Save'));
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
-    $this->assertFieldByName('url', $path, 'Path no longer has query or fragment.');
+    $this->assertFieldByName('link[0][uri]', $path, 'Path no longer has query or fragment.');
 
     // Use <front>#fragment and ensure that saving it does not loose its
     // content.
@@ -509,12 +509,12 @@ function testMenuQueryAndFragment() {
     $item = $this->addMenuLink('', $path);
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
-    $this->assertFieldByName('url', $path, 'Path is found with both query and fragment.');
+    $this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
 
     $this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array(), t('Save'));
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
-    $this->assertFieldByName('url', $path, 'Path is found with both query and fragment.');
+    $this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
   }
 
   /**
@@ -610,7 +610,7 @@ function addMenuLink($parent = '', $path = '<front>', $menu_name = 'tools', $exp
 
     $title = '!link_' . $this->randomMachineName(16);
     $edit = array(
-      'url' => $path,
+      'link[0][uri]' => $path,
       'title[0][value]' => $title,
       'description[0][value]' => '',
       'enabled[value]' => 1,
@@ -639,7 +639,7 @@ function addMenuLink($parent = '', $path = '<front>', $menu_name = 'tools', $exp
   function addInvalidMenuLink() {
     foreach (array('-&-', 'admin/people/permissions', '#') as $link_path) {
       $edit = array(
-        'url' => $link_path,
+        'link[0][uri]' => $link_path,
         'title[0][value]' => 'title',
       );
       $this->drupalPostForm("admin/structure/menu/manage/{$this->menu->id()}/add", $edit, t('Save'));
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index ed4d35a..fff190b 100644
--- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -187,7 +187,7 @@ function testBreadCrumbs() {
     $menu = 'tools';
     $edit = array(
       'title[0][value]' => 'Root',
-      'url' => 'node',
+      'link[0][uri]' => 'node',
     );
     $this->drupalPostForm("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
     $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => 'Root'));
@@ -240,7 +240,7 @@ function testBreadCrumbs() {
       $term = $data['term'];
       $edit = array(
         'title[0][value]' => "$name link",
-        'url' => "taxonomy/term/{$term->id()}",
+        'link[0][uri]' => "taxonomy/term/{$term->id()}",
         'menu_parent' => "$menu:{$parent_mlid}",
         'enabled[value]' => 1,
       );
@@ -248,7 +248,7 @@ function testBreadCrumbs() {
       $menu_links = entity_load_multiple_by_properties('menu_link_content', array(
         'title' => $edit['title[0][value]'],
         // @todo Use link.uri once https://www.drupal.org/node/2391217 is in.
-        'link__uri' => 'taxonomy/term/' . $term->id(),
+        'link__uri' => 'user-path:taxonomy/term/' . $term->id(),
       ));
       $tags[$name]['link'] = reset($menu_links);
       $parent_mlid = $tags[$name]['link']->getPluginId();
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
index cccdb37..c99105a 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
@@ -54,9 +54,8 @@ public function buildForm(array $form, FormStateInterface $form_state) {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     if (!$form_state->isValueEmpty('redirection')) {
       if (!$form_state->isValueEmpty('destination')) {
-        // @todo Use Url::fromPath() once https://www.drupal.org/node/2351379 is
-        //   resolved.
-        $form_state->setRedirectUrl(Url::fromUri('base:' . $form_state->getValue('destination')));
+        // The destination is a random URL, so we can't use routed URLs.
+        $form_state->setRedirectUrl(Url::fromUri('user-path:' . $form_state->getValue('destination')));
       }
     }
     else {
diff --git a/core/modules/views_ui/src/ViewListBuilder.php b/core/modules/views_ui/src/ViewListBuilder.php
index 3961043..b4a53c6 100644
--- a/core/modules/views_ui/src/ViewListBuilder.php
+++ b/core/modules/views_ui/src/ViewListBuilder.php
@@ -263,7 +263,7 @@ protected function getDisplayPaths(EntityInterface $view) {
       if ($display->hasPath()) {
         $path = $display->getPath();
         if ($view->status() && strpos($path, '%') === FALSE) {
-          $all_paths[] = \Drupal::l('/' . $path, Url::fromUri('base:' . $path));
+          $all_paths[] = \Drupal::l('/' . $path, Url::fromUri('user-path:' . $path));
         }
         else {
           $all_paths[] = String::checkPlain('/' . $path);
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 2153058..c5ef9f7 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -702,7 +702,7 @@ public function renderPreview($display_id, $args = array()) {
               Xss::filterAdmin($this->executable->getTitle()),
             );
             if (isset($path)) {
-              $path = \Drupal::l($path, Url::fromUri('base:' . $path));
+              $path = \Drupal::l($path, Url::fromUri('user-path:' . $path));
             }
             else {
               $path = t('This display has no path.');
diff --git a/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php b/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
index 540046a..a96ac58 100644
--- a/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Form\ConfirmFormHelper;
 use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -95,8 +96,20 @@ public function testCancelLinkRouteWithUrl() {
   public function testCancelLinkDestination() {
     $query = array('destination' => 'baz');
     $form = $this->getMock('Drupal\Core\Form\ConfirmFormInterface');
+
+    $path_validator = $this->getMock('Drupal\Core\Path\PathValidatorInterface');
+    $container_builder = new ContainerBuilder();
+    $container_builder->set('path.validator', $path_validator);
+    \Drupal::setContainer($container_builder);
+
+    $url = Url::fromRoute('test_route');
+    $path_validator->expects($this->atLeastOnce())
+      ->method('getUrlIfValidWithoutAccessCheck')
+      ->with('baz')
+      ->willReturn($url);
+
     $link = ConfirmFormHelper::buildCancelLink($form, new Request($query));
-    $this->assertSame('base:' . $query['destination'], $link['#url']->getUri());
+    $this->assertSame($url, $link['#url']);
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index dc6b928..c055ab4 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -61,6 +61,13 @@ class UrlTest extends UnitTestCase {
   protected $map;
 
   /**
+   * The mocked path validator.
+   *
+   * @var \Drupal\Core\Path\PathValidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $pathValidator;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -93,10 +100,13 @@ protected function setUp() {
       ->will($this->returnValueMap($alias_map));
 
     $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface');
+    $this->pathValidator = $this->getMock('Drupal\Core\Path\PathValidatorInterface');
+
     $this->container = new ContainerBuilder();
     $this->container->set('router.no_access_checks', $this->router);
     $this->container->set('url_generator', $this->urlGenerator);
     $this->container->set('path.alias_manager', $this->pathAliasManager);
+    $this->container->set('path.validator', $this->pathValidator);
     \Drupal::setContainer($this->container);
   }
 
@@ -163,6 +173,36 @@ public function testFromRouteFront() {
   }
 
   /**
+   * Tests fromUri() method with a user-entered path not matching any route.
+   *
+   * @covers ::fromUri
+   */
+  public function testFromRoutedPathWithInvalidRoute() {
+    $this->pathValidator->expects($this->once())
+      ->method('getUrlIfValidWithoutAccessCheck')
+      ->with('invalid-path')
+      ->willReturn(FALSE);
+    $url = Url::fromUri('user-path:invalid-path');
+    $this->assertSame(FALSE, $url->isRouted());
+    $this->assertSame('base:invalid-path', $url->getUri());
+  }
+
+  /**
+   * Tests fromUri() method with user-entered path matching a valid route.
+   *
+   * @covers ::fromUri
+   */
+  public function testFromRoutedPathWithValidRoute() {
+    $url = Url::fromRoute('test_route');
+    $this->pathValidator->expects($this->once())
+      ->method('getUrlIfValidWithoutAccessCheck')
+      ->with('valid-path')
+      ->willReturn($url);
+    $result_url = Url::fromUri('user-path:valid-path');
+    $this->assertSame($url, $result_url);
+  }
+
+  /**
    * Tests the createFromRequest method.
    *
    * @covers ::createFromRequest
