diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index be8c695..f991991 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -6,7 +6,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Timer;
 use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
diff --git a/core/includes/common.inc b/core/includes/common.inc
index b97cc5e..f06cae2 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -6,7 +6,7 @@
 use Drupal\Component\Utility\SortArray;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Tags;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
@@ -421,26 +421,26 @@ function drupal_get_feeds($delimiter = "\n") {
  * @return
  *   An array containing query parameters, which can be used for url().
  *
- * @deprecated as of Drupal 8.0. Use Url::filterQueryParameters() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::filterQueryParameters() instead.
  */
 function drupal_get_query_parameters(array $query = NULL, array $exclude = array(), $parent = '') {
   if (!isset($query)) {
     $query = \Drupal::request()->query->all();
   }
-  return Url::filterQueryParameters($query, $exclude, $parent);
+  return UrlHelper::filterQueryParameters($query, $exclude, $parent);
 }
 
 /**
  * Parses an array into a valid, rawurlencoded query string.
  *
  * @see drupal_get_query_parameters()
- * @deprecated as of Drupal 8.0. Use Url::buildQuery() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::buildQuery() instead.
  * @ingroup php_wrappers
  *
- * @deprecated as of Drupal 8.0. Use Url::buildQuery() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::buildQuery() instead.
  */
 function drupal_http_build_query(array $query, $parent = '') {
-  return Url::buildQuery($query, $parent);
+  return UrlHelper::buildQuery($query, $parent);
 }
 
 /**
@@ -471,7 +471,7 @@ function drupal_get_destination() {
   }
   else {
     $path = current_path();
-    $query = Url::buildQuery(Url::filterQueryParameters($query->all()));
+    $query = UrlHelper::buildQuery(UrlHelper::filterQueryParameters($query->all()));
     if ($query != '') {
       $path .= '?' . $query;
     }
@@ -513,10 +513,10 @@ function drupal_get_destination() {
  * @see url()
  * @ingroup php_wrappers
  *
- * @deprecated as of Drupal 8.0. Use Url::parse() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::parse() instead.
  */
 function drupal_parse_url($url) {
-  return Url::parse($url);
+  return UrlHelper::parse($url);
 }
 
 /**
@@ -530,10 +530,10 @@ function drupal_parse_url($url) {
  * @param $path
  *   The Drupal path to encode.
  *
- * @deprecated as of Drupal 8.0. Use Url::encodePath() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::encodePath() instead.
  */
 function drupal_encode_path($path) {
-  return Url::encodePath($path);
+  return UrlHelper::encodePath($path);
 }
 
 /**
@@ -545,10 +545,10 @@ function drupal_encode_path($path) {
  * @return
  *   TRUE if the URL has the same domain and base path.
  *
- * @deprecated as of Drupal 8.0. Use Url::externalIsLocal() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::externalIsLocal() instead.
  */
 function _external_url_is_local($url) {
-  return Url::externalIsLocal($url, base_path());
+  return UrlHelper::externalIsLocal($url, base_path());
 }
 
 /**
@@ -602,12 +602,12 @@ function valid_email_address($mail) {
  * @return
  *   TRUE if the URL is in a valid format.
  *
- * @see \Drupal\Component\Utility\Url::isValid()
+ * @see \Drupal\Component\Utility\UrlHelper::isValid()
  *
- * @deprecated as of Drupal 8.0. Use Url::isValid() instead.
+ * @deprecated as of Drupal 8.0. Use UrlHelper::isValid() instead.
  */
 function valid_url($url, $absolute = FALSE) {
-  return Url::isValid($url, $absolute);
+  return UrlHelper::isValid($url, $absolute);
 }
 
 /**
@@ -666,10 +666,10 @@ function valid_number_step($value, $step, $offset = 0.0) {
  *   \Drupal\Component\Utility\String::checkPlain() being called on it. However,
  *   it can be passed to functions expecting plain-text strings.
  *
- * @see \Drupal\Component\Utility\Url::stripDangerousProtocols()
+ * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
  */
 function drupal_strip_dangerous_protocols($uri) {
-  return Url::stripDangerousProtocols($uri);
+  return UrlHelper::stripDangerousProtocols($uri);
 }
 
 /**
@@ -685,13 +685,13 @@ function drupal_strip_dangerous_protocols($uri) {
  *   because Drupal\Core\Template\Attribute expects those values to be
  *   plain-text strings. To pass a filtered URI to
  *   Drupal\Core\Template\Attribute, call
- *   \Drupal\Component\Utility\Url::stripDangerousProtocols() instead.
+ *   \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() instead.
  *
- * @see \Drupal\Component\Utility\Url::stripDangerousProtocols()
+ * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
  * @see \Drupal\Component\Utility\String::checkPlain()
  */
 function check_url($uri) {
-  return String::checkPlain(Url::stripDangerousProtocols($uri));
+  return String::checkPlain(UrlHelper::stripDangerousProtocols($uri));
 }
 
 /**
@@ -757,10 +757,10 @@ function filter_xss($string, $allowed_tags = array('a', 'em', 'strong', 'cite',
  * @return string
  *   Cleaned up and HTML-escaped version of $string.
  *
- * @see \Drupal\Component\Utility\Url::filterBadProtocol()
+ * @see \Drupal\Component\Utility\UrlHelper::filterBadProtocol()
  */
 function filter_xss_bad_protocol($string) {
-  return Url::filterBadProtocol($string);
+  return UrlHelper::filterBadProtocol($string);
 }
 
 /**
@@ -1140,7 +1140,7 @@ function url($path = NULL, array $options = array()) {
  *   Boolean TRUE or FALSE, where TRUE indicates an external path.
  */
 function url_is_external($path) {
-  return Url::isExternal($path);
+  return UrlHelper::isExternal($path);
 }
 
 /**
@@ -3082,7 +3082,7 @@ function _drupal_bootstrap_code() {
     // of allowed protocols for these cases.
     $allowed_protocols = array('http', 'https');
   }
-  Url::setAllowedProtocols($allowed_protocols);
+  UrlHelper::setAllowedProtocols($allowed_protocols);
 }
 
 /**
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 5d4a943..e41e360 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -8,7 +8,7 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\Number;
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Template\Attribute;
@@ -2565,7 +2565,7 @@ function form_pre_render_color($element) {
 function theme_form($variables) {
   $element = $variables['element'];
   if (isset($element['#action'])) {
-    $element['#attributes']['action'] = Url::stripDangerousProtocols($element['#action']);
+    $element['#attributes']['action'] = UrlHelper::stripDangerousProtocols($element['#action']);
   }
   element_set_attributes($element, array('method', 'id'));
   if (empty($element['#attributes']['accept-charset'])) {
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index bbf5c43..02f40b7 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -16,6 +16,7 @@
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 use Symfony\Component\Routing\Route;
 
 /**
@@ -922,7 +923,6 @@ function _menu_link_translate(&$item) {
  * @return bool
  *   TRUE if the user has access or FALSE if the user should be presented
  *   with access denied.
- *
  */
 function menu_item_route_access(Route $route, $href, &$map, Request $request = NULL) {
   if (!isset($request)) {
@@ -937,6 +937,9 @@ function menu_item_route_access(Route $route, $href, &$map, Request $request = N
   catch (NotFoundHttpException $e) {
     return FALSE;
   }
+  catch (ResourceNotFoundException $e) {
+    return FALSE;
+  }
 
   // Populate the map with any matching values from the request.
   $path_bits = explode('/', trim($route->getPath(), '/'));
diff --git a/core/includes/pager.inc b/core/includes/pager.inc
index 9874689..caf1044 100644
--- a/core/includes/pager.inc
+++ b/core/includes/pager.inc
@@ -6,7 +6,7 @@
  */
 
 use Drupal\Core\Template\Attribute;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 
 /**
  * Returns the current page being requested for display within a pager.
@@ -138,7 +138,7 @@ function pager_default_initialize($total, $limit, $element = 0) {
 function pager_get_query_parameters() {
   $query = &drupal_static(__FUNCTION__);
   if (!isset($query)) {
-    $query = Url::filterQueryParameters(\Drupal::request()->query->all(), array('page'));
+    $query = UrlHelper::filterQueryParameters(\Drupal::request()->query->all(), array('page'));
   }
   return $query;
 }
diff --git a/core/includes/tablesort.inc b/core/includes/tablesort.inc
index 1f6962b..e342e3e 100644
--- a/core/includes/tablesort.inc
+++ b/core/includes/tablesort.inc
@@ -3,7 +3,7 @@
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Database\Query\SelectExtender;
 use Drupal\Core\Database\Query\SelectInterface;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 
 /**
  * @file
@@ -101,7 +101,7 @@ function tablesort_cell($cell, $header, $ts, $i) {
  *   page request except for those pertaining to table sorting.
  */
 function tablesort_get_query_parameters() {
-  return Url::filterQueryParameters(\Drupal::request()->query->all(), array('sort', 'order'));
+  return UrlHelper::filterQueryParameters(\Drupal::request()->query->all(), array('sort', 'order'));
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 96a0466..529ce90 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -9,7 +9,7 @@
  */
 
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Config\Config;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Extension\ExtensionNameLengthException;
@@ -2117,7 +2117,7 @@ function template_preprocess_html(&$variables) {
     $type = theme_get_setting('favicon.mimetype');
     $build['#attached']['drupal_add_html_head_link'][][] = array(
       'rel' => 'shortcut icon',
-      'href' => Url::stripDangerousProtocols($favicon),
+      'href' => UrlHelper::stripDangerousProtocols($favicon),
       'type' => $type,
     );
     drupal_render($build);
@@ -2410,7 +2410,7 @@ function template_preprocess_maintenance_page(&$variables) {
     $type = theme_get_setting('favicon.mimetype');
     $build['#attached']['drupal_add_html_head_link'][][] = array(
       'rel' => 'shortcut icon',
-      'href' => Url::stripDangerousProtocols($favicon),
+      'href' => UrlHelper::stripDangerousProtocols($favicon),
       'type' => $type,
     );
     drupal_render($build);
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 0ec88d7..f98d607 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -5,6 +5,7 @@
  * Contains Drupal.
  */
 
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -517,7 +518,13 @@ public static function linkGenerator() {
    * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
    */
   public static function l($text, $route_name, array $parameters = array(), array $options = array()) {
-    return static::$container->get('link_generator')->generate($text, $route_name, $parameters, $options);
+    $link_generator = static::linkGenerator();
+    if ($route_name instanceof Url) {
+      return $link_generator->generateFromUrl($text, $route_name);
+    }
+    else {
+      return $link_generator->generate($text, $route_name, $parameters, $options);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Component/Utility/Url.php b/core/lib/Drupal/Component/Utility/UrlHelper.php
similarity index 98%
rename from core/lib/Drupal/Component/Utility/Url.php
rename to core/lib/Drupal/Component/Utility/UrlHelper.php
index c7eff3e..bf956c9 100644
--- a/core/lib/Drupal/Component/Utility/Url.php
+++ b/core/lib/Drupal/Component/Utility/UrlHelper.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\Component\Utility\Url.
+ * Contains \Drupal\Component\Utility\UrlHelper.
  */
 
 namespace Drupal\Component\Utility;
@@ -10,7 +10,7 @@
 /**
  * Helper class URL based methods.
  */
-class Url {
+class UrlHelper {
 
   /**
    * The list of allowed protocols.
@@ -119,7 +119,7 @@ public static function filterQueryParameters(array $query, array $exclude = arra
    * The returned array contains a 'path' that may be passed separately to url().
    * For example:
    * @code
-   *   $options = Url::parse(\Drupal::request()->query->get('destination'));
+   *   $options = UrlHelper::parse(\Drupal::request()->query->get('destination'));
    *   $my_url = url($options['path'], $options);
    *   $my_link = l('Example link', $options['path'], $options);
    * @endcode
diff --git a/core/lib/Drupal/Component/Utility/Xss.php b/core/lib/Drupal/Component/Utility/Xss.php
index 0daab37..2485f6c 100644
--- a/core/lib/Drupal/Component/Utility/Xss.php
+++ b/core/lib/Drupal/Component/Utility/Xss.php
@@ -226,7 +226,7 @@ protected static function attributes($attributes) {
         case 2:
           // Attribute value, a URL after href= for instance.
           if (preg_match('/^"([^"]*)"(\s+|$)/', $attributes, $match)) {
-            $thisval = Url::filterBadProtocol($match[1]);
+            $thisval = UrlHelper::filterBadProtocol($match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
@@ -238,7 +238,7 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("/^'([^']*)'(\s+|$)/", $attributes, $match)) {
-            $thisval = Url::filterBadProtocol($match[1]);
+            $thisval = UrlHelper::filterBadProtocol($match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name='$thisval'";
@@ -249,7 +249,7 @@ protected static function attributes($attributes) {
           }
 
           if (preg_match("%^([^\s\"']+)(\s+|$)%", $attributes, $match)) {
-            $thisval = Url::filterBadProtocol($match[1]);
+            $thisval = UrlHelper::filterBadProtocol($match[1]);
 
             if (!$skip) {
               $attributes_array[] = "$attribute_name=\"$thisval\"";
diff --git a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
index f2b4f25..0475d5a 100644
--- a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
+++ b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
@@ -8,7 +8,7 @@
 namespace Drupal\Core\Form;
 
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -36,7 +36,7 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ
     $query = $request->query;
     // If a destination is specified, that serves as the cancel link.
     if ($query->has('destination')) {
-      $options = Url::parse($query->get('destination'));
+      $options = UrlHelper::parse($query->get('destination'));
       $link = array(
         '#href' => $options['path'],
         '#options' => $options,
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 43d15a6..821ed61 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -10,7 +10,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Access\CsrfTokenGenerator;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\HttpKernel;
@@ -18,6 +18,7 @@
 use Drupal\Core\Render\Element;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Request;
@@ -847,7 +848,7 @@ public function validateForm($form_id, &$form, &$form_state) {
     if (isset($form['#token'])) {
       if (!$this->csrfToken->validate($form_state['values']['form_token'], $form['#token'])) {
         $path = $this->request->attributes->get('_system_path');
-        $query = Url::filterQueryParameters($this->request->query->all());
+        $query = UrlHelper::filterQueryParameters($this->request->query->all());
         $url = $this->urlGenerator->generateFromPath($path, array('query' => $query));
 
         // Setting this error will cause the form to fail validation.
@@ -933,13 +934,17 @@ public function redirectForm($form_state) {
 
     // Check for a route-based redirection.
     if (isset($form_state['redirect_route'])) {
-      $form_state['redirect_route'] += array(
-        'route_parameters' => array(),
-        'options' => array(),
-      );
-      $form_state['redirect_route']['options']['absolute'] = TRUE;
-      $url = $this->urlGenerator->generateFromRoute($form_state['redirect_route']['route_name'], $form_state['redirect_route']['route_parameters'], $form_state['redirect_route']['options']);
-      return new RedirectResponse($url);
+      // @todo Remove once all redirects are converted to Url.
+      if (!($form_state['redirect_route'] instanceof Url)) {
+        $form_state['redirect_route'] += array(
+          'route_parameters' => array(),
+          'options' => array(),
+        );
+        $form_state['redirect_route'] = new Url($form_state['redirect_route']['route_name'], $form_state['redirect_route']['route_parameters'], $form_state['redirect_route']['options']);
+      }
+
+      $form_state['redirect_route']->setAbsolute();
+      return new RedirectResponse($form_state['redirect_route']->toString());
     }
 
     // Only invoke a redirection if redirect value was not set to FALSE.
@@ -1318,7 +1323,7 @@ public function doBuildForm($form_id, &$element, &$form_state) {
     // Special handling if we're on the top level form element.
     if (isset($element['#type']) && $element['#type'] == 'form') {
       if (!empty($element['#https']) && settings()->get('mixed_mode_sessions', FALSE) &&
-        !Url::isExternal($element['#action'])) {
+        !UrlHelper::isExternal($element['#action'])) {
         global $base_root;
 
         // Not an external URL so ensure that it is secure.
diff --git a/core/lib/Drupal/Core/Routing/NullGenerator.php b/core/lib/Drupal/Core/Routing/NullGenerator.php
index 1430f1f..a677a60 100644
--- a/core/lib/Drupal/Core/Routing/NullGenerator.php
+++ b/core/lib/Drupal/Core/Routing/NullGenerator.php
@@ -12,7 +12,7 @@
 use Symfony\Component\Routing\Route;
 
 /**
- * No-op implementation of a Url Generator, needed for backward compatibility.
+ * No-op implementation of a UrlGenerator, needed for backward compatibility.
  */
 class NullGenerator extends UrlGenerator {
 
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index 1bb4b53..9496e19 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -16,7 +16,7 @@
 use Symfony\Cmf\Component\Routing\ProviderBasedGenerator;
 
 use Drupal\Component\Utility\Settings;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
 use Drupal\Core\RouteProcessor\OutboundRouteProcessorInterface;
@@ -92,7 +92,7 @@ public function __construct(RouteProviderInterface $provider, OutboundPathProces
     $this->routeProcessor = $route_processor;
     $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE);
     $allowed_protocols = $config->get('system.filter')->get('protocols') ?: array('http', 'https');
-    Url::setAllowedProtocols($allowed_protocols);
+    UrlHelper::setAllowedProtocols($allowed_protocols);
   }
 
   /**
@@ -237,12 +237,12 @@ public function generateFromPath($path = NULL, $options = array()) {
 
     if (!isset($options['external'])) {
       // Return an external link if $path contains an allowed absolute URL. Only
-      // call the slow \Drupal\Component\Utility\Url::stripDangerousProtocols()
+      // call the slow \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
       // if $path contains a ':' before any / ? or #. Note: we could use
       // url_is_external($path) here, but that would require another function
       // call, and performance inside url() is critical.
       $colonpos = strpos($path, ':');
-      $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && Url::stripDangerousProtocols($path) == $path);
+      $options['external'] = ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && UrlHelper::stripDangerousProtocols($path) == $path);
     }
 
     if (isset($options['fragment']) && $options['fragment'] !== '') {
@@ -260,7 +260,7 @@ public function generateFromPath($path = NULL, $options = array()) {
       }
       // Append the query.
       if ($options['query']) {
-        $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . Url::buildQuery($options['query']);
+        $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']);
       }
       if (isset($options['https']) && $this->mixedModeSessions) {
         if ($options['https'] === TRUE) {
@@ -303,7 +303,7 @@ public function generateFromPath($path = NULL, $options = array()) {
     $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
 
     $path = str_replace('%2F', '/', rawurlencode($prefix . $path));
-    $query = $options['query'] ? ('?' . Url::buildQuery($options['query'])) : '';
+    $query = $options['query'] ? ('?' . UrlHelper::buildQuery($options['query'])) : '';
     return $base . $options['script'] . $path . $query . $options['fragment'];
   }
 
diff --git a/core/lib/Drupal/Core/Routing/UrlMatcher.php b/core/lib/Drupal/Core/Routing/UrlMatcher.php
index 9e973d4..c347b51 100644
--- a/core/lib/Drupal/Core/Routing/UrlMatcher.php
+++ b/core/lib/Drupal/Core/Routing/UrlMatcher.php
@@ -30,36 +30,16 @@ public function finalMatch(RouteCollection $collection, Request $request) {
     $context = new RequestContext();
     $context->fromRequest($request);
     $this->setContext($context);
-    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;
+    if ($request->attributes->has('_system_path')) {
+      // _system_path never has leading or trailing slashes.
+      $path = '/' . $request->attributes->get('_system_path');
     }
-    catch (\Exception $e) {
-      return array();
+    else {
+      // getPathInfo() always has leading slash, and might or might not have a
+      // trailing slash.
+      $path = rtrim($request->getPathInfo(), '/');
     }
+    return $this->match($path);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
new file mode 100644
index 0000000..7e3886d
--- /dev/null
+++ b/core/lib/Drupal/Core/Url.php
@@ -0,0 +1,346 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Url.
+ */
+
+namespace Drupal\Core;
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Routing\UrlGeneratorInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines an object that holds information about an internal route.
+ */
+class Url {
+
+  /**
+   * The URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * The route name.
+   *
+   * @var string
+   */
+  protected $routeName;
+
+  /**
+   * The route parameters.
+   *
+   * @var array
+   */
+  protected $routeParameters = array();
+
+  /**
+   * The route options.
+   *
+   * @var array
+   */
+  protected $options = array();
+
+  /**
+   * Indicates whether this URL is external.
+   *
+   * @var bool
+   */
+  protected $external = FALSE;
+
+  /**
+   * The external path.
+   *
+   * Only used if self::$external is TRUE.
+   *
+   * @var string
+   */
+  protected $path;
+
+  /**
+   * Constructs a new Url object.
+   *
+   * @param string $route_name
+   *   The name of the route
+   * @param array $route_parameters
+   *   (optional) An associative array of parameter names and values.
+   * @param array $options
+   *   (optional) An associative array of additional options, with the following
+   *   elements:
+   *   - 'query': An array of query key/value-pairs (without any URL-encoding)
+   *     to append to the URL. Merged with the parameters array.
+   *   - 'fragment': A fragment identifier (named anchor) to append to the URL.
+   *     Do not include the leading '#' character.
+   *   - 'absolute': Defaults to FALSE. Whether to force the output to be an
+   *     absolute link (beginning with http:). Useful for links that will be
+   *     displayed outside the site, such as in an RSS feed.
+   *   - 'language': An optional language object used to look up the alias
+   *     for the URL. If $options['language'] is omitted, the language will be
+   *     obtained from language(Language::TYPE_URL).
+   *   - 'https': Whether this URL should point to a secure location. If not
+   *     defined, the current scheme is used, so the user stays on HTTP or HTTPS
+   *     respectively. if mixed mode sessions are permitted, TRUE enforces HTTPS
+   *     and FALSE enforces HTTP.
+   */
+  public function __construct($route_name, $route_parameters = array(), $options = array()) {
+    $this->routeName = $route_name;
+    $this->routeParameters = $route_parameters;
+    $this->options = $options;
+  }
+
+  /**
+   * Returns the Url object matching a system path.
+   *
+   * @param string $path
+   *   A system path (e.g. 'node/1').
+   *
+   * @return static
+   *   An Url object.
+   *
+   * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException
+   */
+  public static function createFromPath($path) {
+    if (UrlHelper::isExternal($path)) {
+      $url = new static($path);
+      $url->setExternal();
+      return $url;
+    }
+    // Look up the route name and parameters used for the given path.
+    $result = \Drupal::service('router')->match('/' . $path);
+    return new static($result['_route'], $result['_raw_variables']->all());
+  }
+
+  /**
+   * Returns the Url object matching a request
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   *
+   * @return static
+   *   An Url object.
+   */
+  public static function createFromRequest(Request $request) {
+    if ($route_name = $request->attributes->get(RouteObjectInterface::ROUTE_NAME)) {
+      $route_parameters = $request->attributes->get('_raw_variables')->all();
+      return new static($route_name, $route_parameters);
+    }
+    elseif (($path = $request->attributes->get('_system_path')) && UrlHelper::isExternal($path)) {
+      $url = new static($path);
+      $url->setExternal();
+      return $url;
+    }
+    throw new \Exception('asdf');
+  }
+
+  /**
+   * Sets this Url to be external.
+   *
+   * @return $this
+   */
+  protected function setExternal() {
+    $this->external = TRUE;
+    // What was passed in as the route name is actually the path.
+    $this->path = $this->routeName;
+    $this->routeName = NULL;
+    return $this;
+  }
+
+  /**
+   * Indicates if this Url is external.
+   *
+   * @return bool
+   */
+  public function isExternal() {
+    return $this->external;
+  }
+
+  /**
+   * Returns the route name.
+   *
+   * @return string
+   */
+  public function getRouteName() {
+    if ($this->isExternal()) {
+      throw new \Exception('External URLs do not have route names.');
+    }
+    return $this->routeName;
+  }
+
+  /**
+   * Returns the route parameters.
+   *
+   * @return array
+   */
+  public function getRouteParameters() {
+    if ($this->isExternal()) {
+      throw new \Exception('External URLs do not have route parameters.');
+    }
+    return $this->routeParameters;
+  }
+
+  /**
+   * Sets the route parameters.
+   *
+   * @param array $parameters
+   *   The array of parameters.
+   *
+   * @return $this
+   */
+  public function setRouteParameters($parameters) {
+    if ($this->isExternal()) {
+      throw new \Exception('External URLs do not have route parameters.');
+    }
+    $this->routeParameters = $parameters;
+    return $this;
+  }
+
+  /**
+   * Sets a specific route parameter.
+   *
+   * @param string $key
+   *   The key of the route parameter.
+   * @param mixed $value
+   *   The route parameter.
+   *
+   * @return $this
+   */
+  public function setRouteParameter($key, $value) {
+    if ($this->isExternal()) {
+      throw new \Exception('External URLs do not have route parameters.');
+    }
+    $this->routeParameters[$key] = $value;
+    return $this;
+  }
+
+  /**
+   * Returns the URL options.
+   *
+   * @return array
+   */
+  public function getOptions() {
+    return $this->options;
+  }
+
+  /**
+   * Sets the URL options.
+   *
+   * @param array $options
+   *   The array of options.
+   *
+   * @return $this
+   */
+  public function setOptions($options) {
+    $this->options = $options;
+    return $this;
+  }
+
+  /**
+   * Sets a specific option.
+   *
+   * @param string $name
+   *   The name of the option.
+   * @param mixed $value
+   *   The option value.
+   *
+   * @return $this
+   */
+  public function setOption($name, $value) {
+    $this->options[$name] = $value;
+    return $this;
+  }
+
+  /**
+   * Sets the absolute value for this Url.
+   *
+   * @param bool $absolute
+   *   (optional) Whether to make this Url absolute or not. Defaults to TRUE.
+   *
+   * @return $this
+   */
+  public function setAbsolute($absolute = TRUE) {
+    $this->options['absolute'] = $absolute;
+    return $this;
+  }
+
+  /**
+   * Generates the path for this Url object.
+   */
+  public function toString() {
+    if ($this->isExternal()) {
+      return $this->urlGenerator()->generateFromPath($this->path, $this->getOptions());
+    }
+
+    return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions());
+  }
+
+  /**
+   * Returns all the information about the route.
+   *
+   * @return array
+   *   An associative array containing all the properties of the route.
+   */
+  public function toArray() {
+    if ($this->isExternal()) {
+      throw new \Exception('External URLs do not have route metadata.');
+    }
+    return array(
+      'route_name' => $this->getRouteName(),
+      'route_parameters' => $this->getRouteParameters(),
+      'options' => $this->getOptions(),
+    );
+  }
+
+  /**
+   * Returns the internal path for this route.
+   *
+   * This path will not include any prefixes, fragments, or query strings.
+   *
+   * @return string
+   *   The internal path for this route.
+   */
+  public function getInternalPath() {
+    if ($this->isExternal()) {
+      throw new \Exception('External URLs do not have internal representations.');
+    }
+    return $this->urlGenerator()->getPathFromRoute($this->getRouteName(), $this->getRouteParameters());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __sleep() {
+    unset($this->urlGenerator);
+    return array_keys(get_object_vars($this));
+  }
+
+  /**
+   * Gets the URL generator.
+   *
+   * @return \Drupal\Core\Routing\UrlGeneratorInterface
+   *   The URL generator.
+   */
+  protected function urlGenerator() {
+    if (!$this->urlGenerator) {
+      $this->urlGenerator = \Drupal::urlGenerator();
+    }
+    return $this->urlGenerator;
+  }
+
+  /**
+   * Sets the URL generator.
+   *
+   * @param \Drupal\Core\Routing\UrlGeneratorInterface
+   *   The URL generator.
+   *
+   * @return $this
+   */
+  public function setUrlGenerator(UrlGeneratorInterface $url_generator) {
+    $this->urlGenerator = $url_generator;
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index 2874da4..5c06ab8 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -15,7 +15,7 @@
 use Drupal\Core\Path\AliasManagerInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Routing\UrlGeneratorInterface;
-use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Url;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -99,80 +99,88 @@ public function getActive() {
 
   /**
    * {@inheritdoc}
-   *
-   * For anonymous users, the "active" class will be calculated on the server,
-   * because most sites serve each anonymous user the same cached page anyway.
-   * For authenticated users, the "active" class will be calculated on the
-   * client (through JavaScript), only data- attributes are added to links to
-   * prevent breaking the render cache. The JavaScript is added in
-   * system_page_build().
-   *
-   * @see system_page_build()
    */
-  public function generate($text, $route_name, array $parameters = array(), array $options = array()) {
-    // Start building a structured representation of our link to be altered later.
-    $variables = array(
-      // @todo Inject the service when drupal_render() is converted to one.
-      'text' => is_array($text) ? drupal_render($text) : $text,
-      'route_name' => $route_name,
-      'parameters' => $parameters,
-      'options' => $options,
-    );
+  public function generateFromUrl($text, Url $url) {
+    // @todo Inject the service when drupal_render() is converted to one.
+    $text = is_array($text) ? drupal_render($text) : $text;
 
     // Merge in default options.
-    $variables['options'] += array(
+    $options = $url->getOptions();
+    $options += array(
       'attributes' => array(),
       'query' => array(),
       'html' => FALSE,
       'language' => NULL,
       'set_active_class' => FALSE,
+      'absolute' => FALSE,
     );
 
     // Add a hreflang attribute if we know the language of this link's url and
     // hreflang has not already been set.
-    if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) {
-      $variables['options']['attributes']['hreflang'] = $variables['options']['language']->id;
+    if (!empty($options['language']) && !isset($options['attributes']['hreflang'])) {
+      $options['attributes']['hreflang'] = $options['language']->id;
     }
 
     // Set the "active" class if the 'set_active_class' option is not empty.
-    if (!empty($variables['options']['set_active_class'])) {
+    if (!empty($options['set_active_class'])) {
       // Add a "data-drupal-link-query" attribute to let the
       // drupal.active-link library know the query in a standardized manner.
-      if (!empty($variables['options']['query'])) {
-        $query = $variables['options']['query'];
+      if (!empty($options['query'])) {
+        $query = $options['query'];
         ksort($query);
-        $variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query);
+        $options['attributes']['data-drupal-link-query'] = Json::encode($query);
       }
 
       // Add a "data-drupal-link-system-path" attribute to let the
       // drupal.active-link library know the path in a standardized manner.
-      if (!isset($variables['options']['attributes']['data-drupal-link-system-path'])) {
-        $path = $this->urlGenerator->getPathFromRoute($route_name, $parameters);
-        $variables['options']['attributes']['data-drupal-link-system-path'] = $this->aliasManager->getSystemPath($path);
+      if (!isset($options['attributes']['data-drupal-link-system-path'])) {
+        $options['attributes']['data-drupal-link-system-path'] = $this->aliasManager->getSystemPath($url->getInternalPath());
       }
     }
 
     // Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags()
     // only when a quick strpos() gives suspicion tags are present.
-    if (isset($variables['options']['attributes']['title']) && strpos($variables['options']['attributes']['title'], '<') !== FALSE) {
-      $variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']);
+    if (isset($options['attributes']['title']) && strpos($options['attributes']['title'], '<') !== FALSE) {
+      $options['attributes']['title'] = strip_tags($options['attributes']['title']);
     }
 
+    // Update the options before allowing altering.
+    $url->setOptions($options);
+
     // Allow other modules to modify the structure of the link.
-    $this->moduleHandler->alter('link', $variables);
+    $this->moduleHandler->alter('link', $text, $url);
 
     // Move attributes out of options. generateFromRoute(() doesn't need them.
-    $attributes = new Attribute($variables['options']['attributes']);
-    unset($variables['options']['attributes']);
+    $attributes = new Attribute($options['attributes']);
+    unset($options['attributes']);
+    $url->setOptions($options);
 
     // The result of the url generator is a plain-text URL. Because we are using
     // it here in an HTML argument context, we need to encode it properly.
-    $url = String::checkPlain($this->urlGenerator->generateFromRoute($variables['route_name'], $variables['parameters'], $variables['options']));
+    $url = String::checkPlain($url->toString());
 
     // Sanitize the link text if necessary.
-    $text = $variables['options']['html'] ? $variables['text'] : String::checkPlain($variables['text']);
+    $text = $options['html'] ? $text : String::checkPlain($text);
 
     return '<a href="' . $url . '"' . $attributes . '>' . $text . '</a>';
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * For anonymous users, the "active" class will be calculated on the server,
+   * because most sites serve each anonymous user the same cached page anyway.
+   * For authenticated users, the "active" class will be calculated on the
+   * client (through JavaScript), only data- attributes are added to links to
+   * prevent breaking the render cache. The JavaScript is added in
+   * system_page_build().
+   *
+   * @see system_page_build()
+   */
+  public function generate($text, $route_name, array $parameters = array(), array $options = array()) {
+    $url = new Url($route_name, $parameters, $options);
+    $url->setUrlGenerator($this->urlGenerator);
+    return $this->generateFromUrl($text, $url);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
index 02e31af..851100d 100644
--- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
+++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\Core\Utility;
 
+use Drupal\Core\Url;
+
 /**
  * Defines an interface for generating links from route names and parameters.
  */
@@ -78,6 +80,19 @@
   public function generate($text, $route_name, array $parameters = array(), array $options = array());
 
   /**
+   * Renders a link to a URL.
+   *
+   * @param string $text
+   *   The link text for the anchor tag as a translated string.
+   * @param \Drupal\Core\Url $url
+   *   The URL object used for the link.
+   *
+   * @return string
+   *   An HTML string containing a link to the given route and parameters.
+   */
+  public function generateFromUrl($text, Url $url);
+
+  /**
    * Returns information for the currently active route.
    *
    * @return array
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php
index b370063..f26b395 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php
@@ -8,7 +8,7 @@
 namespace Drupal\aggregator\Form;
 
 use Drupal\aggregator\FeedStorageControllerInterface;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Entity\Query\QueryFactory;
 use Drupal\Core\Form\FormBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -158,7 +158,7 @@ public function submitForm(array &$form, array &$form_state) {
     // @todo Move this functionality to a processor.
     foreach ($feeds as $feed) {
       // Ensure URL is valid.
-      if (!Url::isValid($feed['url'], TRUE)) {
+      if (!UrlHelper::isValid($feed['url'], TRUE)) {
         drupal_set_message($this->t('The URL %url is invalid.', array('%url' => $feed['url'])), 'warning');
         continue;
       }
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index 3fd2cb9..77e240f 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -5,7 +5,7 @@
  * Adds contextual links to perform actions related to elements on a page.
  */
 
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Template\Attribute;
 
 /**
@@ -329,8 +329,8 @@ function contextual_contextual_links_view_alter(&$element, $items) {
 function _contextual_links_to_id($contextual_links) {
   $ids = array();
   foreach ($contextual_links as $group => $args) {
-    $route_parameters = Url::buildQuery($args['route_parameters']);
-    $metadata = Url::buildQuery((isset($args['metadata'])) ? $args['metadata'] : array());
+    $route_parameters = UrlHelper::buildQuery($args['route_parameters']);
+    $metadata = UrlHelper::buildQuery((isset($args['metadata'])) ? $args['metadata'] : array());
     $ids[] = "{$group}:{$route_parameters}:{$metadata}";
   }
   return implode('|', $ids);
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php
index c0ed669..d6d2a2d 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\field_ui;
 
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 
 /**
  * Static service container wrapper for Field UI.
@@ -59,7 +59,7 @@ public static function getNextDestination(array $destinations) {
       $next_destination['options']['query']['destinations'] = $destinations;
     }
     else {
-      $options = Url::parse($next_destination);
+      $options = UrlHelper::parse($next_destination);
       if ($destinations) {
         $options['query']['destinations'] = $destinations;
       }
diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
index 57869d4..286f1e8 100644
--- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php
@@ -13,7 +13,7 @@
 use Drupal\image\ImageEffectInterface;
 use Drupal\image\ImageStyleInterface;
 use Drupal\Component\Utility\Crypt;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
 
 /**
@@ -225,7 +225,7 @@ public function buildUrl($path, $clean_urls = NULL) {
     $file_url = file_create_url($uri);
     // Append the query string with the token, if necessary.
     if ($token_query) {
-      $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . Url::buildQuery($token_query);
+      $file_url .= (strpos($file_url, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($token_query);
     }
 
     return $file_url;
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
index ef8db96..ef86eaf 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\link\Plugin\Field\FieldFormatter;
 
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FormatterBase;
@@ -169,7 +169,7 @@ protected function buildLink(FieldItemInterface $item) {
     $settings = $this->getSettings();
 
     // Split out the link into the parts required for url(): path and options.
-    $parsed_url = Url::parse($item->url);
+    $parsed_url = UrlHelper::parse($item->url);
     $result = array(
       'path' => $parsed_url['path'],
       'options' => 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 6d70014..d5d358b 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
@@ -9,7 +9,9 @@
 
 use Drupal\Core\Entity\Entity;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Url;
 use Drupal\menu_link\MenuLinkInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 use Symfony\Component\Routing\Route;
 
 /**
@@ -461,6 +463,10 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
     // This is the easiest way to handle the unique internal path '<front>',
     // since a path marked as external does not need to match a route.
     $this->external = (url_is_external($this->link_path) || $this->link_path == '<front>') ? 1 : 0;
+    if ($this->link_path == '<front>') {
+      $this->route_name = '<front>';
+      $this->route_parameters = array();
+    }
 
     // Try to find a parent link. If found, assign it and derive its menu.
     $parent = $this->findParent($storage_controller);
@@ -505,13 +511,20 @@ public function preSave(EntityStorageControllerInterface $storage_controller) {
 
     // Find the route_name.
     if (!isset($this->route_name)) {
-      if (!$this->external && $result = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($this->link_path)) {
-        list($this->route_name, $this->route_parameters) = $result;
-      }
-      else {
-        $this->route_name = '';
-        $this->route_parameters = array();
+      $route_name = '';
+      $route_parameters = array();
+      if (!$this->external) {
+        try {
+          $url = Url::createFromPath($this->link_path);
+          $route_name = $url->getRouteName();
+          $route_parameters = $url->getRouteParameters();
+        }
+        catch (ResourceNotFoundException $e) {
+        }
       }
+
+      $this->route_name = $route_name;
+      $this->route_parameters = $route_parameters;
     }
     elseif (empty($this->link_path)) {
       $this->link_path = \Drupal::urlGenerator()->getPathFromRoute($this->route_name, $this->route_parameters);
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
index 59f8287..6784bd0 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
@@ -11,7 +11,9 @@
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\Core\Field\FieldDefinition;
 use Drupal\Core\Routing\UrlMatcher;
+use Drupal\Core\Url;
 use Drupal\shortcut\ShortcutInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
 
 /**
  * Defines the shortcut entity class.
@@ -124,9 +126,12 @@ public static function preCreate(EntityStorageControllerInterface $storage_contr
   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]);
+    try {
+      $url = Url::createFromPath($this->path->value);
+      $this->setRouteName($url->getRouteName());
+      $this->setRouteParams($url->getRouteParameters());
+    }
+    catch (ResourceNotFoundException $e) {
     }
   }
 
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index a988cf4..381ec2b 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -5,7 +5,7 @@
  * Allows users to manage customizable lists of shortcut links.
  */
 
-use Drupal\Core\Routing\UrlMatcher;
+use Drupal\Core\Url;
 use Drupal\shortcut\ShortcutSetInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -389,7 +389,7 @@ function shortcut_preprocess_page(&$variables) {
   // pages).
   if (shortcut_set_edit_access() && ($item = menu_get_item()) && $item['access']) {
     $link = current_path();
-    if (!($route_info = \Drupal::service('router.matcher.final_matcher')->findRouteNameParameters($link))) {
+    if (!($url = Url::createFromPath($link))) {
       // Bail out early if we couldn't find a matching route.
       return;
     }
@@ -405,7 +405,7 @@ function shortcut_preprocess_page(&$variables) {
     // Check if $link is already a shortcut and set $link_mode accordingly.
     $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]) {
+      if ($shortcut->getRouteName() == $url->getRouteName() && $shortcut->getRouteParams() == $url->getRouteParameters()) {
         $shortcut_id = $shortcut->id();
         break;
       }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php
index bcafe30..0f47192 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/XssUnitTest.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\system\Tests\Common;
 
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
@@ -54,12 +54,12 @@ function testT() {
    */
   function testBadProtocolStripping() {
     // Ensure that check_url() strips out harmful protocols, and encodes for
-    // HTML. Ensure \Drupal\Component\Utility\Url::stripDangerousProtocols() can
+    // HTML. Ensure \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() can
     // be used to return a plain-text string stripped of harmful protocols.
     $url = 'javascript:http://www.example.com/?x=1&y=2';
     $expected_plain = 'http://www.example.com/?x=1&y=2';
     $expected_html = 'http://www.example.com/?x=1&amp;y=2';
     $this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.');
-    $this->assertIdentical(Url::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\Url::stripDangerousProtocols() filters a URL and returns plain text.');
+    $this->assertIdentical(UrlHelper::stripDangerousProtocols($url), $expected_plain, '\Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() filters a URL and returns plain text.');
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php
index 060303a..3f3a1d8 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/RouterPermissionTest.php
@@ -37,17 +37,10 @@ public function testPermissionAccess() {
     $path = 'router_test/test7';
     $this->drupalGet($path);
     $this->assertResponse(403, "Access denied for a route where we don't have a permission");
-    // An invalid path should throw an exception.
+
     $map = array();
     $route = \Drupal::service('router.route_provider')->getRouteByName('router_test.7');
-    try {
-      menu_item_route_access($route, $path . 'invalid', $map);
-      $exception = FALSE;
-    }
-    catch (ResourceNotFoundException $e) {
-      $exception = TRUE;
-    }
-    $this->assertTrue($exception, 'A ResourceNotFoundException was thrown while checking access for an invalid route.');
+    $this->assertFalse(menu_item_route_access($route, $path . 'invalid', $map));
 
     $this->drupalGet('router_test/test8');
     $this->assertResponse(403, 'Access denied by default if no access specified');
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 6c83611..42159d6 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -5,6 +5,7 @@
  * Hooks provided by Drupal core and the System module.
  */
 
+use Drupal\Core\Url;
 use Drupal\Core\Utility\UpdateException;
 
 /**
@@ -3157,50 +3158,35 @@ function hook_filetransfer_info_alter(&$filetransfer_info) {
 /**
  * Alter the parameters for links.
  *
- * @param array $variables
- *   An associative array of variables defining a link. The link may be either a
- *   "route link" using \Drupal\Core\Utility\LinkGenerator::link(), which is
- *   exposed as the 'link_generator' service or a link generated by l(). If the
- *   link is a "route link", 'route_name' will be set, otherwise 'path' will be
- *   set. The following keys can be altered:
- *   - text: The link text for the anchor tag as a translated string.
- *   - url_is_active: Whether or not the link points to the currently active
- *     URL.
- *   - path: If this link is being generated by l(), this system path, relative
- *     path, or external URL will be passed to url() to generate the href
- *     attribute for this link.
- *   - route_name: The name of the route to use to generate the link, if
- *     this is a "route link".
- *   - parameters: Any parameters needed to render the route path pattern, if
- *     this is a "route link".
- *   - options: An associative array of additional options that will be passed
- *     to either \Drupal\Core\Routing\UrlGenerator::generateFromPath() or
- *     \Drupal\Core\Routing\UrlGenerator::generateFromRoute() to generate the
- *     href attribute for this link, and also used when generating the link.
- *     Defaults to an empty array. It may contain the following elements:
- *     - 'query': An array of query key/value-pairs (without any URL-encoding) to
- *       append to the URL.
- *     - absolute: Whether to force the output to be an absolute link (beginning
- *       with http:). Useful for links that will be displayed outside the site,
- *       such as in an RSS feed. Defaults to FALSE.
- *     - language: An optional language object. May affect the rendering of
- *       the anchor tag, such as by adding a language prefix to the path.
- *     - attributes: An associative array of HTML attributes to apply to the
- *       anchor tag. If element 'class' is included, it must be an array; 'title'
- *       must be a string; other elements are more flexible, as they just need
- *       to work as an argument for the constructor of the class
- *       Drupal\Core\Template\Attribute($options['attributes']).
- *     - html: Whether or not HTML should be allowed as the link text. If FALSE,
- *       the text will be run through
- *       \Drupal\Component\Utility\String::checkPlain() before being output.
+ * @param string $text
+ *   The link text for the anchor tag as a translated string.
+ * @param \Drupal\Core\Url $url
+ *   The URL object used for the link. Url::getOptions() can contain many
+ *   keys, but the following are guaranteed to exist:
+ *   - query: An array of query key/value-pairs (without any URL-encoding) to
+ *     append to the URL.
+ *   - absolute: Whether to force the output to be an absolute link (beginning
+ *     with http:). Useful for links that will be displayed outside the site,
+ *     such as in an RSS feed. Defaults to FALSE.
+ *   - language: An optional language object. May affect the rendering of the
+ *     anchor tag, such as by adding a language prefix to the path.
+ *   - attributes: An associative array of HTML attributes to apply to the
+ *     anchor tag. If element 'class' is included, it must be an array; 'title'
+ *     must be a string; other elements are more flexible, as they just need
+ *     to work as an argument for the constructor of the class
+ *     \Drupal\Core\Template\Attribute($options['attributes']).
+ *   - html: Whether or not HTML should be allowed as the link text. If FALSE,
+ *     the text will be run through
+ *     \Drupal\Component\Utility\String::checkPlain() before being output.
+ *   - set_active_class: Whether to set the 'active' class or not.
  *
  * @see \Drupal\Core\Routing\UrlGenerator::generateFromPath()
  * @see \Drupal\Core\Routing\UrlGenerator::generateFromRoute()
  */
-function hook_link_alter(&$variables) {
+function hook_link_alter(&$text, Url $url) {
   // Add a warning to the end of route links to the admin section.
-  if (isset($variables['route_name']) && strpos($variables['route_name'], 'admin') !== FALSE) {
-    $variables['text'] .= ' (Warning!)';
+  if (strpos($url->getRouteName(), 'admin') !== FALSE) {
+    $text .= ' (Warning!)';
   }
 }
 
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 25f75c7..6ad4f6c 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1761,7 +1761,7 @@ function system_update_8034() {
  * Move filter_allowed_protocols variable to config.
  *
  * This config is provided now by the system module because it is used by
- * \Drupal\Component\Utility\Url::stripDangerousProtocols() and must to be
+ * \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols() and must to be
  * available before the filter module be installed.
  *
  * @ingroup config_upgrade
diff --git a/core/modules/views/lib/Drupal/views/Controller/ViewAjaxController.php b/core/modules/views/lib/Drupal/views/Controller/ViewAjaxController.php
index 307d63f..8df3017 100644
--- a/core/modules/views/lib/Drupal/views/Controller/ViewAjaxController.php
+++ b/core/modules/views/lib/Drupal/views/Controller/ViewAjaxController.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\views\Controller;
 
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
@@ -115,7 +115,7 @@ public function ajaxView(Request $request) {
         // Overwrite the destination.
         // @see drupal_get_destination()
         $origin_destination = $path;
-        $query = Url::buildQuery($request->query->all());
+        $query = UrlHelper::buildQuery($request->query->all());
         if ($query != '') {
           $origin_destination .= '?' . $query;
         }
diff --git a/core/modules/views/lib/Drupal/views/Form/ViewsForm.php b/core/modules/views/lib/Drupal/views/Form/ViewsForm.php
index 3e409f7..cc01f8d 100644
--- a/core/modules/views/lib/Drupal/views/Form/ViewsForm.php
+++ b/core/modules/views/lib/Drupal/views/Form/ViewsForm.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\views\Form;
 
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Controller\ControllerResolverInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\DependencyInjection\DependencySerialization;
@@ -126,7 +126,7 @@ public function buildForm(array $form, array &$form_state, ViewExecutable $view
     $form = array();
 
     $query = $this->request->query->all();
-    $query = Url::filterQueryParameters($query, array(), '');
+    $query = UrlHelper::filterQueryParameters($query, array(), '');
 
     $form['#action'] = $this->urlGenerator->generateFromPath($view->getUrl(), array('query' => $query));
     // Tell the preprocessor whether it should hide the header, footer, pager...
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
index 1b8c4ce..1d3e9c7 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -9,7 +9,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
@@ -230,7 +230,7 @@ public function sanitizeValue($value, $type = NULL) {
         $value = Xss::filterAdmin($value);
         break;
       case 'url':
-        $value = String::checkPlain(Url::stripDangerousProtocols($value));
+        $value = String::checkPlain(UrlHelper::stripDangerousProtocols($value));
         break;
       default:
         $value = String::checkPlain($value);
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php
index 05c11cc..2bf6b69 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewEditFormController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Ajax\HtmlCommand;
 use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Url;
 use Drupal\user\TempStoreFactory;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -271,10 +272,10 @@ public function submit(array $form, array &$form_state) {
         unset($displays[$id]);
 
         // Redirect the user to the renamed display to be sure that the page itself exists and doesn't throw errors.
-        $form_state['redirect_route'] = array(
-          'route_name' => 'views_ui.edit_display',
-          'route_parameters' => array('view' => $view->id(), 'display_id' => $new_id),
-        );
+        $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+          'view' => $view->id(),
+          'display_id' => $new_id,
+        ));
       }
     }
     $view->set('display', $displays);
@@ -321,7 +322,7 @@ public function cancel(array $form, array &$form_state) {
     // Remove this view from cache so edits will be lost.
     $view = $this->entity;
     $this->tempStore->delete($view->id());
-    $form_state['redirect_route']['route_name'] = 'views_ui.list';
+    $form_state['redirect_route'] = new Url('views_ui.list');
   }
 
   /**
@@ -568,10 +569,10 @@ public function submitDisplayUndoDelete($form, &$form_state) {
     $view->cacheSet();
 
     // Redirect to the top-level edit page.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit_display',
-      'route_parameters' => array('view' => $view->id(), 'display_id' => $id),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+      'view' => $view->id(),
+      'display_id' => $id,
+    ));
   }
 
   /**
@@ -587,10 +588,10 @@ public function submitDisplayEnable($form, &$form_state) {
     $view->cacheSet();
 
     // Redirect to the top-level edit page.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit_display',
-      'route_parameters' => array('view' => $view->id(), 'display_id' => $id),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+      'view' => $view->id(),
+      'display_id' => $id,
+    ));
   }
 
   /**
@@ -605,10 +606,10 @@ public function submitDisplayDisable($form, &$form_state) {
     $view->cacheSet();
 
     // Redirect to the top-level edit page.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit_display',
-      'route_parameters' => array('view' => $view->id(), 'display_id' => $id),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+      'view' => $view->id(),
+      'display_id' => $id,
+    ));
   }
 
   /**
@@ -626,10 +627,9 @@ public function submitDisplayDelete($form, &$form_state) {
 
     // Redirect to the top-level edit page. The first remaining display will
     // become the active display.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit',
-      'route_parameters' => array('view' => $view->id()),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit', array(
+      'view' => $view->id(),
+    ));
   }
 
   /**
@@ -802,10 +802,10 @@ public function submitDisplayDuplicate($form, &$form_state) {
     $view->cacheSet();
 
     // Redirect to the new display's edit page.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit_display',
-      'route_parameters' => array('view' => $view->id(), 'display_id' => $new_display_id),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+      'view' => $view->id(),
+      'display_id' => $new_display_id,
+    ));
   }
 
   /**
@@ -824,10 +824,10 @@ public function submitDisplayAdd($form, &$form_state) {
     $view->cacheSet();
 
     // Redirect to the new display's edit page.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit_display',
-      'route_parameters' => array('view' => $view->id(), 'display_id' => $display_id),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+      'view' => $view->id(),
+      'display_id' => $display_id,
+    ));
   }
 
   /**
@@ -860,10 +860,10 @@ public function submitCloneDisplayAsType($form, &$form_state) {
     $view->cacheSet();
 
     // Redirect to the new display's edit page.
-    $form_state['redirect_route'] = array(
-      'route_name' => 'views_ui.edit_display',
-      'route_parameters' => array('view' => $view->id(), 'display_id' => $new_display_id),
-    );
+    $form_state['redirect_route'] = new Url('views_ui.edit_display', array(
+      'view' => $view->id(),
+      'display_id' => $new_display_id,
+    ));
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Component/Utility/UrlTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
similarity index 89%
rename from core/tests/Drupal/Tests/Component/Utility/UrlTest.php
rename to core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
index 4dec865..df970aa 100644
--- a/core/tests/Drupal/Tests/Component/Utility/UrlTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
@@ -2,13 +2,12 @@
 
 /**
  * @file
- * Contains \Drupal\Tests\Component\Utility\UrlTest.
+ * Contains \Drupal\Tests\Component\Utility\UrlHelperTest.
  */
 
 namespace Drupal\Tests\Component\Utility;
 
-
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\String;
 use Drupal\Tests\UnitTestCase;
 
@@ -17,12 +16,12 @@
  *
  * @see \Drupal\Component\Utility\Url
  */
-class UrlTest extends UnitTestCase {
+class UrlHelperTest extends UnitTestCase {
 
   public static function getInfo() {
     return array(
-      'name' => t('Url Tests'),
-      'description' => t('Tests the Url utility class.'),
+      'name' => t('UrlHelper Tests'),
+      'description' => t('Tests the UrlHelper utility class.'),
       'group' => t('Path API'),
     );
   }
@@ -43,7 +42,7 @@ public function providerTestBuildQuery() {
   }
 
   /**
-   * Tests Url::buildQuery().
+   * Tests UrlHelper::buildQuery().
    *
    * @param array $query
    *   The array of query parameters.
@@ -55,7 +54,7 @@ public function providerTestBuildQuery() {
    * @dataProvider providerTestBuildQuery
    */
   public function testBuildQuery($query, $expected, $message) {
-    $this->assertEquals(Url::buildQuery($query), $expected, $message);
+    $this->assertEquals(UrlHelper::buildQuery($query), $expected, $message);
   }
 
   /**
@@ -100,7 +99,7 @@ public function providerTestValidAbsoluteData() {
    */
   public function testValidAbsolute($url, $scheme) {
     $test_url = $scheme . '://' . $url;
-    $valid_url = Url::isValid($test_url, TRUE);
+    $valid_url = UrlHelper::isValid($test_url, TRUE);
     $this->assertTrue($valid_url, String::format('@url is a valid URL.', array('@url' => $test_url)));
   }
 
@@ -130,7 +129,7 @@ public function providerTestInvalidAbsolute() {
    */
   public function testInvalidAbsolute($url, $scheme) {
     $test_url = $scheme . '://' . $url;
-    $valid_url = Url::isValid($test_url, TRUE);
+    $valid_url = UrlHelper::isValid($test_url, TRUE);
     $this->assertFalse($valid_url, String::format('@url is NOT a valid URL.', array('@url' => $test_url)));
   }
 
@@ -163,7 +162,7 @@ public function providerTestValidRelativeData() {
    */
   public function testValidRelative($url, $prefix) {
     $test_url = $prefix . $url;
-    $valid_url = Url::isValid($test_url);
+    $valid_url = UrlHelper::isValid($test_url);
     $this->assertTrue($valid_url, String::format('@url is a valid URL.', array('@url' => $test_url)));
   }
 
@@ -193,7 +192,7 @@ public function providerTestInvalidRelativeData() {
    */
   public function testInvalidRelative($url, $prefix) {
     $test_url = $prefix . $url;
-    $valid_url = Url::isValid($test_url);
+    $valid_url = UrlHelper::isValid($test_url);
     $this->assertFalse($valid_url, String::format('@url is NOT a valid URL.', array('@url' => $test_url)));
   }
 
@@ -210,10 +209,10 @@ public function testInvalidRelative($url, $prefix) {
    *
    * @dataProvider providerTestFilterQueryParameters
    *
-   * @see \Drupal\Component\Utility\Url::filterQueryParameters().
+   * @see \Drupal\Component\Utility\UrlHelper::filterQueryParameters().
    */
   public function testFilterQueryParameters($query, $exclude, $expected) {
-    $filtered = Url::filterQueryParameters($query, $exclude);
+    $filtered = UrlHelper::filterQueryParameters($query, $exclude);
     $this->assertEquals($expected, $filtered, 'The query was not properly filtered.');
   }
 
@@ -249,10 +248,10 @@ public static function providerTestFilterQueryParameters() {
    *
    * @dataProvider providerTestParse
    *
-   * @see \Drupal\Component\Utility\Url::parse()
+   * @see \Drupal\Component\Utility\UrlHelper::parse()
    */
   public function testParse($url, $expected) {
-    $parsed = Url::parse($url);
+    $parsed = UrlHelper::parse($url);
     $this->assertEquals($expected, $parsed, 'The url was not properly parsed.');
   }
 
@@ -302,12 +301,12 @@ public static function providerTestParse() {
    * @param string $expected
    *   The expected encoded path.
    *
-   * @see \Drupal\Component\Utility\Url::encodePath().
+   * @see \Drupal\Component\Utility\UrlHelper::encodePath().
    *
    * @dataProvider providerTestEncodePath
    */
   public function testEncodePath($path, $expected) {
-    $encoded = Url::encodePath($path);
+    $encoded = UrlHelper::encodePath($path);
     $this->assertEquals($expected, $encoded);
   }
 
@@ -331,12 +330,12 @@ public static function providerTestEncodePath() {
    * @param bool $expected
    *   Expected result.
    *
-   * @see \Drupal\Component\Utility\Url::isExternal()
+   * @see \Drupal\Component\Utility\UrlHelper::isExternal()
    *
    * @dataProvider providerTestIsExternal
    */
   public function testIsExternal($path, $expected) {
-    $isExternal = Url::isExternal($path);
+    $isExternal = UrlHelper::isExternal($path);
     $this->assertEquals($expected, $isExternal);
   }
 
@@ -366,8 +365,8 @@ public static function providerTestIsExternal() {
    * @dataProvider providerTestFilterBadProtocol
    */
   public function testFilterBadProtocol($uri, $expected, $protocols) {
-    Url::setAllowedProtocols($protocols);
-    $filtered = Url::filterBadProtocol($uri);
+    UrlHelper::setAllowedProtocols($protocols);
+    $filtered = UrlHelper::filterBadProtocol($uri);
     $this->assertEquals($expected, $filtered);
   }
 
@@ -398,13 +397,13 @@ public static function providerTestFilterBadProtocol() {
    * @param array $protocols
    *    Protocols to allow.
    *
-   * @see \Drupal\Component\Utility\Url::stripDangerousProtocols()
+   * @see \Drupal\Component\Utility\UrlHelper::stripDangerousProtocols()
    *
    * @dataProvider providerTestStripDangerousProtocols
    */
   public function testStripDangerousProtocols($uri, $expected, $protocols) {
-    Url::setAllowedProtocols($protocols);
-    $stripped = Url::stripDangerousProtocols($uri);
+    UrlHelper::setAllowedProtocols($protocols);
+    $stripped = UrlHelper::stripDangerousProtocols($uri);
     $this->assertEquals($expected, $stripped);
   }
 
diff --git a/core/tests/Drupal/Tests/Component/Utility/XssTest.php b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
index 2614c5d..f58e353 100644
--- a/core/tests/Drupal/Tests/Component/Utility/XssTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/XssTest.php
@@ -8,7 +8,7 @@
 namespace Drupal\Tests\Component\Utility;
 
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Url;
+use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Tests\UnitTestCase;
 
@@ -53,7 +53,7 @@ protected function setUp() {
       'webcal',
       'rtsp',
     );
-    Url::setAllowedProtocols($allowed_protocols);
+    UrlHelper::setAllowedProtocols($allowed_protocols);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/DrupalTest.php b/core/tests/Drupal/Tests/Core/DrupalTest.php
index b37010f..2a9e764 100644
--- a/core/tests/Drupal/Tests/Core/DrupalTest.php
+++ b/core/tests/Drupal/Tests/Core/DrupalTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core;
 
+use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
 
 /**
@@ -296,7 +297,30 @@ public function testL() {
       ->will($this->returnValue('link_html_string'));
     $this->setMockContainerService('link_generator', $generator);
 
-    $this->assertInternalType('string', \Drupal::l('Test title', 'test_route', $route_parameters, $options));
+    $link = \Drupal::l('Test title', 'test_route', $route_parameters, $options);
+    $this->assertSame('link_html_string', $link);
+    $this->assertInternalType('string', $link);
+  }
+
+  /**
+   * Tests the l() method.
+   *
+   * @see \Drupal\Core\Utility\LinkGeneratorInterface::generateFromUrl()
+   */
+  public function testLWithUrl() {
+    $route_parameters = array('test_parameter' => 'test');
+    $options = array('test_option' => 'test');
+    $url = new Url('test_route', $route_parameters, $options);
+    $generator = $this->getMock('Drupal\Core\Utility\LinkGeneratorInterface');
+    $generator->expects($this->once())
+      ->method('generateFromUrl')
+      ->with('Test title', $url)
+      ->will($this->returnValue('link_html_string'));
+    $this->setMockContainerService('link_generator', $generator);
+
+    $link = \Drupal::l('Test title', $url);
+    $this->assertSame('link_html_string', $link);
+    $this->assertInternalType('string', $link);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
new file mode 100644
index 0000000..8dd02d3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\ExternalUrlTest.
+ */
+
+namespace Drupal\Tests\Core;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Url;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the \Drupal\Core\Url class for external paths.
+ *
+ * @group Drupal
+ * @group Url
+ *
+ * @coversDefaultClass \Drupal\Core\Url
+ */
+class ExternalUrlTest extends UnitTestCase {
+
+  /**
+   * The URL generator
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $urlGenerator;
+
+  /**
+   * An external URL to test.
+   *
+   * @var string
+   */
+  protected $path = 'http://drupal.org';
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Url object (external)',
+      'description' => 'Tests the \Drupal\Core\Url class with external paths.',
+      'group' => 'Routing',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+    $this->urlGenerator->expects($this->any())
+      ->method('generateFromPath')
+      ->will($this->returnCallback(function ($path) {
+        return $path;
+      }));
+
+    $container = new ContainerBuilder();
+    $container->set('url_generator', $this->urlGenerator);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * Tests the createFromPath method.
+   *
+   * @covers ::createFromPath()
+   * @covers ::setExternal()
+   */
+  public function testCreateFromPath() {
+    $url = Url::createFromPath($this->path);
+    $this->assertInstanceOf('Drupal\Core\Url', $url);
+    $this->assertTrue($url->isExternal());
+    return $url;
+  }
+
+  /**
+   * Tests the createFromRequest method.
+   *
+   * @covers ::createFromRequest()
+   */
+  public function testCreateFromRequest() {
+    $request = new Request(array(), array(), array('_system_path' => $this->path));
+    $url = Url::createFromRequest($request);
+    $this->assertInstanceOf('Drupal\Core\Url', $url);
+    $this->assertTrue($url->isExternal());
+  }
+
+  /**
+   * Tests the createFromRequest method.
+   *
+   * @covers ::createFromRequest()
+   *
+   * @expectedException \Exception
+   */
+  public function testCreateFromRequestWithoutSystemPath() {
+    $request = new Request();
+    $url = \Drupal\Core\Url::createFromRequest($request);
+    $this->assertNull($url);
+  }
+
+  /**
+   * Tests the isExternal() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::isExternal()
+   */
+  public function testIsExternal(\Drupal\Core\Url $url) {
+    $this->assertTrue($url->isExternal());
+  }
+
+  /**
+   * Tests the toString() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::toString()
+   */
+  public function testToString(Url $url) {
+    $this->assertSame($this->path, $url->toString());
+  }
+
+  /**
+   * Tests the toArray() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::toArray()
+   *
+   * @expectedException \Exception
+   */
+  public function testToArray(Url $url) {
+    $this->assertNull($url->toArray());
+  }
+
+  /**
+   * Tests the getRouteName() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getRouteName()
+   *
+   * @expectedException \Exception
+   */
+  public function testGetRouteName(Url $url) {
+    $this->assertNull($url->getRouteName());
+  }
+
+  /**
+   * Tests the getRouteParameters() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getRouteParameters()
+   *
+   * @expectedException \Exception
+   */
+  public function testGetRouteParameters(Url $url) {
+    $this->assertNull($url->getRouteParameters());
+  }
+
+  /**
+   * Tests the getInternalPath() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getInternalPath()
+   *
+   * @expectedException \Exception
+   */
+  public function testGetInternalPath(\Drupal\Core\Url $url) {
+    $this->assertNull($url->getInternalPath());
+  }
+
+  /**
+   * Tests the getOptions() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getOptions()
+   */
+  public function testGetOptions(\Drupal\Core\Url $url) {
+    $this->assertInternalType('array', $url->getOptions());
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index 0b52a75..4537b6e 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -7,8 +7,10 @@
 
 namespace Drupal\Tests\Core\Form {
 
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Form\FormInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 
@@ -32,6 +34,17 @@ public static function getInfo() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $container = new ContainerBuilder();
+    $container->set('url_generator', $this->urlGenerator);
+    \Drupal::setContainer($container);
+  }
+
+  /**
    * Tests the getFormId() method with a string based form ID.
    */
   public function testGetFormIdWithString() {
@@ -225,6 +238,7 @@ public function providerTestRedirectWithRouteWithResult() {
     return array(
       array(array('redirect_route' => array('route_name' => 'test_route_a')), 'test-route'),
       array(array('redirect_route' => array('route_name' => 'test_route_b', 'route_parameters' => array('key' => 'value'))), 'test-route/value'),
+      array(array('redirect_route' => new Url('test_route_b', array('key' => 'value'))), 'test-route/value'),
     );
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Routing/UrlMatcherTest.php b/core/tests/Drupal/Tests/Core/Routing/UrlMatcherTest.php
deleted file mode 100644
index 450ae85..0000000
--- a/core/tests/Drupal/Tests/Core/Routing/UrlMatcherTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\Core\Routing\UrlMatcherTest.
- */
-
-namespace Drupal\Tests\Core\Routing;
-
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Routing\UrlMatcher;
-use Drupal\Tests\UnitTestCase;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
-use Symfony\Component\HttpFoundation\ParameterBag;
-use Symfony\Component\Routing\Exception\ResourceNotFoundException;
-
-/**
- * Tests the menu link entity class.
- *
- * @group Drupal
- * @group Routing
- *
- * @see \Drupal\Core\Routing\UrlMatcher
- */
-class UrlMatcherTest extends UnitTestCase {
-
-  /**
-   * The url generator to test.
-   *
-   * @var \Drupal\Core\Routing\UrlMatcher
-   */
-  protected $matcher;
-
-  public static function getInfo() {
-    return array(
-      '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\Core\Routing\UrlMatcher::findRouteNameParameters()
-   */
-  public function testFindRouteNameParameters() {
-    $router = $this->getMock('Symfony\Component\Routing\Matcher\RequestMatcherInterface');
-    $container = new ContainerBuilder();
-    $container->set('router', $router);
-    \Drupal::setContainer($container);
-
-    $router->expects($this->at(0))
-      ->method('matchRequest')
-      ->will($this->returnValue(array(
-        RouteObjectInterface::ROUTE_NAME => 'view.frontpage.page_1',
-        '_raw_variables' => new ParameterBag(),
-      )));
-    $router->expects($this->at(1))
-      ->method('matchRequest')
-      ->will($this->returnValue(array(
-        RouteObjectInterface::ROUTE_NAME => 'node_view',
-        '_raw_variables' => new ParameterBag(array('node' => '1')),
-      )));
-    $router->expects($this->at(2))
-      ->method('matchRequest')
-      ->will($this->returnValue(array(
-        RouteObjectInterface::ROUTE_NAME => 'node_edit',
-        '_raw_variables' => new ParameterBag(array('node' => '2')),
-      )));
-    $router->expects($this->at(3))
-      ->method('matchRequest')
-      ->will($this->throwException(new ResourceNotFoundException()));
-
-    $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(), $this->matcher->findRouteNameParameters('non-existing'));
-  }
-
-}
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
new file mode 100644
index 0000000..8719fd1
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -0,0 +1,250 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\UrlTest.
+ */
+
+namespace Drupal\Tests\Core;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Url;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
+use Symfony\Component\HttpFoundation\ParameterBag;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+
+/**
+ * Tests the \Drupal\Core\Url class.
+ *
+ * @group Drupal
+ * @group Url
+ *
+ * @coversDefaultClass \Drupal\Core\Url
+ */
+class UrlTest extends UnitTestCase {
+
+  /**
+   * The URL generator
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $urlGenerator;
+
+  /**
+   * The router.
+   *
+   * @var \Symfony\Component\Routing\RouterInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $router;
+
+  /**
+   * An array of values to use for the test.
+   *
+   * @var array
+   */
+  protected $map;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Url object (internal)',
+      'description' => 'Tests the \Drupal\Core\Url class.',
+      'group' => 'Routing',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $map = array();
+    $map[] = array('view.frontpage.page_1', array(), array(), '/node');
+    $map[] = array('node_view', array('node' => '1'), array(), '/node/1');
+    $map[] = array('node_edit', array('node' => '2'), array(), '/node/2/edit');
+    $this->map = $map;
+
+    $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+    $this->urlGenerator->expects($this->any())
+      ->method('generateFromRoute')
+      ->will($this->returnValueMap($this->map));
+
+    $this->router = $this->getMock('Symfony\Component\Routing\RouterInterface');
+    $container = new ContainerBuilder();
+    $container->set('router', $this->router);
+    $container->set('url_generator', $this->urlGenerator);
+    \Drupal::setContainer($container);
+  }
+
+  /**
+   * Tests the createFromPath method.
+   *
+   * @covers ::createFromPath()
+   */
+  public function testCreateFromPath() {
+    $this->router->expects($this->exactly(3))
+      ->method('match')
+      ->will($this->returnValueMap(array(
+        array('/node', array(
+          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')),
+        )),
+      )));
+
+    $urls = array();
+    foreach ($this->map as $index => $values) {
+      $path = trim(array_pop($values), '/');
+      $url = Url::createFromPath($path);
+      $this->assertSame($values, array_values($url->toArray()));
+      $urls[$index] = $url;
+    }
+    return $urls;
+  }
+
+  /**
+   * Tests that an invalid path will thrown an exception.
+   *
+   * @covers ::createFromPath()
+   *
+   * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+   */
+  public function testCreateFromPathInvalid() {
+    $this->router->expects($this->once())
+      ->method('match')
+      ->with('/non-existent')
+      ->will($this->throwException(new ResourceNotFoundException()));
+
+    Url::createFromPath('non-existent');
+  }
+
+  /**
+   * Tests the createFromRequest method.
+   *
+   * @covers ::createFromRequest()
+   */
+  public function testCreateFromRequest() {
+    $request = new Request(array(), array(), array(
+      '_raw_variables' => new ParameterBag(array(
+        'color' => 'chartreuse',
+      )),
+      RouteObjectInterface::ROUTE_NAME => 'the_route_name',
+    ));
+
+    $url = Url::createFromRequest($request);
+    $expected = new Url('the_route_name', array('color' => 'chartreuse'));
+    $this->assertEquals($expected, $url);
+  }
+
+  /**
+   * Tests the isExternal() method.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::isExternal()
+   */
+  public function testIsExternal($urls) {
+    foreach ($urls as $url) {
+      $this->assertFalse($url->isExternal());
+    }
+  }
+
+  /**
+   * Tests the toString() method.
+   *
+   * @param \Drupal\Core\Url[] $urls
+   *   An array of Url objects.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::toString()
+   */
+  public function testToString($urls) {
+    foreach ($urls as $index => $url) {
+      $path = array_pop($this->map[$index]);
+      $this->assertSame($path, $url->toString());
+    }
+  }
+
+  /**
+   * Tests the toArray() method.
+   *
+   * @param \Drupal\Core\Url[] $urls
+   *   An array of Url objects.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::toArray()
+   */
+  public function testToArray($urls) {
+    foreach ($urls as $index => $url) {
+      $expected = array(
+        'route_name' => $this->map[$index][0],
+        'route_parameters' => $this->map[$index][1],
+        'options' => $this->map[$index][2],
+      );
+      $this->assertSame($expected, $url->toArray());
+    }
+  }
+
+  /**
+   * Tests the getRouteName() method.
+   *
+   * @param \Drupal\Core\Url[] $urls
+   *   An array of Url objects.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getRouteName()
+   */
+  public function testGetRouteName($urls) {
+    foreach ($urls as $index => $url) {
+      $this->assertSame($this->map[$index][0], $url->getRouteName());
+    }
+  }
+
+  /**
+   * Tests the getRouteParameters() method.
+   *
+   * @param \Drupal\Core\Url[] $urls
+   *   An array of Url objects.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getRouteParameters()
+   */
+  public function testGetRouteParameters($urls) {
+    foreach ($urls as $index => $url) {
+      $this->assertSame($this->map[$index][1], $url->getRouteParameters());
+    }
+  }
+
+  /**
+   * Tests the getOptions() method.
+   *
+   * @param \Drupal\Core\Url[] $urls
+   *   An array of Url objects.
+   *
+   * @depends testCreateFromPath
+   *
+   * @covers ::getOptions()
+   */
+  public function testGetOptions($urls) {
+    foreach ($urls as $index => $url) {
+      $this->assertSame($this->map[$index][2], $url->getOptions());
+    }
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 80d9d58..23dd3ee 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -64,6 +64,7 @@ class LinkGeneratorTest extends UnitTestCase {
     'html' => FALSE,
     'language' => NULL,
     'set_active_class' => FALSE,
+    'absolute' => FALSE,
   );
 
   /**
