diff --git a/core/core.services.yml b/core/core.services.yml
index 183f7e2..9222667 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -502,7 +502,7 @@ services:
     arguments: ['@config.factory']
   path.validator:
     class: Drupal\Core\Path\PathValidator
-    arguments: ['@router', '@router.route_provider', '@request_stack']
+    arguments: ['@router', '@router.no_access_checks', '@current_user', '@path_processor_manager']
 
 # The argument to the hashing service defined in services.yml, to the
 # constructor of PhpassHashedPassword is the log2 number of iterations for
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index fcc6476..4e4ba8c 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -666,4 +666,13 @@ public static function menuTree() {
     return static::$container->get('menu.link_tree');
   }
 
+  /**
+   * Returns the path validator.
+   *
+   * @return \Drupal\Core\Path\PathValidatorInterface
+   */
+  public static function pathValidator() {
+    return static::$container->get('path.validator');
+  }
+
 }
diff --git a/core/lib/Drupal/Component/Utility/UrlHelper.php b/core/lib/Drupal/Component/Utility/UrlHelper.php
index 4ff656a..0c0e07d 100644
--- a/core/lib/Drupal/Component/Utility/UrlHelper.php
+++ b/core/lib/Drupal/Component/Utility/UrlHelper.php
@@ -151,7 +151,12 @@ public static function parse($url) {
     if (strpos($url, '://') !== FALSE) {
       // Split off everything before the query string into 'path'.
       $parts = explode('?', $url);
-      $options['path'] = $parts[0];
+
+      // Don't support URLs without a path, like 'http://'.
+      list(, $path) = explode('://', $parts[0], 2);
+      if ($path != '') {
+        $options['path'] = $parts[0];
+      }
       // If there is a query string, transform it into keyed query parameters.
       if (isset($parts[1])) {
         $query_parts = explode('#', $parts[1]);
diff --git a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
index ae5b285..ff1792b 100644
--- a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
+++ b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Menu;
 
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Path\PathValidator;
 use Drupal\Core\Session\AccountInterface;
 
 /**
@@ -93,6 +94,9 @@ public function checkAccess(array $tree) {
    *   TRUE if the current user can access the link, FALSE otherwise.
    */
   protected function menuLinkCheckAccess(MenuLinkInterface $instance) {
+    if ($this->account->hasPermission('link to any page')) {
+      return TRUE;
+    }
     // Use the definition here since that's a lot faster than creating a Url
     // object that we don't need.
     $definition = $instance->getPluginDefinition();
diff --git a/core/lib/Drupal/Core/Path/PathValidator.php b/core/lib/Drupal/Core/Path/PathValidator.php
index d363417..b6c3a5a 100644
--- a/core/lib/Drupal/Core/Path/PathValidator.php
+++ b/core/lib/Drupal/Core/Path/PathValidator.php
@@ -2,18 +2,22 @@
 
 /**
  * @file
- * Contains Drupal\Core\Path\PathValidator
+ * Contains \Drupal\Core\Path\PathValidator
  */
 
 namespace Drupal\Core\Path;
 
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\ParamConverter\ParamNotConvertedException;
-use Drupal\Core\Routing\RequestHelper;
-use Drupal\Core\Routing\RouteProviderInterface;
-use Symfony\Component\HttpFoundation\RequestStack;
+use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
+use Drupal\Core\Routing\AccessAwareRouterInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Url;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
 
 /**
  * Provides a default path validator and access checker.
@@ -21,66 +25,122 @@
 class PathValidator implements PathValidatorInterface {
 
   /**
-   * The request matcher.
+   * The access aware router.
    *
-   * @var \Symfony\Component\Routing\Matcher\RequestMatcherInterface
+   * @var \Drupal\Core\Routing\AccessAwareRouterInterface
    */
-  protected $requestMatcher;
+  protected $accessAwareRouter;
 
   /**
-   * The route provider.
+   * A router implementation which does not check access.
    *
-   * @var \Drupal\Core\Routing\RouteProviderInterface
+   * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface
    */
-  protected $routeProvider;
+  protected $accessUnawareRouter;
 
   /**
-   * The request stack.
+   * The current user.
    *
-   * @var \Symfony\Component\HttpFoundation\RequestStack
+   * @var \Drupal\Core\Session\AccountInterface
    */
-  protected $requestStack;
+  protected $account;
+
+  /**
+   * The path processor.
+   *
+   * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface
+   */
+  protected $pathProcessor;
 
   /**
    * Creates a new PathValidator.
    *
-   * @param \Symfony\Component\Routing\Matcher\RequestMatcherInterface $request_matcher
-   *   The request matcher.
-   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
-   *   The route provider.
-   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
-   *   The request stack.
+   * @param \Drupal\Core\Routing\AccessAwareRouterInterface $access_aware_router
+   *   The access aware router.
+   * @param \Symfony\Component\Routing\Matcher\UrlMatcherInterface $access_unaware_router
+   *   A router implementation which does not check access.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user.
+   * @param \Drupal\Core\PathProcessor\InboundPathProcessorInterface $path_processor
+   *   The path processor;
    */
-  public function __construct(RequestMatcherInterface $request_matcher, RouteProviderInterface $route_provider, RequestStack $request_stack) {
-    $this->requestMatcher = $request_matcher;
-    $this->routeProvider = $route_provider;
-    $this->requestStack = $request_stack;
+  public function __construct(AccessAwareRouterInterface $access_aware_router, UrlMatcherInterface $access_unaware_router, AccountInterface $account, InboundPathProcessorInterface $path_processor) {
+    $this->accessAwareRouter = $access_aware_router;
+    $this->accessUnawareRouter = $access_unaware_router;
+    $this->account = $account;
+    $this->pathProcessor = $path_processor;
   }
 
   /**
    * {@inheritdoc}
    */
   public function isValid($path) {
-    // External URLs and the front page are always valid.
-    if ($path == '<front>' || UrlHelper::isExternal($path)) {
-      return TRUE;
+    return (bool) $this->getUrlIfValid($path);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUrlIfValid($path) {
+    $parsed_url = UrlHelper::parse($path);
+
+    $options = [];
+    if (!empty($parsed_url['query'])) {
+      $options['query'] = $parsed_url['query'];
+    }
+    if (!empty($parsed_url['fragment'])) {
+      $options['fragment'] = $parsed_url['fragment'];
     }
 
-    // Check the routing system.
-    $collection = $this->routeProvider->getRoutesByPattern('/' . $path);
-    if ($collection->count() == 0) {
+    if ($parsed_url['path'] == '<front>') {
+      return new Url('<front>', [], $options);
+    }
+    elseif (UrlHelper::isExternal($path) && UrlHelper::isValid($path)) {
+      if (empty($parsed_url['path'])) {
+        return FALSE;
+      }
+      return Url::createFromPath($path);
+    }
+
+    $request = Request::create('/' . $path);
+    $attributes = $this->getPathAttributes($path, $request);
+
+    if (!$attributes) {
       return FALSE;
     }
 
-    // We can not use $this->requestMatcher->match() because we need to set
-    // the _menu_admin attribute to indicate a menu administrator is running
-    // the menu access check.
-    $request = RequestHelper::duplicate($this->requestStack->getCurrentRequest(), '/' . $path);
-    $request->attributes->set('_system_path', $path);
-    $request->attributes->set('_menu_admin', TRUE);
+    $route_name = $attributes[RouteObjectInterface::ROUTE_NAME];
+    $route_parameters = $attributes['_raw_variables']->all();
+
+    return new Url($route_name, $route_parameters, $options + ['query' => $request->query->all()]);
+  }
+
+  /**
+   * Gets the matched attributes for a given path.
+   *
+   * @param string $path
+   *   The path to check.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object with the given path.
+   *
+   * @return array|bool
+   *   An array of request attributes of FALSE if an exception was thrown.
+   */
+  protected function getPathAttributes($path, Request $request) {
+    if ($this->account->hasPermission('link to any page')) {
+      $router = $this->accessUnawareRouter;
+    }
+    else {
+      $router = $this->accessAwareRouter;
+    }
+
+    $path = $this->pathProcessor->processInbound($path, $request);
 
     try {
-      $this->requestMatcher->matchRequest($request);
+      return $router->match('/' . $path);
+    }
+    catch (ResourceNotFoundException $e) {
+      return FALSE;
     }
     catch (ParamNotConvertedException $e) {
       return FALSE;
@@ -88,7 +148,6 @@ public function isValid($path) {
     catch (AccessDeniedHttpException $e) {
       return FALSE;
     }
-    return TRUE;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Path/PathValidatorInterface.php b/core/lib/Drupal/Core/Path/PathValidatorInterface.php
index fed2f75..e0bcf04 100644
--- a/core/lib/Drupal/Core/Path/PathValidatorInterface.php
+++ b/core/lib/Drupal/Core/Path/PathValidatorInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains Drupal\Core\Path\PathValidatorInterface
+ * Contains \Drupal\Core\Path\PathValidatorInterface
  */
 
 namespace Drupal\Core\Path;
@@ -13,6 +13,17 @@
 interface PathValidatorInterface {
 
   /**
+   * Returns an URL object, if the path is valid and accessible.
+   *
+   * @param string $path
+   *   The path to check.
+   *
+   * @return \Drupal\Core\Url|false
+   *   The url object, or FALSE if the path is not valid.
+   */
+  public function getUrlIfValid($path);
+
+  /**
    * Checks if the URL path is valid and accessible by the current user.
    *
    * @param string $path
diff --git a/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php b/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php
index 946b29c..7bc22a8 100644
--- a/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php
+++ b/core/lib/Drupal/Core/PathProcessor/InboundPathProcessorInterface.php
@@ -22,6 +22,9 @@
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The HttpRequest object representing the current request.
+   *
+   * @return string
+   *   The processed path.
    */
   public function processInbound($path, Request $request);
 
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index c6a8641..ea407cc 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -9,11 +9,9 @@
 
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
-use Drupal\Core\Routing\MatchingRouteNotFoundException;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 
 /**
  * Defines an object that holds information about a URL.
@@ -97,7 +95,15 @@ public function __construct($route_name, $route_parameters = array(), $options =
   }
 
   /**
-   * Returns the Url object matching a path.
+   * Returns the Url object matching a path. READ THE FOLLOWING SECURITY NOTE.
+   *
+   * SECURITY NOTE: The path is not checked to be valid and accessible by the
+   * current user to allow storing and reusing Url objects by different users.
+   * The 'path.validator' service getUrlIfValid() method should be used instead
+   * of this one if validation and access check is desired. Otherwise,
+   * 'access_manager' service checkNamedRoute() method should be used on the
+   * router name and parameters stored in the Url object returned by this
+   * method.
    *
    * @param string $path
    *   A path (e.g. 'node/1', 'http://drupal.org').
@@ -118,27 +124,15 @@ public static function createFromPath($path) {
 
     // Special case the front page route.
     if ($path == '<front>') {
-      $route_name = $path;
-      $route_parameters = array();
+      return new static($path);
     }
     else {
-      // Look up the route name and parameters used for the given path.
-      try {
-        // We use the router without access checks because URL objects might be
-        // created and stored for different users.
-        $result = \Drupal::service('router.no_access_checks')->match('/' . $path);
-      }
-      catch (ResourceNotFoundException $e) {
-        throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the path "%s"', $path), 0, $e);
-      }
-      $route_name = $result[RouteObjectInterface::ROUTE_NAME];
-      $route_parameters = $result['_raw_variables']->all();
+      return static::createFromRequest(Request::create("/$path"));
     }
-    return new static($route_name, $route_parameters);
   }
 
   /**
-   * Returns the Url object matching a request.
+   * Returns the Url object matching a request. READ THE SECURITY NOTE ON createFromPath().
    *
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   A request object.
@@ -152,14 +146,9 @@ public static function createFromPath($path) {
    *   Thrown when the request cannot be matched.
    */
   public static function createFromRequest(Request $request) {
-    try {
-      // We use the router without access checks because URL objects might be
-      // created and stored for different users.
-      $result = \Drupal::service('router.no_access_checks')->matchRequest($request);
-    }
-    catch (ResourceNotFoundException $e) {
-      throw new MatchingRouteNotFoundException(sprintf('No matching route could be found for the request: %s', $request), 0, $e);
-    }
+    // We use the router without access checks because URL objects might be
+    // created and stored for different users.
+    $result = \Drupal::service('router.no_access_checks')->matchRequest($request);
     $route_name = $result[RouteObjectInterface::ROUTE_NAME];
     $route_parameters = $result['_raw_variables']->all();
     return new static($route_name, $route_parameters);
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index 040123b..cb95532 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -7,15 +7,10 @@
 
 namespace Drupal\link\Plugin\Field\FieldWidget;
 
-use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
-use Drupal\Core\ParamConverter\ParamNotConvertedException;
-use Drupal\Core\Routing\MatchingRouteNotFoundException;
-use Drupal\Core\Url;
 use Drupal\link\LinkItemInterface;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Plugin implementation of the 'link' widget.
@@ -47,9 +42,10 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
 
     $default_url_value = NULL;
     if (isset($items[$delta]->url)) {
-      $url = Url::createFromPath($items[$delta]->url);
-      $url->setOptions($items[$delta]->options);
-      $default_url_value = ltrim($url->toString(), '/');
+      if ($url = \Drupal::pathValidator()->getUrlIfValid($items[$delta]->url)) {
+        $url->setOptions($items[$delta]->options);
+        $default_url_value = ltrim($url->toString(), '/');
+      }
     }
     $element['url'] = array(
       '#type' => 'url',
@@ -204,32 +200,16 @@ public function validateTitle(&$element, FormStateInterface $form_state, $form)
   public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
     foreach ($values as &$value) {
       if (!empty($value['url'])) {
-        try {
-          $parsed_url = UrlHelper::parse($value['url']);
-
-          // If internal links are supported, look up whether the given value is
-          // a path alias and store the system path instead.
-          if ($this->supportsInternalLinks() && !UrlHelper::isExternal($value['url'])) {
-            $parsed_url['path'] = \Drupal::service('path.alias_manager')->getPathByAlias($parsed_url['path']);
-          }
-
-          $url = Url::createFromPath($parsed_url['path']);
-          $url->setOption('query', $parsed_url['query']);
-          $url->setOption('fragment', $parsed_url['fragment']);
-          $url->setOption('attributes', $value['attributes']);
-
-          $value += $url->toArray();
-          // Reset the URL value to contain only the path.
-          $value['url'] = $parsed_url['path'];
+        $url = \Drupal::pathValidator()->getUrlIfValid($value['url']);
+        if (!$url) {
+          return $values;
         }
-        catch (NotFoundHttpException $e) {
-          // Nothing to do here, LinkTypeConstraintValidator emits errors.
-        }
-        catch (MatchingRouteNotFoundException $e) {
-          // Nothing to do here, LinkTypeConstraintValidator emits errors.
-        }
-        catch (ParamNotConvertedException $e) {
-          // Nothing to do here, LinkTypeConstraintValidator emits errors.
+
+        $value += $url->toArray();
+
+        // Reset the URL value to contain only the path.
+        if (!$url->isExternal() && $this->supportsInternalLinks()) {
+          $value['url'] = substr($url->toString(), strlen(\Drupal::request()->getBasePath() . '/'));
         }
       }
     }
diff --git a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
index de28bab..89ff6da 100644
--- a/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
+++ b/core/modules/link/src/Plugin/Validation/Constraint/LinkTypeConstraint.php
@@ -8,14 +8,9 @@
 namespace Drupal\link\Plugin\Validation\Constraint;
 
 use Drupal\link\LinkItemInterface;
-use Drupal\Core\Url;
-use Drupal\Core\Routing\MatchingRouteNotFoundException;
-use Drupal\Core\ParamConverter\ParamNotConvertedException;
-use Drupal\Component\Utility\UrlHelper;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidatorInterface;
 use Symfony\Component\Validator\ExecutionContextInterface;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Validation constraint for links receiving data allowed by its settings.
@@ -53,35 +48,19 @@ public function validatedBy() {
    */
   public function validate($value, Constraint $constraint) {
     if (isset($value)) {
-      $url_is_valid = TRUE;
+      $url_is_valid = FALSE;
       /** @var $link_item \Drupal\link\LinkItemInterface */
       $link_item = $value;
       $link_type = $link_item->getFieldDefinition()->getSetting('link_type');
       $url_string = $link_item->url;
       // Validate the url property.
       if ($url_string !== '') {
-        try {
-          // @todo This shouldn't be needed, but massageFormValues() may not
-          //   run.
-          $parsed_url = UrlHelper::parse($url_string);
+        if ($url = \Drupal::pathValidator()->getUrlIfValid($url_string)) {
+          $url_is_valid = (bool) $url;
 
-          $url = Url::createFromPath($parsed_url['path']);
-
-          if ($url->isExternal() && !UrlHelper::isValid($url_string, TRUE)) {
+          if ($url->isExternal() && !($link_type & LinkItemInterface::LINK_EXTERNAL)) {
             $url_is_valid = FALSE;
           }
-          elseif ($url->isExternal() && !($link_type & LinkItemInterface::LINK_EXTERNAL)) {
-            $url_is_valid = FALSE;
-          }
-        }
-        catch (NotFoundHttpException $e) {
-          $url_is_valid = FALSE;
-        }
-        catch (MatchingRouteNotFoundException $e) {
-          $url_is_valid = FALSE;
-        }
-        catch (ParamNotConvertedException $e) {
-          $url_is_valid = FALSE;
         }
       }
       if (!$url_is_valid) {
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index 6729e6b..c7eb596 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -52,6 +52,7 @@ protected function setUp() {
     $this->web_user = $this->drupalCreateUser(array(
       'view test entity',
       'administer entity_test content',
+      'link to any page',
     ));
     $this->drupalLogin($this->web_user);
   }
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
index b3966eb..ae4bac0 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -18,11 +18,9 @@
 use Drupal\Core\Menu\Form\MenuLinkFormInterface;
 use Drupal\Core\Menu\MenuLinkInterface;
 use Drupal\Core\Menu\MenuParentFormSelectorInterface;
-use Drupal\Core\ParamConverter\ParamNotConvertedException;
 use Drupal\Core\Path\AliasManagerInterface;
-use Drupal\Core\Routing\MatchingRouteNotFoundException;
+use Drupal\Core\Path\PathValidatorInterface;
 use Drupal\Core\Session\AccountInterface;
-use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Routing\RequestContext;
 
@@ -78,6 +76,13 @@ class MenuLinkContentForm extends ContentEntityForm implements MenuLinkFormInter
   protected $account;
 
   /**
+   * The path validator.
+   *
+   * @var \Drupal\Core\Path\PathValidatorInterface
+   */
+  protected $pathValidator;
+
+  /**
    * Constructs a MenuLinkContentForm object.
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
@@ -96,8 +101,10 @@ class MenuLinkContentForm extends ContentEntityForm implements MenuLinkFormInter
    *   The access manager.
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The current user.
+   * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
+   *   The path validator.
    */
-  public function __construct(EntityManagerInterface $entity_manager, MenuParentFormSelectorInterface $menu_parent_selector, AliasManagerInterface $alias_manager, ModuleHandlerInterface $module_handler, RequestContext $request_context, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) {
+  public function __construct(EntityManagerInterface $entity_manager, MenuParentFormSelectorInterface $menu_parent_selector, AliasManagerInterface $alias_manager, ModuleHandlerInterface $module_handler, RequestContext $request_context, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account, PathValidatorInterface $path_validator) {
     parent::__construct($entity_manager, $language_manager);
     $this->menuParentSelector = $menu_parent_selector;
     $this->pathAliasManager = $alias_manager;
@@ -106,6 +113,7 @@ public function __construct(EntityManagerInterface $entity_manager, MenuParentFo
     $this->languageManager = $language_manager;
     $this->accessManager = $access_manager;
     $this->account = $account;
+    $this->pathValidator = $path_validator;
   }
 
   /**
@@ -120,7 +128,8 @@ public static function create(ContainerInterface $container) {
       $container->get('router.request_context'),
       $container->get('language_manager'),
       $container->get('access_manager'),
-      $container->get('current_user')
+      $container->get('current_user'),
+      $container->get('path.validator')
     );
   }
 
@@ -168,46 +177,6 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s
   }
 
   /**
-   * Breaks up a user-entered URL or path into all the relevant parts.
-   *
-   * @param string $url
-   *   The user-entered URL or path.
-   *
-   * @return array
-   *   The extracted parts.
-   */
-  protected function extractUrl($url) {
-    $extracted = UrlHelper::parse($url);
-    $external = UrlHelper::isExternal($url);
-    if ($external) {
-      $extracted['url'] = $extracted['path'];
-      $extracted['route_name'] = NULL;
-      $extracted['route_parameters'] = array();
-    }
-    else {
-      $extracted['url'] = '';
-      // If the path doesn't match a Drupal path, the route should end up empty.
-      $extracted['route_name'] = NULL;
-      $extracted['route_parameters'] = array();
-      try {
-        // Find the route_name.
-        $normal_path = $this->pathAliasManager->getPathByAlias($extracted['path']);
-        $url_obj = Url::createFromPath($normal_path);
-        $extracted['route_name'] = $url_obj->getRouteName();
-        $extracted['route_parameters'] = $url_obj->getRouteParameters();
-      }
-      catch (MatchingRouteNotFoundException $e) {
-        // The path doesn't match a Drupal path.
-      }
-      catch (ParamNotConvertedException $e) {
-        // A path like node/99 matched a route, but the route parameter was
-        // invalid (e.g. node with ID 99 does not exist).
-      }
-    }
-    return $extracted;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function extractFormValues(array &$form, FormStateInterface $form_state) {
@@ -220,17 +189,24 @@ public function extractFormValues(array &$form, FormStateInterface $form_state)
     }
     $new_definition['parent'] = isset($parent) ? $parent : '';
 
-    $extracted = $this->extractUrl($form_state->getValue('url'));
-    $new_definition['url'] = $extracted['url'];
-    $new_definition['route_name'] = $extracted['route_name'];
-    $new_definition['route_parameters'] = $extracted['route_parameters'];
-    $new_definition['options'] = array();
-    if ($extracted['query']) {
-      $new_definition['options']['query'] = $extracted['query'];
-    }
-    if ($extracted['fragment']) {
-      $new_definition['options']['fragment'] = $extracted['fragment'];
+    $new_definition['url'] = NULL;
+    $new_definition['route_name'] = NULL;
+    $new_definition['route_parameters'] = [];
+    $new_definition['options'] = [];
+
+    $extracted = $this->pathValidator->getUrlIfValid($form_state->getValue('url'));
+
+    if ($extracted) {
+      if ($extracted->isExternal()) {
+        $new_definition['url'] = $extracted->getPath();
+      }
+      else {
+        $new_definition['route_name'] = $extracted->getRouteName();
+        $new_definition['route_parameters'] = $extracted->getRouteParameters();
+        $new_definition['options'] = $extracted->getOptions();
+      }
     }
+
     $new_definition['title'] = $form_state->getValue(array('title', 0, 'value'));
     $new_definition['description'] = $form_state->getValue(array('description', 0, 'value'));
     $new_definition['weight'] = (int) $form_state->getValue(array('weight', 0, 'value'));
@@ -380,31 +356,11 @@ public function save(array $form, FormStateInterface $form_state) {
    *   The current state of the form.
    */
   protected function doValidate(array $form, FormStateInterface $form_state) {
-    $extracted = $this->extractUrl($form_state->getValue('url'));
+    $extracted = $this->pathValidator->getUrlIfValid($form_state->getValue('url'));
 
-    // If both URL and route_name are empty, the entered value is not valid.
-    $valid = FALSE;
-    if ($extracted['url']) {
-      // This is an external link.
-      $valid = TRUE;
-    }
-    elseif ($extracted['route_name']) {
-      // Users are not allowed to add a link to a page they cannot access.
-      $valid = $this->accessManager->checkNamedRoute($extracted['route_name'], $extracted['route_parameters'], $this->account);
-    }
-    if (!$valid) {
+    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'))));
     }
-    elseif ($extracted['route_name']) {
-      // The user entered a Drupal path.
-      $normal_path = $this->pathAliasManager->getPathByAlias($extracted['path']);
-      if ($extracted['path'] != $normal_path) {
-        drupal_set_message($this->t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array(
-          '%link_path' => $extracted['path'],
-          '%normal_path' => $normal_path,
-        )));
-      }
-    }
   }
 
 }
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 6f446c1..b705ebf 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -259,29 +259,6 @@ function shortcut_set_title_exists($title) {
 }
 
 /**
- * Determines if a path corresponds to a valid shortcut link.
- *
- * @param string $path
- *   The path to the link.
- *
- * @return bool
- *   TRUE if the shortcut link is valid, FALSE otherwise. Valid links are ones
- *   that correspond to actual paths on the site.
- *
- * @see menu_edit_item_validate()
- */
-function shortcut_valid_link($path) {
-  // Do not use URL aliases.
-  $normal_path = \Drupal::service('path.alias_manager')->getPathByAlias($path);
-  if ($path != $normal_path) {
-    $path = $normal_path;
-  }
-
-  // An empty path is valid too and will be converted to <front>.
-  return (!UrlHelper::isExternal($path) && (\Drupal::service('router.route_provider')->getRoutesByPattern('/' . $path)->count() > 0)) || empty($path) || $path == '<front>';
-}
-
-/**
  * Returns an array of shortcut links, suitable for rendering.
  *
  * @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
diff --git a/core/modules/shortcut/src/Controller/ShortcutSetController.php b/core/modules/shortcut/src/Controller/ShortcutSetController.php
index e63e027..729430f 100644
--- a/core/modules/shortcut/src/Controller/ShortcutSetController.php
+++ b/core/modules/shortcut/src/Controller/ShortcutSetController.php
@@ -8,6 +8,7 @@
 namespace Drupal\shortcut\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Path\PathValidatorInterface;
 use Drupal\shortcut\ShortcutSetInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -20,6 +21,30 @@
 class ShortcutSetController extends ControllerBase {
 
   /**
+   * The path validator.
+   *
+   * @var \Drupal\Core\Path\PathValidatorInterface
+   */
+  protected $pathValidator;
+
+  /**
+   * Creates a new ShortcutSetController instance.
+   *
+   * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
+   *   The path validator.
+   */
+  public function __construct(PathValidatorInterface $path_validator) {
+    $this->pathValidator = $path_validator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('path.validator'));
+  }
+
+  /**
    * Creates a new link in the provided shortcut set.
    *
    * @param \Drupal\shortcut\ShortcutSetInterface $shortcut_set
@@ -35,7 +60,7 @@ 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)) {
+    if ($this->pathValidator->isValid($link)) {
       $shortcut = $this->entityManager()->getStorage('shortcut')->create(array(
         'title' => $name,
         'shortcut_set' => $shortcut_set->id(),
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 75541b8..e6fcf49 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -84,6 +84,13 @@ public function setWeight($weight) {
   /**
    * {@inheritdoc}
    */
+  public function getUrl() {
+    return new Url($this->getRouteName(), $this->getRouteParams());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getRouteName() {
     return $this->get('route_name')->value;
   }
diff --git a/core/modules/shortcut/src/ShortcutForm.php b/core/modules/shortcut/src/ShortcutForm.php
index 1c70d52..c9f5d3d 100644
--- a/core/modules/shortcut/src/ShortcutForm.php
+++ b/core/modules/shortcut/src/ShortcutForm.php
@@ -8,8 +8,11 @@
 namespace Drupal\shortcut;
 
 use Drupal\Core\Entity\ContentEntityForm;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Path\PathValidatorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Form controller for the shortcut entity forms.
@@ -24,6 +27,34 @@ class ShortcutForm extends ContentEntityForm {
   protected $entity;
 
   /**
+   * The path validator.
+   *
+   * @var \Drupal\Core\Path\PathValidatorInterface
+   */
+  protected $pathValidator;
+
+  /**
+   * Constructs a new ShortcutForm instance.
+   *
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager
+   * @param \Drupal\Core\Path\PathValidatorInterface $path_validator
+   *   The path validator.
+   */
+  public function __construct(EntityManagerInterface $entity_manager, PathValidatorInterface $path_validator) {
+    parent::__construct($entity_manager);
+
+    $this->pathValidator = $path_validator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('entity.manager'), $container->get('path.validator'));
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function form(array $form, FormStateInterface $form_state) {
@@ -65,7 +96,7 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function validate(array $form, FormStateInterface $form_state) {
-    if (!shortcut_valid_link($form_state->getValue('path'))) {
+    if (!$this->pathValidator->isValid($form_state->getValue('path'))) {
       $form_state->setErrorByName('path', $this->t('The shortcut must correspond to a valid path on the site.'));
     }
 
diff --git a/core/modules/shortcut/src/ShortcutInterface.php b/core/modules/shortcut/src/ShortcutInterface.php
index 6dc0be2..90d90f9 100644
--- a/core/modules/shortcut/src/ShortcutInterface.php
+++ b/core/modules/shortcut/src/ShortcutInterface.php
@@ -53,6 +53,14 @@ public function getWeight();
   public function setWeight($weight);
 
   /**
+   * Returns the URL object pointing to the configured route.
+   *
+   * @return \Drupal\Core\Url
+   *   The URL object.
+   */
+  public function getUrl();
+
+  /**
    * Returns the route name associated with this shortcut, if any.
    *
    * @return string|null
diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
index e13b4eb..1dc314d 100644
--- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -38,13 +38,13 @@ public function testShortcutLinkAdd() {
 
     // Create some paths to test.
     $test_cases = array(
-      array('path' => ''),
-      array('path' => 'admin'),
-      array('path' => 'admin/config/system/site-information'),
-      array('path' => 'node/' . $this->node->id() . '/edit'),
-      array('path' => $path['alias']),
-      array('path' => 'router_test/test2'),
-      array('path' => 'router_test/test3/value'),
+      array('path' => '<front>', 'route_name' => '<front>'),
+      array('path' => 'admin', 'route_name' => 'system.admin'),
+      array('path' => 'admin/config/system/site-information', 'route_name' => 'system.site_information_settings'),
+      array('path' => 'node/' . $this->node->id() . '/edit', 'route_name' => 'entity.node.edit_form'),
+      array('path' => $path['alias'], 'route_name' => 'entity.node.canonical'),
+      array('path' => 'router_test/test2', 'route_name' => 'router_test.2'),
+      array('path' => 'router_test/test3/value', 'route_name' => 'router_test.3'),
     );
 
     // Check that each new shortcut links where it should.
@@ -57,8 +57,8 @@ public function testShortcutLinkAdd() {
       $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
       $this->assertResponse(200);
       $saved_set = ShortcutSet::load($set->id());
-      $paths = $this->getShortcutInformation($saved_set, 'path');
-      $this->assertTrue(in_array($this->container->get('path.alias_manager')->getPathByAlias($test['path']), $paths), 'Shortcut created: ' . $test['path']);
+      $routes = $this->getShortcutInformation($saved_set, 'route_name');
+      $this->assertTrue(in_array($test['route_name'], $routes), 'Shortcut created: ' . $test['path']);
       $this->assertLink($title, 0, 'Shortcut link found on the page.');
     }
     $saved_set = ShortcutSet::load($set->id());
@@ -73,6 +73,25 @@ public function testShortcutLinkAdd() {
         $this->assertEqual($entity->get('route_parameters')->first()->getValue(), $loaded->get('route_parameters')->first()->getValue());
       }
     }
+
+    // Login as non admin user, to check that access is checked when creating
+    // shortcuts.
+    $this->drupalLogin($this->shortcut_user);
+    $title = $this->randomMachineName();
+    $form_data = [
+      'title[0][value]' => $title,
+      'path' => 'admin',
+    ];
+    $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
+    $this->assertResponse(200);
+    $this->assertRaw(t('The shortcut must correspond to a valid path on the site.'));
+
+    $form_data = [
+      'title[0][value]' => $title,
+      'path' => 'node',
+    ];
+    $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
+    $this->assertLink($title, 0, 'Shortcut link found on the page.');
   }
 
   /**
@@ -137,8 +156,8 @@ public function testShortcutLinkChangePath() {
     $shortcut = reset($shortcuts);
     $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $shortcut->getTitle(), 'path' => $new_link_path), t('Save'));
     $saved_set = ShortcutSet::load($set->id());
-    $paths = $this->getShortcutInformation($saved_set, 'path');
-    $this->assertTrue(in_array($new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
+    $routes = $this->getShortcutInformation($saved_set, 'route_name');
+    $this->assertTrue(in_array('system.admin_config', $routes), 'Shortcut path changed: ' . $new_link_path);
     $this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.');
   }
 
diff --git a/core/modules/shortcut/src/Tests/ShortcutTestBase.php b/core/modules/shortcut/src/Tests/ShortcutTestBase.php
index a61f80e..388f5d2 100644
--- a/core/modules/shortcut/src/Tests/ShortcutTestBase.php
+++ b/core/modules/shortcut/src/Tests/ShortcutTestBase.php
@@ -73,8 +73,8 @@ protected function setUp() {
     }
 
     // Create users.
-    $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users'));
-    $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets'));
+    $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users', 'link to any page'));
+    $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets', 'access shortcuts', 'access content'));
 
     // Create a node.
     $this->node = $this->drupalCreateNode(array('type' => 'article'));
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index dc372f8..c87b768 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -261,6 +261,11 @@ function system_permission() {
       'title' => t('View site reports'),
       'restrict access' => TRUE,
     ),
+    'link to any page' => [
+      'title' => t('Link to any page'),
+      'description' => t('This allows to bypass access checking when linking to internal paths.'),
+      'restrict access' => TRUE,
+    ],
   );
 }
 
diff --git a/core/modules/user/src/Access/LoginStatusCheck.php b/core/modules/user/src/Access/LoginStatusCheck.php
index fbceacd..7539fe2 100644
--- a/core/modules/user/src/Access/LoginStatusCheck.php
+++ b/core/modules/user/src/Access/LoginStatusCheck.php
@@ -9,7 +9,6 @@
 
 use Drupal\Core\Routing\Access\AccessInterface;
 use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Determines access to routes based on login status of current user.
@@ -19,16 +18,14 @@ class LoginStatusCheck implements AccessInterface {
   /**
    * Checks access.
    *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request object.
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The currently logged in account.
    *
    * @return string
    *   A \Drupal\Core\Access\AccessInterface constant value.
    */
-  public function access(Request $request, AccountInterface $account) {
-    return ($request->attributes->get('_menu_admin') || $account->isAuthenticated()) ? static::ALLOW : static::DENY;
+  public function access(AccountInterface $account) {
+    return $account->isAuthenticated() ? static::ALLOW : static::DENY;
   }
 
 }
diff --git a/core/modules/user/src/Access/RegisterAccessCheck.php b/core/modules/user/src/Access/RegisterAccessCheck.php
index aec179b..aa9433f 100644
--- a/core/modules/user/src/Access/RegisterAccessCheck.php
+++ b/core/modules/user/src/Access/RegisterAccessCheck.php
@@ -9,7 +9,6 @@
 
 use Drupal\Core\Routing\Access\AccessInterface;
 use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Access check for user registration routes.
@@ -19,15 +18,13 @@ class RegisterAccessCheck implements AccessInterface {
   /**
    * Checks access.
    *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request object.
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The currently logged in account.
    *
    * @return string
    *   A \Drupal\Core\Access\AccessInterface constant value.
    */
-  public function access(Request $request, AccountInterface $account) {
-    return ($request->attributes->get('_menu_admin') || $account->isAnonymous()) && (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) ? static::ALLOW : static::DENY;
+  public function access(AccountInterface $account) {
+    return ($account->isAnonymous()) && (\Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) ? static::ALLOW : static::DENY;
   }
 }
diff --git a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
index 46232a5..12cbad5 100644
--- a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
@@ -273,6 +273,22 @@ public static function providerTestParse() {
         ),
       ),
       array(
+        'http://',
+        array(
+          'path' => '',
+          'query' => array(),
+          'fragment' => '',
+        ),
+      ),
+      array(
+        'https://',
+        array(
+          'path' => '',
+          'query' => array(),
+          'fragment' => '',
+        ),
+      ),
+      array(
         '/my/path?destination=home#footer',
         array(
           'path' => '/my/path',
diff --git a/core/tests/Drupal/Tests/Core/DrupalTest.php b/core/tests/Drupal/Tests/Core/DrupalTest.php
index 0491099..f618beb 100644
--- a/core/tests/Drupal/Tests/Core/DrupalTest.php
+++ b/core/tests/Drupal/Tests/Core/DrupalTest.php
@@ -320,6 +320,22 @@ public function testFormBuilder() {
   }
 
   /**
+   * Tests the menuTree() method.
+   */
+  public function testMenuTree() {
+    $this->setMockContainerService('menu.link_tree');
+    $this->assertNotNull(\Drupal::menuTree());
+  }
+
+  /**
+   * Tests the pathValidator() method.
+   */
+  public function testPathValidator() {
+    $this->setMockContainerService('path.validator');
+    $this->assertNotNull(\Drupal::pathValidator());
+  }
+
+  /**
    * Sets up a mock expectation for the container get() method.
    *
    * @param string $service_name
diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
index 3285a9d..57a51d4 100644
--- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
+++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
@@ -76,15 +76,10 @@ public function testCreateFromPath() {
    *
    * @covers ::createFromRequest()
    *
-   * @expectedException \Drupal\Core\Routing\MatchingRouteNotFoundException
-   * @expectedExceptionMessage No matching route could be found for the request: request_as_a_string
+   * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
    */
   public function testCreateFromRequest() {
-    // Mock the request in order to override the __toString() method.
-    $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
-    $request->expects($this->once())
-      ->method('__toString')
-      ->will($this->returnValue('request_as_a_string'));
+    $request = Request::create('/test-path');
 
     $this->router->expects($this->once())
       ->method('matchRequest')
diff --git a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
index 4309a4e..0080e7d 100644
--- a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
@@ -44,7 +44,7 @@ class DefaultMenuLinkTreeManipulatorsTest extends UnitTestCase {
   /**
    * The original menu tree build in mockTree().
    *
-   * @var \Drupal\Tests\Core\Menu\MenuLinkMock[]
+   * @var \Drupal\Core\Menu\MenuLinkTreeElement[]
    */
   protected $originalTree = array();
 
@@ -134,6 +134,7 @@ public function testGenerateIndexAndSort() {
    * Tests the checkAccess() tree manipulator.
    *
    * @covers ::checkAccess
+   * @covers ::menuLinkCheckAccess
    */
   public function testCheckAccess() {
     // Those menu links that are non-external will have their access checks
@@ -179,6 +180,31 @@ public function testCheckAccess() {
   }
 
   /**
+   * Tests checkAccess() tree manipulator with 'link to any page' permission.
+   *
+   * @covers ::checkAccess
+   * @covers ::menuLinkCheckAccess
+   */
+  public function testCheckAccessWithLinkToAnyPagePermission() {
+    $this->mockTree();
+    $this->currentUser->expects($this->exactly(8))
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(TRUE);
+
+    $this->mockTree();
+    $this->defaultMenuTreeManipulators->checkAccess($this->originalTree);
+
+    $this->assertTrue($this->originalTree[1]->access);
+    $this->assertTrue($this->originalTree[2]->access);
+    $this->assertTrue($this->originalTree[2]->subtree[3]->access);
+    $this->assertTrue($this->originalTree[2]->subtree[3]->subtree[4]->access);
+    $this->assertTrue($this->originalTree[5]->subtree[7]->access);
+    $this->assertTrue($this->originalTree[6]->access);
+    $this->assertTrue($this->originalTree[8]->access);
+  }
+
+  /**
    * Tests the flatten() tree manipulator.
    *
    * @covers ::flatten
diff --git a/core/tests/Drupal/Tests/Core/Path/PathValidatorTest.php b/core/tests/Drupal/Tests/Core/Path/PathValidatorTest.php
new file mode 100644
index 0000000..fe704e2
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Path/PathValidatorTest.php
@@ -0,0 +1,326 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Path\PathValidatorTest.
+ */
+
+namespace Drupal\Tests\Core\Path;
+
+use Drupal\Core\ParamConverter\ParamNotConvertedException;
+use Drupal\Core\Path\PathValidator;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Path\PathValidator
+ * @group Routing
+ */
+class PathValidatorTest extends UnitTestCase {
+
+  /**
+   * The mocked access aware router.
+   *
+   * @var \Drupal\Core\Routing\AccessAwareRouterInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $accessAwareRouter;
+
+  /**
+   * The mocked access unaware router.
+   * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $accessUnawareRouter;
+
+  /**
+   * The mocked account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $account;
+
+  /**
+   * The path processor.
+   *
+   * @var \Drupal\Core\PathProcessor\InboundPathProcessorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $pathProcessor;
+
+  /**
+   * The tested path validator.
+   *
+   * @var \Drupal\Core\Path\PathValidator
+   */
+  protected $pathValidator;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->accessAwareRouter = $this->getMock('Drupal\Core\Routing\AccessAwareRouterInterface');
+    $this->accessUnawareRouter = $this->getMock('Symfony\Component\Routing\Matcher\UrlMatcherInterface');
+    $this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
+    $this->pathProcessor = $this->getMock('Drupal\Core\PathProcessor\InboundPathProcessorInterface');
+    $this->pathValidator = new PathValidator($this->accessAwareRouter, $this->accessUnawareRouter, $this->account, $this->pathProcessor);
+  }
+
+  /**
+   * Tests the isValid() method for the frontpage.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithFrontpage() {
+    $this->accessAwareRouter->expects($this->never())
+      ->method('match');
+
+    $this->assertTrue($this->pathValidator->isValid('<front>'));
+  }
+
+  /**
+   * Tests the isValid() method for an external URL.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithExternalUrl() {
+    $this->accessAwareRouter->expects($this->never())
+      ->method('match');
+
+    $this->assertTrue($this->pathValidator->isValid('https://drupal.org'));
+  }
+
+  /**
+   * Tests the isValid() method with an invalid external URL.
+   */
+  public function testIsValidWithInvalidExternalUrl() {
+    $this->accessAwareRouter->expects($this->never())
+      ->method('match');
+
+    $this->assertFalse($this->pathValidator->isValid('http://'));
+  }
+
+  /**
+   * Tests the isValid() method with a 'link to any page' permission.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithLinkToAnyPageAccount() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(TRUE);
+    $this->accessAwareRouter->expects($this->never())
+      ->method('match');
+    $this->accessUnawareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path')
+      ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+
+    $this->assertTrue($this->pathValidator->isValid('test-path'));
+  }
+
+  /**
+   * Tests the isValid() method without the 'link to any page' permission.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithoutLinkToAnyPageAccount() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+    $this->accessUnawareRouter->expects($this->never())
+      ->method('match');
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path')
+      ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $this->assertTrue($this->pathValidator->isValid('test-path'));
+  }
+
+  /**
+   * Tests the isValid() method with a path alias.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithPathAlias() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+    $this->accessUnawareRouter->expects($this->never())
+      ->method('match');
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path')
+      ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->with('path-alias', $this->anything())
+      ->willReturn('test-path');
+
+    $this->assertTrue($this->pathValidator->isValid('path-alias'));
+  }
+
+  /**
+   * Tests the isValid() method with a user without access to the path.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithAccessDenied() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+    $this->accessUnawareRouter->expects($this->never())
+      ->method('match');
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path')
+      ->willThrowException(new AccessDeniedHttpException());
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $this->assertFalse($this->pathValidator->isValid('test-path'));
+  }
+
+  /**
+   * Tests the isValid() method with a not working param converting.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithFailingParameterConverting() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+    $this->accessUnawareRouter->expects($this->never())
+      ->method('match');
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/entity-test/1')
+      ->willThrowException(new ParamNotConvertedException());
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $this->assertFalse($this->pathValidator->isValid('entity-test/1'));
+  }
+
+  /**
+   * Tests the isValid() method with a not existing path.
+   *
+   * @covers ::isValid
+   */
+  public function testIsValidWithNotExistingPath() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+    $this->accessUnawareRouter->expects($this->never())
+      ->method('match');
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/not-existing-path')
+      ->willThrowException(new ResourceNotFoundException());
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $this->assertFalse($this->pathValidator->isValid('not-existing-path'));
+  }
+
+  /**
+   * Tests the getUrlIfValid() method when there is access.
+   */
+  public function testGetUrlIfValidWithAccess() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path')
+      ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag(['key' => 'value'])]);
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $url = $this->pathValidator->getUrlIfValid('test-path');
+    $this->assertInstanceOf('Drupal\Core\Url', $url);
+
+    $this->assertEquals('test_route', $url->getRouteName());
+    $this->assertEquals(['key' => 'value'], $url->getRouteParameters());
+  }
+
+  /**
+   * Tests the getUrlIfValid() method with a query in the path.
+   */
+  public function testGetUrlIfValidWithQuery() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path?k=bar')
+      ->willReturn([RouteObjectInterface::ROUTE_NAME => 'test_route', '_raw_variables' => new ParameterBag()]);
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $url = $this->pathValidator->getUrlIfValid('test-path?k=bar');
+    $this->assertInstanceOf('Drupal\Core\Url', $url);
+
+    $this->assertEquals('test_route', $url->getRouteName());
+    $this->assertEquals(['k' => 'bar'], $url->getOptions()['query']);
+  }
+
+  /**
+   * Tests the getUrlIfValid() method where there is no access.
+   */
+  public function testGetUrlIfValidWithoutAccess() {
+    $this->account->expects($this->once())
+      ->method('hasPermission')
+      ->with('link to any page')
+      ->willReturn(FALSE);
+
+    $this->accessAwareRouter->expects($this->once())
+      ->method('match')
+      ->with('/test-path')
+      ->willThrowException(new AccessDeniedHttpException());
+
+    $this->pathProcessor->expects($this->once())
+      ->method('processInbound')
+      ->willReturnArgument(0);
+
+    $url = $this->pathValidator->getUrlIfValid('test-path');
+    $this->assertFalse($url);
+  }
+
+  /**
+   * Tests the getUrlIfValid() method with a front page + query + fragments.
+   */
+  public function testGetUrlIfValidWithFrontPageAndQueryAndFragments() {
+    $url = $this->pathValidator->getUrlIfValid('<front>?hei=sen#berg');
+    $this->assertEquals('<front>', $url->getRouteName());
+    $this->assertEquals(['hei' => 'sen'], $url->getOptions()['query']);
+    $this->assertEquals('berg', $url->getOptions()['fragment']);
+  }
+
+}
+
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index 735af21..92f429d 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -72,22 +72,27 @@ protected function setUp() {
    * @covers ::createFromPath()
    */
   public function testCreateFromPath() {
-    $this->router->expects($this->any())
-      ->method('match')
-      ->will($this->returnValueMap(array(
-        array('/node', array(
+    $this->router->expects($this->at(0))
+      ->method('matchRequest')
+      ->with(Request::create('/node'))
+      ->willReturn([
           RouteObjectInterface::ROUTE_NAME => 'view.frontpage.page_1',
           '_raw_variables' => new ParameterBag(),
-        )),
-        array('/node/1', array(
-          RouteObjectInterface::ROUTE_NAME => 'node_view',
-          '_raw_variables' => new ParameterBag(array('node' => '1')),
-        )),
-        array('/node/2/edit', array(
-          RouteObjectInterface::ROUTE_NAME => 'node_edit',
-          '_raw_variables' => new ParameterBag(array('node' => '2')),
-        )),
-      )));
+        ]);
+    $this->router->expects($this->at(1))
+      ->method('matchRequest')
+      ->with(Request::create('/node/1'))
+      ->willReturn([
+        RouteObjectInterface::ROUTE_NAME => 'node_view',
+        '_raw_variables' => new ParameterBag(['node' => '1']),
+      ]);
+    $this->router->expects($this->at(2))
+      ->method('matchRequest')
+      ->with(Request::create('/node/2/edit'))
+      ->willReturn([
+        RouteObjectInterface::ROUTE_NAME => 'node_edit',
+        '_raw_variables' => new ParameterBag(['node' => '2']),
+      ]);
 
     $urls = array();
     foreach ($this->map as $index => $values) {
@@ -114,13 +119,12 @@ public function testCreateFromPathFront() {
    *
    * @covers ::createFromPath()
    *
-   * @expectedException \Drupal\Core\Routing\MatchingRouteNotFoundException
-   * @expectedExceptionMessage No matching route could be found for the path "non-existent"
+   * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
    */
   public function testCreateFromPathInvalid() {
     $this->router->expects($this->once())
-      ->method('match')
-      ->with('/non-existent')
+      ->method('matchRequest')
+      ->with(Request::create('/non-existent'))
       ->will($this->throwException(new ResourceNotFoundException()));
 
     $this->assertNull(Url::createFromPath('non-existent'));
@@ -155,15 +159,10 @@ public function testCreateFromRequest() {
    *
    * @covers ::createFromRequest()
    *
-   * @expectedException \Drupal\Core\Routing\MatchingRouteNotFoundException
-   * @expectedExceptionMessage No matching route could be found for the request: request_as_a_string
+   * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
    */
   public function testCreateFromRequestInvalid() {
-    // Mock the request in order to override the __toString() method.
-    $request = $this->getMock('Symfony\Component\HttpFoundation\Request');
-    $request->expects($this->once())
-      ->method('__toString')
-      ->will($this->returnValue('request_as_a_string'));
+    $request = Request::create('/test-path');
 
     $this->router->expects($this->once())
       ->method('matchRequest')
