diff --git a/core/core.services.yml b/core/core.services.yml
index 3c27130..5355141 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -491,7 +491,7 @@ services:
       - [setContext, ['@?router.request_context']]
   unrouted_url_assembler:
     class: Drupal\Core\Utility\UnroutedUrlAssembler
-    arguments: ['@request_stack', '@config.factory' ]
+    arguments: ['@path_processor_manager', '@request_stack', '@config.factory']
   link_generator:
     class: Drupal\Core\Utility\LinkGenerator
     arguments: ['@url_generator', '@module_handler']
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index be5171e..f8766f1 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -8,6 +8,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Url;
 
 /**
  * @defgroup menu Menu and routing system
@@ -347,15 +348,8 @@ function template_preprocess_menu_local_task(&$variables) {
     '#options' => $link['localized_options'],
   );
 
-  if (!empty($link['href'])) {
-    // @todo - Remove this once all pages are converted to routes.
-    $variables['link']['#href'] = $link['href'];
-  }
-  else {
-    $variables['link'] += array(
-      '#route_name' => $link['route_name'],
-      '#route_parameters' => $link['route_parameters'],
-    );
+  if (!isset($variables['link']['#url'])) {
+    $variables['link']['#url'] = $link['url'];
   }
 }
 
@@ -388,15 +382,10 @@ function template_preprocess_menu_local_action(&$variables) {
   );
 
   // @todo Figure out how to support local actions without a href properly.
-  if ($link['href'] === '' && !empty($link['route_name'])) {
-    $variables['link'] += array(
-      '#route_name' => $link['route_name'],
-      '#route_parameters' => $link['route_parameters'],
-    );
-  }
-  else {
-    // @todo - Remove this once all pages are converted to routes.
-    $variables['link']['#href'] = $link['href'];
+  if ($link['href'] === '' && !empty($link['url'])) {
+    if (!isset($variables['link']['#url'])) {
+      $variables['link']['#url'] = $link['url'];
+    }
   }
 }
 
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index d10ce95..923e5c8 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -24,6 +24,7 @@
 use Drupal\Core\Theme\ThemeSettings;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -893,14 +894,8 @@ function template_preprocess_status_messages(&$variables) {
  *     is used as its CSS class. Each link should be itself an array, with the
  *     following elements:
  *     - title: The link text.
- *     - route_name: (optional) The name of the route to link to. If omitted
- *       (and if 'href' is omitted as well), the 'title' is shown as
- *       a plain text item in the links list.
- *     - route_parameters: (optional) An array of route parameters for the link.
- *     - href: (optional) The link URL. It is preferred to use 'route_name' and
- *       'route parameters' for internal links. Use 'href' for links to external
- *       URLs. If omitted (and if 'route_name' is omitted as well), the 'title'
- *       is shown as a plain text item in the links list.
+ *     - url: (optional) The url object to link to. If omitted, no a tag is
+ *       printed out.
  *     - html: (optional) Whether or not 'title' is HTML. If set, the title
  *       will not be passed through
  *       \Drupal\Component\Utility\String::checkPlain().
@@ -977,30 +972,29 @@ function template_preprocess_links(&$variables) {
     foreach ($links as $key => $link) {
       $item = array();
       $link += array(
-        'href' => NULL,
-        'route_name' => NULL,
-        'route_parameters' => NULL,
         'ajax' => NULL,
+        'url' => NULL,
       );
+      if (!empty($link['route_name'])) {
+        throw new \Exception('route_name: ' . $link['route_name'] . "\n" . \Drupal\Core\Utility\Error::formatBacktrace(debug_backtrace()));
+      }
 
       $li_attributes = array('class' => array());
       // Use the array key as class name.
       $li_attributes['class'][] = drupal_html_class($key);
 
-      $keys = array('title', 'href', 'route_name', 'route_parameters');
+      $keys = array('title', 'url');
       $link_element = array(
         '#type' => 'link',
         '#title' => $link['title'],
         '#options' => array_diff_key($link, array_combine($keys, $keys)),
-        '#href' => $link['href'],
-        '#route_name' => $link['route_name'],
-        '#route_parameters' => $link['route_parameters'],
+        '#url' => $link['url'],
         '#ajax' => $link['ajax'],
       );
 
       // Handle links and ensure that the active class is added on the LIs, but
       // only if the 'set_active_class' option is not empty.
-      if (isset($link['href']) || isset($link['route_name'])) {
+      if (isset($link['url'])) {
         if (!empty($variables['set_active_class'])) {
 
           // Also enable set_active_class for the contained link.
@@ -1018,16 +1012,15 @@ function template_preprocess_links(&$variables) {
             $li_attributes['data-drupal-link-query'] = Json::encode($query);
           }
 
-          if (isset($link['route_name'])) {
-            $path = \Drupal::service('url_generator')->getPathFromRoute($link['route_name'], $link['route_parameters']);
-          }
-          else {
-            $path = $link['href'];
-          }
+          /** @var \Drupal\Core\Url $url */
+          $url = $link['url'];
 
           // Add a "data-drupal-link-system-path" attribute to let the
           // drupal.active-link library know the path in a standardized manner.
-          $li_attributes['data-drupal-link-system-path'] = \Drupal::service('path.alias_manager')->getPathByAlias($path);
+          $system_path = $url->getInternalPath();
+          // @todo System path is deprecated - use the route name and parameters.
+          // Special case for the front page.
+          $li_attributes['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path;
         }
 
         $item['link'] = $link_element;
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 407b9d7..09d579d 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -222,7 +222,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
           'class' => array('button', 'button--danger'),
         ),
       );
-      $actions['delete'] += $route_info->toRenderArray();
+      $actions['delete']['#url'] = $route_info;
     }
 
     return $actions;
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
index 58f6c6b..19311b5 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'email_mailto' formatter.
@@ -33,7 +34,7 @@ public function viewElements(FieldItemListInterface $items) {
       $elements[$delta] = array(
         '#type' => 'link',
         '#title' => $item->value,
-        '#href' => 'mailto:' . $item->value,
+        '#url' => Url::fromUri('mailto:' . $item->value),
       );
     }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
index 762d5f3..d9d26b1 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'uri_link' formatter.
@@ -30,11 +31,13 @@ public function viewElements(FieldItemListInterface $items) {
     $elements = array();
 
     foreach ($items as $delta => $item) {
-      $elements[$delta] = array(
-        '#type' => 'link',
-        '#href' => $item->value,
-        '#title' => $item->value,
-      );
+      if (!$item->isEmpty()) {
+        $elements[$delta] = array(
+          '#type' => 'link',
+          '#url' => Url::fromUri($item->value),
+          '#title' => $item->value,
+        );
+      }
     }
 
     return $elements;
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index a823cac..a50696b 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -61,4 +61,15 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $value = $this->getValue();
+    if (!isset($value['value']) || $value['value'] === '') {
+      return TRUE;
+    }
+    return parent::isEmpty();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
index de98053..afa779a 100644
--- a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
+++ b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
@@ -35,8 +35,7 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ
     if ($query->has('destination')) {
       $options = UrlHelper::parse($query->get('destination'));
       $link = array(
-        '#href' => $options['path'],
-        '#options' => $options,
+        '#url' => Url::fromUri('base://' . $options['path'], $options),
       );
     }
     // Check for a route-based cancel link.
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index 605a5ab..d34799c 100644
--- a/core/lib/Drupal/Core/Language/LanguageManager.php
+++ b/core/lib/Drupal/Core/Language/LanguageManager.php
@@ -11,6 +11,7 @@
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\StringTranslation\TranslationWrapper;
+use Drupal\Core\Url;
 
 /**
  * Class responsible for providing language support on language-unaware sites.
@@ -239,7 +240,7 @@ public function getFallbackCandidates(array $context = array()) {
   /**
    * {@inheritdoc}
    */
-  public function getLanguageSwitchLinks($type, $path) {
+  public function getLanguageSwitchLinks($type, Url $url) {
     return array();
   }
 
diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
index e163c0c..783496c 100644
--- a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
+++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Language;
 
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
 
 /**
  * Common interface for the language manager service.
@@ -181,13 +182,13 @@ public function getFallbackCandidates(array $context = array());
    *
    * @param string $type
    *   The language type.
-   * @param string $path
-   *   The internal path the switch links will be relative to.
+   * @param \Drupal\Core\Url $url
+   *   The URL the switch links will be relative to.
    *
    * @return array
    *   A keyed array of links ready to be themed.
    */
-  public function getLanguageSwitchLinks($type, $path);
+  public function getLanguageSwitchLinks($type, Url $url);
 
   /**
    * Sets the configuration override language.
diff --git a/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
index f6ee14b..bf86365 100644
--- a/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
+++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
@@ -113,7 +113,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
     $link = array(
       '#type' => 'link',
       '#title' => $this->menuLink->getTitle(),
-    ) + $this->menuLink->getUrlObject()->toRenderArray();
+      '#url' => $this->menuLink->getUrlObject(),
+    );
     $form['path'] = array(
       'link' => $link,
       '#type' => 'item',
diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php
index 474e9f5..238b753 100644
--- a/core/lib/Drupal/Core/Render/Element/Link.php
+++ b/core/lib/Drupal/Core/Render/Element/Link.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Render\Element;
 
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Url as UrlObject;
 
 /**
@@ -36,11 +37,7 @@ public function getInfo() {
    * @param array $element
    *   A structured array whose keys form the arguments to _l():
    *   - #title: The link text to pass as argument to _l().
-   *   - One of the following
-   *     - #route_name and (optionally) a #route_parameters array; The route
-   *       name and route parameters which will be passed into the link
-   *       generator.
-   *     - #href: The system path or URL to pass as argument to _l().
+   *   - #url: The URL info either pointing to a route or a non routed path.
    *   - #options: (optional) An array of options to pass to _l() or the link
    *     generator.
    *
@@ -75,21 +72,15 @@ public static function preRenderLink($element) {
       if (!isset($element['#id'])) {
         $element['#id'] = $element['#options']['attributes']['id'] = drupal_html_id('ajax-link');
       }
-      // If #ajax['path] was not specified, use the href as Ajax request URL.
-      if (!isset($element['#ajax']['path'])) {
-        $element['#ajax']['path'] = $element['#href'];
-        $element['#ajax']['options'] = $element['#options'];
-      }
       $element = static::preRenderAjaxForm($element);
     }
 
-    if (isset($element['#route_name'])) {
-      $element['#route_parameters'] = empty($element['#route_parameters']) ? array() : $element['#route_parameters'];
-      $element['#markup'] = \Drupal::l($element['#title'], new UrlObject($element['#route_name'], $element['#route_parameters'], $element['#options']));
+    if (!empty($element['#url'])) {
+      $options = NestedArray::mergeDeep($element['#url']->getOptions(), $element['#options']);
+      $element['#markup'] = \Drupal::l($element['#title'], $element['#url']->setOptions($options));
     }
-    else {
-      // @todo Convert to \Drupal::l(): https://www.drupal.org/node/2347045.
-      $element['#markup'] = _l($element['#title'], $element['#href'], $element['#options']);
+    if (!empty($element['#route_name'])) {
+      throw new \Exception('#route_name: ' . $element['#route_name'] . "\n" . \Drupal\Core\Utility\Error::formatBacktrace(debug_backtrace()));
     }
     return $element;
   }
diff --git a/core/lib/Drupal/Core/Render/Element/Table.php b/core/lib/Drupal/Core/Render/Element/Table.php
index d82fda8..08c539a 100644
--- a/core/lib/Drupal/Core/Render/Element/Table.php
+++ b/core/lib/Drupal/Core/Render/Element/Table.php
@@ -281,7 +281,7 @@ public static function validateTable(&$element, FormStateInterface $form_state,
    *   $form['table'][$row]['edit'] = array(
    *     '#type' => 'link',
    *     '#title' => t('Edit'),
-   *     '#href' => 'thing/' . $row . '/edit',
+   *     '#url' => Url::fromRoute('entity.test_entity.edit_form', ['test_entity' => $row]),
    *   );
    * }
    * @endcode
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index a6f0220..8361be7 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -203,7 +204,7 @@ public static function fromRoute($route_name, $route_parameters = array(), $opti
    */
   public static function fromUri($uri, $options = array()) {
     if (!parse_url($uri, PHP_URL_SCHEME)) {
-      throw new \InvalidArgumentException('You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.');
+      throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
     }
 
     $url = new static($uri, array(), $options);
@@ -480,20 +481,14 @@ public function toArray() {
    *   An associative array suitable for a render array.
    */
   public function toRenderArray() {
-    if ($this->unrouted) {
-      return array(
-        '#href' => $this->getUri(),
-        '#options' => $this->getOptions(),
-      );
-    }
-    else {
-      return array(
-        '#route_name' => $this->getRouteName(),
-        '#route_parameters' => $this->getRouteParameters(),
-        '#options' => $this->getOptions(),
-        '#access_callback' => array(get_class(), 'renderAccess'),
-      );
+    $render_array = [
+      '#url' => $this,
+      '#options' => $this->getOptions(),
+    ];
+    if (!$this->unrouted) {
+      $render_array['#access_callback'] = [get_class(), 'renderAccess'];
     }
+    return $render_array;
   }
 
   /**
@@ -543,7 +538,7 @@ public function access(AccountInterface $account = NULL) {
    *   Returns TRUE if the current user has access to the url, otherwise FALSE.
    */
   public static function renderAccess(array $element) {
-    return (new static($element['#route_name'], $element['#route_parameters'], $element['#options']))->access();
+    return $element['#url']->access();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
index 52d1d13..dd267d9 100644
--- a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
+++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
@@ -7,8 +7,10 @@
 
 namespace Drupal\Core\Utility;
 
+use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
@@ -19,6 +21,13 @@
 class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
 
   /**
+   * The path processor to convert the system path to one suitable for urls.
+   *
+   * @var \Drupal\Core\PathProcessor\OutboundPathProcessorInterface
+   */
+  protected $pathProcessor;
+
+  /**
    * A request stack object.
    *
    * @var \Symfony\Component\HttpFoundation\RequestStack
@@ -28,14 +37,17 @@ class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
   /**
    *  Constructs a new unroutedUrlAssembler object.
    *
+   * @param \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $path_processor
+   *   The path processor to convert the system path to one suitable for urls.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config
    *    The config factory.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   A request stack object.
    */
-  public function __construct(RequestStack $request_stack, ConfigFactoryInterface $config) {
+  public function __construct(OutboundPathProcessorInterface $path_processor, RequestStack $request_stack, ConfigFactoryInterface $config) {
     $allowed_protocols = $config->get('system.filter')->get('protocols') ?: ['http', 'https'];
     UrlHelper::setAllowedProtocols($allowed_protocols);
+    $this->pathProcessor = $path_processor;
     $this->requestStack = $request_stack;
   }
 
@@ -56,7 +68,7 @@ public function assemble($uri, array $options = []) {
       // UrlHelper::isExternal() only returns true for safe protocols.
       return $this->buildExternalUrl($uri, $options);
     }
-    throw new \InvalidArgumentException('You must use a valid URI scheme. Use base:// for a path e.g. to a Drupal file that needs the base path.');
+    throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
   }
 
   /**
@@ -98,6 +110,7 @@ protected function buildLocalUrl($uri, array $options = []) {
 
     // Remove the base:// scheme.
     $uri = substr($uri, 7);
+    $uri = $this->pathProcessor->processOutbound(trim($uri, '/'), $options, $this->requestStack->getCurrentRequest());
     // Add any subdirectory where Drupal is installed.
     $current_base_path = $request->getBasePath() . '/';
 
diff --git a/core/modules/aggregator/src/Controller/AggregatorController.php b/core/modules/aggregator/src/Controller/AggregatorController.php
index a37a81d..a7e25ff 100644
--- a/core/modules/aggregator/src/Controller/AggregatorController.php
+++ b/core/modules/aggregator/src/Controller/AggregatorController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Datetime\DateFormatter;
 use Drupal\aggregator\FeedInterface;
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -128,25 +129,21 @@ public function adminOverview() {
       $refresh_rate = $feed->getRefreshRate();
       $row[] = ($last_checked ? $this->t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $last_checked))) : $this->t('never'));
       $row[] = ($last_checked && $refresh_rate ? $this->t('%time left', array('%time' => $this->dateFormatter->formatInterval($last_checked + $refresh_rate - REQUEST_TIME))) : $this->t('never'));
-      $links['edit'] = array(
+      $links['edit'] = [
         'title' => $this->t('Edit'),
-        'route_name' => 'entity.aggregator_feed.edit_form',
-        'route_parameters' => array('aggregator_feed' => $feed->id()),
-      );
+        'url' => Url::fromRoute('entity.aggregator_feed.edit_form', ['aggregator_feed' => $feed->id()]),
+      ];
       $links['delete'] = array(
         'title' => $this->t('Delete'),
-        'route_name' => 'entity.aggregator_feed.delete_form',
-        'route_parameters' => array('aggregator_feed' => $feed->id()),
+        'url' => Url::fromRoute('entity.aggregator_feed.delete_form', ['aggregator_feed' => $feed->id()]),
       );
       $links['delete_items'] = array(
         'title' => $this->t('Delete items'),
-        'route_name' => 'aggregator.feed_items_delete',
-        'route_parameters' => array('aggregator_feed' => $feed->id()),
+        'url' => Url::fromRoute('aggregator.feed_items_delete', ['aggregator_feed' => $feed->id()]),
       );
       $links['update'] = array(
         'title' => $this->t('Update items'),
-        'route_name' => 'aggregator.feed_refresh',
-        'route_parameters' => array('aggregator_feed' => $feed->id()),
+        'url' => Url::fromRoute('aggregator.feed_refresh', ['aggregator_feed' => $feed->id()])
       );
       $row[] = array(
         'data' => array(
diff --git a/core/modules/aggregator/src/FeedViewBuilder.php b/core/modules/aggregator/src/FeedViewBuilder.php
index 26523ab..944d9a8 100644
--- a/core/modules/aggregator/src/FeedViewBuilder.php
+++ b/core/modules/aggregator/src/FeedViewBuilder.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityViewBuilder;
 use Drupal\Core\Config\Config;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -99,7 +100,7 @@ public function buildComponents(array &$build, array $entities, array $displays,
           $image_link = array(
             '#type' => 'link',
             '#title' => $link_title,
-            '#href' => $link_href,
+            '#url' => Url::fromUri($link_href),
             '#options' => array(
               'attributes' => array('class' => array('feed-image')),
               'html' => TRUE,
@@ -124,8 +125,7 @@ public function buildComponents(array &$build, array $entities, array $displays,
           '#title' => t('More<span class="visually-hidden"> posts about @title</span>', array(
             '@title' => $title_stripped,
           )),
-          '#route_name' => 'entity.aggregator_feed.canonical',
-          '#route_parameters' => array('aggregator_feed' => $entity->id()),
+          '#url' => Url::fromRoute('entity.aggregator_feed.canonical', ['aggregator_feed' => $entity->id()]),
           '#options' => array(
             'html' => TRUE,
             'attributes' => array(
diff --git a/core/modules/aggregator/src/ItemsImporter.php b/core/modules/aggregator/src/ItemsImporter.php
index af19325..3098f08 100644
--- a/core/modules/aggregator/src/ItemsImporter.php
+++ b/core/modules/aggregator/src/ItemsImporter.php
@@ -120,7 +120,7 @@ public function refresh(FeedInterface $feed) {
       // Parse the feed.
       try {
         if ($this->parserManager->createInstance($this->config->get('parser'))->parse($feed)) {
-          if ($feed->getWebsiteUrl()) {
+          if (!$feed->getWebsiteUrl()) {
             $feed->setWebsiteUrl($feed->getUrl());
           }
           $feed->setHash($hash);
diff --git a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
index f28ff0d..2710ced 100644
--- a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
+++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
@@ -164,7 +164,7 @@ public function build() {
 
       $more_link = array(
         '#type' => 'more_link',
-        '#href' => 'aggregator/sources/' . $feed->id(),
+        '#url' => $feed->urlInfo(),
         '#attributes' => array('title' => $this->t("View this feed's recent news.")),
       );
       $read_more = drupal_render($more_link);
diff --git a/core/modules/aggregator/src/Tests/AddFeedTest.php b/core/modules/aggregator/src/Tests/AddFeedTest.php
index 5b15d02..bbeae85 100644
--- a/core/modules/aggregator/src/Tests/AddFeedTest.php
+++ b/core/modules/aggregator/src/Tests/AddFeedTest.php
@@ -18,6 +18,7 @@ class AddFeedTest extends AggregatorTestBase {
    */
   function testAddFeed() {
     $feed = $this->createFeed();
+    $feed->refreshItems();
 
     // Check feed data.
     $this->assertUrl(\Drupal::url('aggregator.feed_add', [], ['absolute' => TRUE]), [], 'Directed to correct url.');
@@ -27,7 +28,7 @@ function testAddFeed() {
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, 'Feed source exists.');
     $this->assertText($feed->label(), 'Page title');
-    $this->assertText($feed->label());
+    $this->assertRaw($feed->getWebsiteUrl());
 
     // Delete feed.
     $this->deleteFeed($feed);
@@ -40,11 +41,13 @@ function testAddLongFeed() {
     // Create a feed with a URL of > 255 characters.
     $long_url = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889&ix=heb";
     $feed = $this->createFeed($long_url);
+    $feed->refreshItems();
 
     // Create a second feed of > 255 characters, where the only difference is
     // after the 255th character.
     $long_url_2 = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889";
     $feed_2 = $this->createFeed($long_url_2);
+    $feed->refreshItems();
 
     // Check feed data.
     $this->assertTrue($this->uniqueFeed($feed->label(), $feed->getUrl()), 'The first long URL feed is unique.');
@@ -54,7 +57,6 @@ function testAddLongFeed() {
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, 'Long URL feed source exists.');
     $this->assertText($feed->label(), 'Page title');
-    $this->assertText($feed->label());
 
     // Delete feeds.
     $this->deleteFeed($feed);
diff --git a/core/modules/ban/src/Form/BanAdmin.php b/core/modules/ban/src/Form/BanAdmin.php
index 94c1d4f..1690b91 100644
--- a/core/modules/ban/src/Form/BanAdmin.php
+++ b/core/modules/ban/src/Form/BanAdmin.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Form\FormBase;
 use Drupal\ban\BanIpManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -65,8 +66,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $default_
       $links = array();
       $links['delete'] = array(
         'title' => $this->t('Delete'),
-        'route_name' => 'ban.delete',
-        'route_parameters' => array('ban_id' => $ip->iid),
+        'url' => Url::fromRoute('ban.delete', ['ban_id' => $ip->iid]),
       );
       $row[] = array(
         'data' => array(
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index cbf56ab..693e90c 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -93,10 +93,15 @@ function block_page_build(&$page) {
     $page['page_top']['backlink'] = array(
       '#type' => 'link',
       '#title' => t('Exit block region demonstration'),
-      '#href' => 'admin/structure/block' . (\Drupal::config('system.theme')->get('default') == $theme ? '' : '/list/' . $theme),
       '#options' => array('attributes' => array('class' => array('block-demo-backlink'))),
       '#weight' => -10,
     );
+    if (\Drupal::config('system.theme')->get('default') == $theme) {
+      $page['page_top']['backlink']['#url'] = Url::fromRoute('block.admin_display_theme', ['theme' => $theme]);
+    }
+    else {
+      $page['page_top']['backlink']['#url'] = Url::fromRoute('block.admin_display');
+    }
   }
 }
 
diff --git a/core/modules/block/src/BlockListBuilder.php b/core/modules/block/src/BlockListBuilder.php
index e5da65d..1d11f99 100644
--- a/core/modules/block/src/BlockListBuilder.php
+++ b/core/modules/block/src/BlockListBuilder.php
@@ -16,6 +16,7 @@
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -356,11 +357,10 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       }
       $form['place_blocks']['list'][$category_key]['content']['#links'][$plugin_id] = array(
         'title' => $plugin_definition['admin_label'],
-        'route_name' => 'block.admin_add',
-        'route_parameters' => array(
+        'url' => Url::fromRoute('block.admin_add', [
           'plugin_id' => $plugin_id,
           'theme' => $this->theme
-        ),
+        ]),
         'attributes' => array(
           'class' => array('use-ajax', 'block-filter-text-source'),
           'data-accepts' => 'application/vnd.drupal-modal',
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index 469267c..ced19d9 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -8,6 +8,7 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\book\BookManager;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 
 /**
  * Returns HTML for a book administration form.
@@ -45,19 +46,17 @@ function theme_book_admin_table($variables) {
     $links = array();
     $links['view'] = array(
       'title' => t('View'),
-      'href' => $href,
+      'url' => Url::fromRoute('entity.node.canonical', ['node' => $nid]),
     );
     if ($access) {
       $links['edit'] = array(
         'title' => t('Edit'),
-        'route_name' => 'entity.node.edit_form',
-        'route_parameters' => array('node' => $nid),
+        'url' => Url::fromRoute('entity.node.edit_form', ['node' => $nid]),
         'query' => $destination,
       );
       $links['delete'] = array(
         'title' => t('Delete'),
-        'route_name' => 'entity.node.delete_form',
-        'route_parameters' => array('node' => $nid),
+        'url' => Url::fromRoute('entity.node.delete_form', ['node' => $nid]),
         'query' => $destination,
       );
     }
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 6b631c7..361fa1c 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -12,6 +12,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
 use Drupal\node\NodeInterface;
 use Drupal\node\NodeTypeInterface;
 use Drupal\node\Entity\Node;
@@ -108,15 +109,17 @@ function book_node_links_alter(array &$node_links, NodeInterface $node, array &$
         if (($account->hasPermission('add content to books') || $account->hasPermission('administer book outlines')) && $access_control_handler->createAccess($child_type) && $node->isPublished() && $node->book['depth'] < BookManager::BOOK_MAX_DEPTH) {
           $links['book_add_child'] = array(
             'title' => t('Add child page'),
-            'href' => 'node/add/' . $child_type,
-            'query' => array('parent' => $node->id()),
+            'url' => Url::fromRoute('node.add', ['node_type' => $child_type], ['parent' => $node->id()]),
           );
         }
 
         if ($account->hasPermission('access printer-friendly version')) {
           $links['book_printer'] = array(
             'title' => t('Printer-friendly version'),
-            'href' => 'book/export/html/' . $node->id(),
+            'url' => Url::fromRoute('book.export', [
+              'type' => 'html',
+              'node' => $node->id(),
+            ]),
             'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))
           );
         }
diff --git a/core/modules/book/src/Controller/BookController.php b/core/modules/book/src/Controller/BookController.php
index 3079032..2f89937 100644
--- a/core/modules/book/src/Controller/BookController.php
+++ b/core/modules/book/src/Controller/BookController.php
@@ -10,6 +10,7 @@
 use Drupal\book\BookExport;
 use Drupal\book\BookManagerInterface;
 use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Url;
 use Drupal\node\Entity\Node;
 use Drupal\node\NodeInterface;
 use Symfony\Component\DependencyInjection\Container;
@@ -83,8 +84,7 @@ public function adminOverview() {
       $links = array();
       $links['edit'] = array(
         'title' => t('Edit order and titles'),
-        'route_name' => 'book.admin_edit',
-        'route_parameters' => array('node' => $book['nid']),
+        'url' => Url::fromRoute('book.admin_edit', ['node' => $book['nid']]),
       );
       $row[] = array(
         'data' => array(
diff --git a/core/modules/comment/comment.api.php b/core/modules/comment/comment.api.php
index 5430ee9..139f3e4 100644
--- a/core/modules/comment/comment.api.php
+++ b/core/modules/comment/comment.api.php
@@ -2,6 +2,7 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\comment\CommentInterface;
+use Drupal\Core\Url;
 
 /**
  * @file
@@ -37,9 +38,8 @@ function hook_comment_links_alter(array &$links, CommentInterface $entity, array
     '#links' => array(
       'comment-report' => array(
         'title' => t('Report'),
-        'href' => "comment/{$entity->id()}/report",
+        'url' => Url::fromRoute('comment_test.report', ['comment' => $entity->id()], ['query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("comment/{$entity->id()}/report"))]),
         'html' => TRUE,
-        'query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("comment/{$entity->id()}/report")),
       ),
     ),
   );
diff --git a/core/modules/comment/src/CommentLinkBuilder.php b/core/modules/comment/src/CommentLinkBuilder.php
index 9726712..406aac4 100644
--- a/core/modules/comment/src/CommentLinkBuilder.php
+++ b/core/modules/comment/src/CommentLinkBuilder.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
 
 /**
  * Defines a class for building markup for comment links on a commented entity.
@@ -114,7 +115,7 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra
               if ($this->moduleHandler->moduleExists('history')) {
                 $links['comment-new-comments'] = array(
                   'title' => '',
-                  'href' => '',
+                  'url' => Url::fromRoute('<current>'),
                   'attributes' => array(
                     'class' => 'hidden',
                     'title' => $this->t('Jump to the first new comment of this posting.'),
@@ -136,15 +137,14 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra
                 'fragment' => 'comment-form',
               );
               if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) {
-                $links['comment-add']['route_name'] = 'comment.reply';
-                $links['comment-add']['route_parameters'] = array(
+                $links['comment-add']['url'] = Url::fromRoute('comment.reply', [
                   'entity_type' => $entity->getEntityTypeId(),
                   'entity' => $entity->id(),
                   'field_name' => $field_name,
-                );
+                ]);
               }
               else {
-                $links['comment-add'] += $entity->urlInfo()->toArray();
+                $links['comment-add'] += ['url' => $entity->urlInfo()];
               }
             }
             elseif ($this->currentUser->isAnonymous()) {
@@ -171,12 +171,11 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra
                   'fragment' => 'comment-form',
                 );
                 if ($comment_form_location == CommentItemInterface::FORM_SEPARATE_PAGE) {
-                  $links['comment-add']['route_name'] = 'comment.reply';
-                  $links['comment-add']['route_parameters'] = array(
+                  $links['comment-add']['url'] = Url::fromRoute('comment.reply', [
                     'entity_type' => $entity->getEntityTypeId(),
                     'entity' => $entity->id(),
                     'field_name' => $field_name,
-                  );
+                  ]);
                 }
                 else {
                   $links['comment-add'] += $entity->urlInfo()->toArray();
diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php
index 524349d..09399bf 100644
--- a/core/modules/comment/src/CommentViewBuilder.php
+++ b/core/modules/comment/src/CommentViewBuilder.php
@@ -17,6 +17,7 @@
 use Drupal\Core\Entity\EntityViewBuilder;
 use Drupal\Core\Entity\Entity\EntityViewDisplay;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -246,7 +247,7 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
       if ($entity->access('delete')) {
         $links['comment-delete'] = array(
           'title' => t('Delete'),
-          'href' => "comment/{$entity->id()}/delete",
+          'url' => $entity->urlInfo('delete-form'),
           'html' => TRUE,
         );
       }
@@ -254,22 +255,26 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
       if ($entity->access('update')) {
         $links['comment-edit'] = array(
           'title' => t('Edit'),
-          'href' => "comment/{$entity->id()}/edit",
+          'url' => $entity->urlInfo('edit-form'),
           'html' => TRUE,
         );
       }
       if ($entity->access('create')) {
         $links['comment-reply'] = array(
           'title' => t('Reply'),
-          'href' => "comment/reply/{$entity->getCommentedEntityTypeId()}/{$entity->getCommentedEntityId()}/{$entity->getFieldName()}/{$entity->id()}",
+          'url' => Url::fromRoute('comment.reply', [
+            'entity_type' => $entity->getCommentedEntityTypeId(),
+            'entity' => $entity->getCommentedEntityId(),
+            'field_name' => $entity->getFieldName(),
+            'pid' => $entity->id(),
+          ]),
           'html' => TRUE,
         );
       }
       if (!$entity->isPublished() && $entity->access('approve')) {
         $links['comment-approve'] = array(
           'title' => t('Approve'),
-          'route_name' => 'comment.approve',
-          'route_parameters' => array('comment' => $entity->id()),
+          'url' => Url::fromRoute('comment.approve', ['comment' => $entity->id()]),
           'html' => TRUE,
         );
       }
@@ -283,7 +288,7 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
     if (\Drupal::moduleHandler()->moduleExists('content_translation') && content_translation_translate_access($entity)->isAllowed()) {
       $links['comment-translations'] = array(
         'title' => t('Translate'),
-        'href' => 'comment/' . $entity->id() . '/translations',
+        'url' => $entity->urlInfo('drupal:content-translation-overview'),
         'html' => TRUE,
       );
     }
diff --git a/core/modules/comment/src/Form/CommentAdminOverview.php b/core/modules/comment/src/Form/CommentAdminOverview.php
index 723241a..c68f54b 100644
--- a/core/modules/comment/src/Form/CommentAdminOverview.php
+++ b/core/modules/comment/src/Form/CommentAdminOverview.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -200,7 +201,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
           'data' => array(
             '#type' => 'link',
             '#title' => $comment->getSubject(),
-          ) + $comment_permalink->toRenderArray(),
+            '#url' => $comment_permalink,
+          ),
         ),
         'author' => drupal_render($username),
         'posted_in' => array(
@@ -216,18 +218,12 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
       $links = array();
       $links['edit'] = array(
         'title' => $this->t('Edit'),
-        'route_name' => 'entity.comment.edit_form',
-        'route_parameters' => array('comment' => $comment->id()),
-        'options' => $comment_uri_options,
-        'query' => $destination,
+        'url' => Url::fromRoute('entity.comment.edit_form', ['comment' => $comment->id()], $comment_uri_options + ['query' => $destination]),
       );
       if ($this->moduleHandler->moduleExists('content_translation') && $this->moduleHandler->invoke('content_translation', 'translate_access', array($comment))->isAllowed()) {
         $links['translate'] = array(
           'title' => $this->t('Translate'),
-          'route_name' => 'content_translation.translation_overview_comment',
-          'route_parameters' => array('comment' => $comment->id()),
-          'options' => $comment_uri_options,
-          'query' => $destination,
+          'url' => Url::fromRoute('content_translation.translation_overview_comment', ['comment' => $comment->id()], $comment_uri_options + ['query' => $destination]),
         );
       }
       $options[$comment->id()]['operations']['data'] = array(
diff --git a/core/modules/comment/tests/modules/comment_test/comment_test.module b/core/modules/comment/tests/modules/comment_test/comment_test.module
index 7ff9376..797a5fd 100644
--- a/core/modules/comment/tests/modules/comment_test/comment_test.module
+++ b/core/modules/comment/tests/modules/comment_test/comment_test.module
@@ -7,6 +7,7 @@
  */
 
 use Drupal\comment\CommentInterface;
+use Drupal\Core\Url;
 
 /**
  * Implements hook_entity_type_alter().
@@ -36,9 +37,8 @@ function comment_test_comment_links_alter(array &$links, CommentInterface &$enti
     '#links' => array(
       'comment-report' => array(
         'title' => t('Report'),
-        'href' => "comment/{$entity->id()}/report",
+        'url' => Url::fromRoute('comment_test.report', ['comment' => $entity->id()], ['query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("comment/{$entity->id()}/report"))]),
         'html' => TRUE,
-        'query' => array('token' => \Drupal::getContainer()->get('csrf_token')->get("comment/{$entity->id()}/report")),
       ),
     ),
   );
diff --git a/core/modules/comment/tests/modules/comment_test/comment_test.routing.yml b/core/modules/comment/tests/modules/comment_test/comment_test.routing.yml
new file mode 100644
index 0000000..7841048
--- /dev/null
+++ b/core/modules/comment/tests/modules/comment_test/comment_test.routing.yml
@@ -0,0 +1,8 @@
+comment_test.report:
+  path: '/comment/{comment}/report'
+  defaults:
+    _title: 'Report'
+    _content: '\Drupal\comment_test\Controller\CommentTestController::commentReport'
+  requirements:
+    _access: 'TRUE'
+    _csrf_token: 'TRUE'
diff --git a/core/modules/comment/tests/modules/comment_test/src/Controller/CommentTestController.php b/core/modules/comment/tests/modules/comment_test/src/Controller/CommentTestController.php
new file mode 100644
index 0000000..4f71755
--- /dev/null
+++ b/core/modules/comment/tests/modules/comment_test/src/Controller/CommentTestController.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment_test\Controller\CommentTestController.
+ */
+
+namespace Drupal\comment_test\Controller;
+
+use Drupal\comment\CommentInterface;
+use Drupal\Core\Controller\ControllerBase;
+
+/**
+ * Controller for the comment_test.module.
+ */
+class CommentTestController extends ControllerBase {
+
+  /**
+   * Provides a comment report.
+   */
+  public function commentReport(CommentInterface $comment) {
+    return ['#markup' => $this->t('Report for a comment')];
+  }
+
+}
diff --git a/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php b/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php
deleted file mode 100644
index 2993f28..0000000
--- a/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php
+++ /dev/null
@@ -1,333 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Tests\comment\Unit\CommentLinkBuilderTest.
- */
-
-namespace Drupal\Tests\comment\Unit;
-
-use Drupal\comment\CommentLinkBuilder;
-use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
-use Drupal\node\NodeInterface;
-use Drupal\simpletest\TestBase;
-use Drupal\Tests\UnitTestCase;
-
-/**
- * @coversDefaultClass \Drupal\comment\CommentLinkBuilder
- * @group comment
- */
-class CommentLinkBuilderTest extends UnitTestCase {
-
-  /**
-   * Comment manager mock.
-   *
-   * @var \Drupal\comment\CommentManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $commentManager;
-
-  /**
-   * String translation mock.
-   *
-   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $stringTranslation;
-
-  /**
-   * Module handler mock.
-   *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $moduleHandler;
-
-  /**
-   * Current user proxy mock.
-   *
-   * @var \Drupal\Core\Session\AccountProxyInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $currentUser;
-
-  /**
-   * Timestamp used in test.
-   *
-   * @var int
-   */
-  protected $timestamp;
-
-  /**
-   * @var \Drupal\comment\CommentLinkBuilderInterface;
-   */
-  protected $commentLinkBuilder;
-
-  /**
-   * Prepares mocks for the test.
-   */
-  protected function setUp() {
-    $this->commentManager = $this->getMock('\Drupal\comment\CommentManagerInterface');
-    $this->stringTranslation = $this->getStringTranslationStub();
-    $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->currentUser = $this->getMock('\Drupal\Core\Session\AccountProxyInterface');
-    $this->commentLinkBuilder = new CommentLinkBuilder($this->currentUser, $this->commentManager, $this->moduleHandler, $this->stringTranslation);
-    $this->commentManager->expects($this->any())
-      ->method('getFields')
-      ->with('node')
-      ->willReturn(array(
-        'comment' => array(),
-      ));
-    $this->commentManager->expects($this->any())
-      ->method('forbiddenMessage')
-      ->willReturn("Can't let you do that Dave.");
-    $this->stringTranslation->expects($this->any())
-      ->method('formatPlural')
-      ->willReturnArgument(1);
-  }
-
-  /**
-   * Test the buildCommentedEntityLinks method.
-   *
-   * @param \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject $node
-   *   Mock node.
-   * @param array $context
-   *   Context for the links.
-   * @param bool $has_access_comments
-   *   TRUE if the user has 'access comments' permission.
-   * @param bool $history_exists
-   *   TRUE if the history module exists.
-   * @param bool $has_post_comments
-   *   TRUE if the use has 'post comments' permission.
-   * @param bool $is_anonymous
-   *   TRUE if the user is anonymous.
-   * @param array $expected
-   *   Array of expected links keyed by link ID. Can be either string (link
-   *   title) or array of link properties.
-   *
-   * @dataProvider getLinkCombinations
-   *
-   * @covers ::buildCommentedEntityLinks()
-   */
-  public function testCommentLinkBuilder(NodeInterface $node, $context, $has_access_comments, $history_exists, $has_post_comments, $is_anonymous, $expected) {
-    $this->moduleHandler->expects($this->any())
-      ->method('moduleExists')
-      ->with('history')
-      ->willReturn($history_exists);
-    $this->currentUser->expects($this->any())
-      ->method('hasPermission')
-      ->willReturnMap(array(
-        array('access comments', $has_access_comments),
-        array('post comments', $has_post_comments),
-      ));
-    $this->currentUser->expects($this->any())
-      ->method('isAuthenticated')
-      ->willReturn(!$is_anonymous);
-    $this->currentUser->expects($this->any())
-      ->method('isAnonymous')
-      ->willReturn($is_anonymous);
-    $links = $this->commentLinkBuilder->buildCommentedEntityLinks($node, $context);
-    if (!empty($expected)) {
-      if (!empty($links)) {
-        foreach ($expected as $link => $detail) {
-          if (is_array($detail)) {
-            // Array of link attributes.
-            foreach ($detail as $key => $value) {
-              $this->assertEquals($links['comment__comment']['#links'][$link][$key], $value);
-            }
-          }
-          else {
-            // Just the title.
-            $this->assertEquals($links['comment__comment']['#links'][$link]['title'], $detail);
-          }
-        }
-      }
-      else {
-        $this->fail('Expected links but found none.');
-      }
-    }
-    else {
-      $this->assertSame($links, $expected);
-    }
-    if ($context['view_mode'] == 'rss' && $node->get('comment')->status) {
-      $found = FALSE;
-      if ($node->get('comment')->status) {
-        foreach ($node->rss_elements as $element) {
-          if ($element['key'] == 'comments') {
-            $found = TRUE;
-            break;
-          }
-        }
-      }
-      $this->assertTrue($found);
-    }
-  }
-
-  /**
-   * Data provider for ::testCommentLinkBuilder.
-   */
-  public function getLinkCombinations() {
-    $cases = array();
-    // No links should be created if the entity doesn't have the field.
-    $cases[] = array(
-      $this->getMockNode(FALSE, CommentItemInterface::OPEN, CommentItemInterface::FORM_BELOW, 1),
-      array('view_mode' => 'teaser'),
-      TRUE,
-      TRUE,
-      TRUE,
-      TRUE,
-      array(),
-    );
-    foreach (array('search_result', 'search_index', 'print') as $view_mode) {
-      // Nothing should be output in these view modes.
-      $cases[] = array(
-        $this->getMockNode(TRUE, CommentItemInterface::OPEN, CommentItemInterface::FORM_BELOW, 1),
-        array('view_mode' => $view_mode),
-        TRUE,
-        TRUE,
-        TRUE,
-        TRUE,
-        array(),
-      );
-    }
-    // All other combinations.
-    $combinations = array(
-      'is_anonymous' => array(FALSE, TRUE),
-      'comment_count' => array(0, 1),
-      'has_access_comments' => array(0, 1),
-      'history_exists' => array(FALSE, TRUE),
-      'has_post_comments'   => array(0, 1),
-      'form_location'            => array(CommentItemInterface::FORM_BELOW, CommentItemInterface::FORM_SEPARATE_PAGE),
-      'comments'        => array(
-        CommentItemInterface::OPEN,
-        CommentItemInterface::CLOSED,
-        CommentItemInterface::HIDDEN,
-      ),
-      'view_mode' => array(
-        'teaser', 'rss', 'full',
-      ),
-    );
-    $permutations = TestBase::generatePermutations($combinations);
-    foreach ($permutations as $combination) {
-      $case = array(
-        $this->getMockNode(TRUE, $combination['comments'], $combination['form_location'], $combination['comment_count']),
-        array('view_mode' => $combination['view_mode']),
-        $combination['has_access_comments'],
-        $combination['history_exists'],
-        $combination['has_post_comments'],
-        $combination['is_anonymous'],
-      );
-      $expected = array();
-      // When comments are enabled in teaser mode, and comments exist, and the
-      // user has access - we can output the comment count.
-      if ($combination['comments'] && $combination['view_mode'] == 'teaser' && $combination['comment_count'] && $combination['has_access_comments']) {
-        $expected['comment-comments'] = '1 comment';
-        // And if history module exists, we can show a 'new comments' link.
-        if ($combination['history_exists']) {
-          $expected['comment-new-comments'] = '';
-        }
-      }
-      // All view modes other than RSS.
-      if ($combination['view_mode'] != 'rss') {
-        // Where commenting is open.
-        if ($combination['comments'] == CommentItemInterface::OPEN) {
-          // And the user has post-comments permission.
-          if ($combination['has_post_comments']) {
-            // If the view mode is teaser, or the user can access comments and
-            // comments exist or the form is on a separate page.
-            if ($combination['view_mode'] == 'teaser' || ($combination['has_access_comments'] && $combination['comment_count']) || $combination['form_location'] == CommentItemInterface::FORM_SEPARATE_PAGE) {
-              // There should be a add comment link.
-              $expected['comment-add'] = array('title' => 'Add new comment');
-              if ($combination['form_location'] == CommentItemInterface::FORM_BELOW) {
-                // On the same page.
-                $expected['comment-add']['route_name'] = 'node.view';
-              }
-              else {
-                // On a separate page.
-                $expected['comment-add']['route_name'] = 'comment.reply';
-              }
-            }
-          }
-          elseif ($combination['is_anonymous']) {
-            // Anonymous users get the forbidden message if the can't post
-            // comments.
-            $expected['comment-forbidden'] = "Can't let you do that Dave.";
-          }
-        }
-      }
-
-      $case[] = $expected;
-      $cases[] = $case;
-    }
-    return $cases;
-  }
-
-  /**
-   * Builds a mock node based on given scenario.
-   *
-   * @param bool $has_field
-   *   TRUE if the node has the 'comment' field.
-   * @param int $comment_status
-   *   One of CommentItemInterface::OPEN|HIDDEN|CLOSED
-   * @param int $form_location
-   *   One of CommentItemInterface::FORM_BELOW|FORM_SEPARATE_PAGE
-   * @param int $comment_count
-   *   Number of comments against the field.
-   *
-   * @return \Drupal\node\NodeInterface|\PHPUnit_Framework_MockObject_MockObject
-   *   Mock node for testing.
-   */
-  protected function getMockNode($has_field, $comment_status, $form_location, $comment_count) {
-    $node = $this->getMock('\Drupal\node\NodeInterface');
-    $node->expects($this->once())
-      ->method('hasField')
-      ->willReturn($has_field);
-
-    if (empty($this->timestamp)) {
-      $this->timestamp = time();
-    }
-    $field_item = (object) array(
-      'status' => $comment_status,
-      'comment_count' => $comment_count,
-      'last_comment_timestamp' => $this->timestamp,
-    );
-    $node->expects($this->any())
-      ->method('get')
-      ->with('comment')
-      ->willReturn($field_item);
-
-    $field_definition = $this->getMock('\Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->any())
-      ->method('getSetting')
-      ->with('form_location')
-      ->willReturn($form_location);
-    $node->expects($this->any())
-      ->method('getFieldDefinition')
-      ->with('comment')
-      ->willReturn($field_definition);
-
-    $node->expects($this->any())
-      ->method('language')
-      ->willReturn('und');
-
-    $node->expects($this->any())
-      ->method('getEntityTypeId')
-      ->willReturn('node');
-
-    $node->expects($this->any())
-      ->method('id')
-      ->willReturn(1);
-
-    $url = $this->getMockBuilder('\Drupal\Core\Url')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $url->expects($this->any())
-      ->method('toArray')
-      ->willReturn(array('route_name' => 'node.view'));
-    $node->expects($this->any())
-      ->method('urlInfo')
-      ->willReturn($url);
-    $node->expects($this->any())
-      ->method('url')
-      ->willReturn(array('route_name' => 'node.view'));
-
-    return $node;
-  }
-
-}
diff --git a/core/modules/config/src/Controller/ConfigController.php b/core/modules/config/src/Controller/ConfigController.php
index 48b31df..8eb713f 100644
--- a/core/modules/config/src/Controller/ConfigController.php
+++ b/core/modules/config/src/Controller/ConfigController.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Diff\DiffFormatter;
+use Drupal\Core\Url;
 use Drupal\system\FileDownloadController;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -156,7 +157,7 @@ public function diff($source_name, $target_name = NULL, $collection = NULL) {
         ),
       ),
       '#title' => "Back to 'Synchronize configuration' page.",
-      '#href' => 'admin/config/development/configuration',
+      '#url' => Url::fromRoute('config.sync'),
     );
 
     return $build;
diff --git a/core/modules/config/src/Form/ConfigSync.php b/core/modules/config/src/Form/ConfigSync.php
index f73de96..b026e6f 100644
--- a/core/modules/config/src/Form/ConfigSync.php
+++ b/core/modules/config/src/Form/ConfigSync.php
@@ -20,7 +20,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Core\Config\StorageComparer;
-use Drupal\Core\Routing\UrlGeneratorInterface;
+use Drupal\Core\Url;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -72,13 +72,6 @@ class ConfigSync extends FormBase {
   protected $configManager;
 
   /**
-   * URL generator service.
-   *
-   * @var \Drupal\Core\Routing\UrlGeneratorInterface
-   */
-  protected $urlGenerator;
-
-  /**
    * The typed config manager.
    *
    * @var \Drupal\Core\Config\TypedConfigManagerInterface
@@ -114,8 +107,6 @@ class ConfigSync extends FormBase {
    *   Event dispatcher.
    * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
    *   Configuration manager.
-   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
-   *   The url generator service.
    * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
    *   The typed configuration manager.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -123,14 +114,13 @@ class ConfigSync extends FormBase {
    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
    *   The theme handler
    */
-  public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, UrlGeneratorInterface $url_generator, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
+  public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
     $this->stagingStorage = $staging_storage;
     $this->activeStorage = $active_storage;
     $this->snapshotStorage = $snapshot_storage;
     $this->lock = $lock;
     $this->eventDispatcher = $event_dispatcher;
     $this->configManager = $config_manager;
-    $this->urlGenerator = $url_generator;
     $this->typedConfigManager = $typed_config;
     $this->moduleHandler = $module_handler;
     $this->themeHandler = $theme_handler;
@@ -147,7 +137,6 @@ public static function create(ContainerInterface $container) {
       $container->get('lock'),
       $container->get('event_dispatcher'),
       $container->get('config.manager'),
-      $container->get('url_generator'),
       $container->get('config.typed'),
       $container->get('module_handler'),
       $container->get('theme_handler')
@@ -268,15 +257,15 @@ public function buildForm(array $form, FormStateInterface $form_state) {
             $route_options = array('source_name' => $config_name);
           }
           if ($collection != StorageInterface::DEFAULT_COLLECTION) {
+            $route_name = 'config.diff_collection';
             $route_options['collection'] = $collection;
-            $href = $this->urlGenerator->getPathFromRoute('config.diff_collection', $route_options);
           }
           else {
-            $href = $this->urlGenerator->getPathFromRoute('config.diff', $route_options);
+            $route_name = 'config.diff';
           }
           $links['view_diff'] = array(
             'title' => $this->t('View differences'),
-            'href' => $href,
+            'url' => Url::fromRoute($route_name, $route_options),
             'attributes' => array(
               'class' => array('use-ajax'),
               'data-accepts' => 'application/vnd.drupal-modal',
diff --git a/core/modules/config_translation/src/ConfigEntityMapper.php b/core/modules/config_translation/src/ConfigEntityMapper.php
index ce66ff6..2cbc78e 100644
--- a/core/modules/config_translation/src/ConfigEntityMapper.php
+++ b/core/modules/config_translation/src/ConfigEntityMapper.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
 use Drupal\locale\LocaleConfigManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -216,7 +217,9 @@ public function getOperations() {
     return array(
       'list' => array(
         'title' => $this->t('List'),
-        'href' => 'admin/config/regional/config-translation/' . $this->getPluginId(),
+        'url' => Url::fromRoute('config_translation.entity_list', [
+          'mapper_id' => $this->getPluginId(),
+        ]),
       ),
     );
   }
diff --git a/core/modules/config_translation/src/ConfigNamesMapper.php b/core/modules/config_translation/src/ConfigNamesMapper.php
index 57811d9..ed796af 100644
--- a/core/modules/config_translation/src/ConfigNamesMapper.php
+++ b/core/modules/config_translation/src/ConfigNamesMapper.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Url;
 use Drupal\locale\LocaleConfigManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -200,7 +201,7 @@ protected function processRoute(Route $route) {
    * {@inheritdoc}
    */
   public function getBasePath() {
-    return $this->getPathFromRoute($this->getBaseRoute(), $this->getBaseRouteParameters());
+    return Url::fromRoute($this->getBaseRouteName(), $this->getBaseRouteParameters())->getInternalPath();
   }
 
   /**
@@ -237,7 +238,7 @@ public function getOverviewRoute() {
    * {@inheritdoc}
    */
   public function getOverviewPath() {
-    return $this->getPathFromRoute($this->getOverviewRoute(), $this->getOverviewRouteParameters());
+    return Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters())->getInternalPath();
   }
 
   /**
@@ -335,25 +336,6 @@ public function getDeleteRoute() {
   }
 
   /**
-   * Gets the path for a certain route, given a set of route parameters.
-   *
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   * @param array $parameters
-   *   An array of route parameters.
-   *
-   * @return string
-   *   Processed path with placeholders replaced.
-   */
-  public function getPathFromRoute(Route $route, array $parameters) {
-    $path = $route->getPath();
-    foreach ($parameters as $key => $value) {
-      $path = str_replace('{' . $key . '}', $value, $path);
-    }
-    return $path;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function getConfigNames() {
@@ -492,7 +474,7 @@ public function getOperations() {
     return array(
       'translate' => array(
         'title' => $this->t('Translate'),
-        'href' => $this->getOverviewPath(),
+        'url' => Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters()),
       ),
     );
   }
diff --git a/core/modules/config_translation/src/Controller/ConfigTranslationController.php b/core/modules/config_translation/src/Controller/ConfigTranslationController.php
index 646d3b8..b6b016c 100644
--- a/core/modules/config_translation/src/Controller/ConfigTranslationController.php
+++ b/core/modules/config_translation/src/Controller/ConfigTranslationController.php
@@ -15,6 +15,7 @@
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
@@ -174,9 +175,7 @@ public function itemPage(Request $request, RouteMatchInterface $route_match, $pl
         if ($edit_access) {
           $operations['edit'] = array(
             'title' => $this->t('Edit'),
-            'route_name' => $mapper->getBaseRouteName(),
-            'route_parameters' => $mapper->getBaseRouteParameters(),
-            'query' => array('destination' => $mapper->getOverviewPath()),
+            'url' => Url::fromRoute($mapper->getBaseRouteName(), $mapper->getBaseRouteParameters(), ['query' => ['destination' => $mapper->getOverviewPath()]]),
           );
         }
       }
@@ -188,22 +187,19 @@ public function itemPage(Request $request, RouteMatchInterface $route_match, $pl
         if (!$mapper->hasTranslation($language)) {
           $operations['add'] = array(
             'title' => $this->t('Add'),
-            'route_name' => $mapper->getAddRouteName(),
-            'route_parameters' => $mapper->getAddRouteParameters(),
+            'url' => Url::fromRoute($mapper->getAddRouteName(), $mapper->getAddRouteParameters()),
           );
         }
         else {
           // Otherwise, link to edit the existing translation.
           $operations['edit'] = array(
             'title' => $this->t('Edit'),
-            'route_name' => $mapper->getEditRouteName(),
-            'route_parameters' => $mapper->getEditRouteParameters(),
+            'url' => Url::fromRoute($mapper->getEditRouteName(), $mapper->getEditRouteParameters()),
           );
 
           $operations['delete'] = array(
             'title' => $this->t('Delete'),
-            'route_name' => $mapper->getDeleteRouteName(),
-            'route_parameters' => $mapper->getDeleteRouteParameters(),
+            'url' => Url::fromRoute($mapper->getDeleteRouteName(), $mapper->getDeleteRouteParameters()),
           );
         }
       }
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
index 69828f6..2b1c67e 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\config_translation\Unit;
 
 use Drupal\config_translation\ConfigEntityMapper;
+use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\Routing\Route;
 
@@ -188,11 +189,11 @@ public function testGetOperations() {
     $expected = array(
       'list' => array(
         'title' => 'List',
-        'href' => 'admin/config/regional/config-translation/configurable_language',
-      )
+        'url' => Url::fromRoute('config_translation.entity_list', ['mapper_id' => 'configurable_language']),
+      ),
     );
 
-    $this->assertSame($expected, $result);
+    $this->assertEquals($expected, $result);
   }
 
 }
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
index 5b19122..50e2712 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
@@ -9,7 +9,9 @@
 
 use Drupal\config_translation\ConfigNamesMapper;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\HttpFoundation\Request;
@@ -72,6 +74,13 @@ class ConfigNamesMapperTest extends UnitTestCase {
    */
   protected $routeProvider;
 
+  /**
+   * The mocked URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $urlGenerator;
+
   protected function setUp() {
     $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
 
@@ -91,6 +100,11 @@ protected function setUp() {
 
     $this->configMapperManager = $this->getMock('Drupal\config_translation\ConfigMapperManagerInterface');
 
+    $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+    $container = new ContainerBuilder();
+    $container->set('url_generator', $this->urlGenerator);
+    \Drupal::setContainer($container);
+
     $this->baseRoute = new Route('/admin/config/system/site-information');
 
     $this->routeProvider
@@ -147,6 +161,10 @@ public function testGetBaseRoute() {
    * Tests ConfigNamesMapper::getBasePath().
    */
   public function testGetBasePath() {
+    $this->urlGenerator->expects($this->once())
+      ->method('getPathFromRoute')
+      ->with('system.site_information_settings', [])
+      ->willReturn('/admin/config/system/site-information');
     $result = $this->configNamesMapper->getBasePath();
     $this->assertSame('/admin/config/system/site-information', $result);
   }
@@ -189,6 +207,11 @@ public function testGetOverviewRoute() {
    * Tests ConfigNamesMapper::getOverviewPath().
    */
   public function testGetOverviewPath() {
+    $this->urlGenerator->expects($this->once())
+      ->method('getPathFromRoute')
+      ->with('config_translation.item.overview.system.site_information_settings', [])
+      ->willReturn('/admin/config/system/site-information/translate');
+
     $result = $this->configNamesMapper->getOverviewPath();
     $this->assertSame('/admin/config/system/site-information/translate', $result);
   }
@@ -605,7 +628,7 @@ public function testGetOperations() {
     $expected = array(
       'translate' => array(
         'title' => 'Translate',
-        'href' => '/admin/config/system/site-information/translate',
+        'url' => Url::fromRoute('config_translation.item.overview.system.site_information_settings'),
       ),
     );
     $result = $this->configNamesMapper->getOperations();
diff --git a/core/modules/contextual/src/Element/ContextualLinks.php b/core/modules/contextual/src/Element/ContextualLinks.php
index 8aeec9f..3c95191 100644
--- a/core/modules/contextual/src/Element/ContextualLinks.php
+++ b/core/modules/contextual/src/Element/ContextualLinks.php
@@ -8,6 +8,7 @@
 namespace Drupal\contextual\Element;
 
 use Drupal\Core\Render\Element\RenderElement;
+use Drupal\Core\Url;
 
 /**
  * Provides a contextual_links element.
@@ -82,8 +83,7 @@ public static function preRenderLinks(array $element) {
       $class = drupal_html_class($class);
       $links[$class] = array(
         'title' => $item['title'],
-        'route_name' => isset($item['route_name']) ? $item['route_name'] : '',
-        'route_parameters' => isset($item['route_parameters']) ? $item['route_parameters'] : array(),
+        'url' => Url::fromRoute(isset($item['route_name']) ? $item['route_name'] : '', isset($item['route_parameters']) ? $item['route_parameters'] : array()),
       );
     }
     $element['#links'] = $links;
diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php b/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
index 154759f..e50e70d 100644
--- a/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
+++ b/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
@@ -76,11 +76,14 @@ public function viewElements(FieldItemListInterface $items) {
           $elements[$delta] = array(
             '#type' => 'link',
             '#title' => $label,
-          ) + $uri->toRenderArray();
+            '#url' => $uri,
+          );
 
           if (!empty($item->_attributes)) {
-            $elements[$delta]['#options'] += array('attributes' => array());
-            $elements[$delta]['#options']['attributes'] += $item->_attributes;
+            $options = $elements[$delta]['#url']->getOptions();
+            $options += array('attributes' => array());
+            $options['attributes'] += $item->_attributes;
+            $elements[$delta]['#url']->setOptions($options);
             // Unset field item attributes since they have been included in the
             // formatter output and shouldn't be rendered in the field template.
             unset($item->_attributes);
diff --git a/core/modules/field_ui/src/EntityDisplayModeListBuilder.php b/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
index 2a73834..6056508 100644
--- a/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
+++ b/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -118,7 +119,7 @@ public function render() {
       $table['#rows']['_add_new'][] = array(
         'data' => array(
           '#type' => 'link',
-          '#href' => "admin/structure/display-modes/$short_type/add/$entity_type",
+          '#url' => Url::fromRoute($short_type == 'view' ? 'field_ui.entity_view_mode_add_type' : 'field_ui.entity_form_mode_add_type', ['entity_type_id' => $entity_type]),
           '#title' => t('Add new %label @entity-type', array('%label' => $this->entityTypes[$entity_type]->getLabel(), '@entity-type' => $this->entityType->getLowercaseLabel())),
           '#options' => array(
             'html' => TRUE,
diff --git a/core/modules/field_ui/src/FieldOverview.php b/core/modules/field_ui/src/FieldOverview.php
index 0e42fcd..838c116 100644
--- a/core/modules/field_ui/src/FieldOverview.php
+++ b/core/modules/field_ui/src/FieldOverview.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Drupal\field_ui\OverviewBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\field\Entity\FieldStorageConfig;
@@ -135,8 +136,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
         'type' => array(
           '#type' => 'link',
           '#title' => $field_types[$field_storage->getType()]['label'],
-          '#route_name' => 'field_ui.storage_edit_' . $this->entity_type,
-          '#route_parameters' => $route_parameters,
+          '#url' => Url::fromRoute('field_ui.storage_edit_' . $this->entity_type, $route_parameters),
           '#options' => array('attributes' => array('title' => $this->t('Edit field settings.'))),
         ),
       );
diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
index ff21395..2379899 100644
--- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -106,7 +106,7 @@ function manageFieldsPage($type = '') {
     $url = base_path() . "admin/structure/types/manage/$type/fields/node.$type.body";
     $this->assertIdentical($url, (string) $result[0]['href']);
     $this->assertIdentical("$url/storage", (string) $result[1]['href']);
-    $this->assertIdentical("$url/delete", (string) $result[3]['href']);
+    $this->assertIdentical("$url/delete", (string) $result[2]['href']);
   }
 
   /**
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 1b15b3e..3719076 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -52,8 +52,7 @@ function forum_help($route_name, RouteMatchInterface $route_match) {
       $output = '<p>' . t('Forums contain forum topics. Use containers to group related forums.') . '</p>';
       $more_help_link = array(
         '#type' => 'link',
-        '#route_name' => 'help.page',
-        '#route_parameters' => array('name' => 'forum'),
+        '#url' => Url::fromRoute('help.page', ['name' => 'forum']),
         '#title' => t('More help'),
       );
       $container = array(
@@ -105,8 +104,6 @@ function forum_theme() {
  * Implements hook_menu_local_tasks().
  */
 function forum_menu_local_tasks(&$data, $route_name) {
-  $user = \Drupal::currentUser();
-
   // Add action link to 'node/add/forum' on 'forum' sub-pages.
   if (in_array($route_name, array('forum.index', 'forum.page'))) {
     $forum_term = \Drupal::routeMatch()->getParameter('taxonomy_term');
@@ -120,7 +117,7 @@ function forum_menu_local_tasks(&$data, $route_name) {
           '#theme' => 'menu_local_action',
           '#link' => array(
             'title' => t('Add new @node_type', array('@node_type' => entity_load('node_type', $type)->label())),
-            'href' => 'node/add/' . $type,
+            'url' => Url::fromRoute('node.add', ['node_type' => $type]),
           ),
         );
         if ($forum_term && $forum_term->bundle() == $vid) {
@@ -130,29 +127,6 @@ function forum_menu_local_tasks(&$data, $route_name) {
         }
       }
     }
-    if (empty($links)) {
-      // Authenticated user does not have access to create new topics.
-      if ($user->isAuthenticated()) {
-        $links['disallowed'] = array(
-          '#theme' => 'menu_local_action',
-          '#link' => array(
-            'title' => t('You are not allowed to post new content in the forum.'),
-          ),
-        );
-      }
-      // Anonymous user does not have access to create new topics.
-      else {
-        $links['login'] = array(
-          '#theme' => 'menu_local_action',
-          '#link' => array(
-            'title' => t('<a href="@login">Log in</a> to post new content in the forum.', array(
-              '@login' => \Drupal::url('user.login', [], array('query' => drupal_get_destination())),
-            )),
-            'localized_options' => array('html' => TRUE),
-          ),
-        );
-      }
-    }
     $data['actions'] += $links;
   }
 }
@@ -186,12 +160,7 @@ function forum_entity_bundle_info_alter(&$bundles) {
  * Entity URI callback used in forum_entity_bundle_info_alter().
  */
 function forum_uri($forum) {
-  return array(
-    'route_name' => 'forum.page',
-    'route_parameters' => array(
-      'taxonomy_term' => $forum->id(),
-    ),
-  );
+  return Url::fromRoute('forum.page', ['taxonomy_term' => $forum->id()]);
 }
 
 /**
diff --git a/core/modules/forum/src/Form/ForumForm.php b/core/modules/forum/src/Form/ForumForm.php
index 6a8131a..fc51a32 100644
--- a/core/modules/forum/src/Form/ForumForm.php
+++ b/core/modules/forum/src/Form/ForumForm.php
@@ -107,10 +107,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
     $actions = parent::actions($form, $form_state);
 
     if (!$this->entity->isNew() && $this->entity->hasLinkTemplate('forum-delete-form')) {
-      $route_info = $this->entity->urlInfo('forum-delete-form');
-      $actions['delete']['#options'] = $route_info->getOptions();
-      $actions['delete']['#route_name'] = $route_info->getRouteName();
-      $actions['delete']['#route_parameters'] = $route_info->getRouteParameters();
+      $actions['delete']['#url'] = $this->entity->urlInfo('forum-delete-form');
     }
     else {
       unset($actions['delete']);
diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php
index eb05b3a..a7e1b60 100644
--- a/core/modules/forum/src/Form/Overview.php
+++ b/core/modules/forum/src/Form/Overview.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Drupal\taxonomy\Form\OverviewTerms;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -65,18 +66,18 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     foreach (Element::children($form['terms']) as $key) {
       if (isset($form['terms'][$key]['#term'])) {
         $term = $form['terms'][$key]['#term'];
-        $form['terms'][$key]['term']['#href'] = 'forum/' . $term->id();
+        $form['terms'][$key]['term']['#url'] = Url::fromRoute('forum.page', ['taxonomy_term' => $term->id()]);
         unset($form['terms'][$key]['operations']['#links']['delete']);
         if (!empty($term->forum_container->value)) {
           $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit container');
-          $form['terms'][$key]['operations']['#links']['edit']['route_name'] = 'forum.edit_container';
+          $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('forum.edit_container');
           // We don't want the redirect from the link so we can redirect the
           // delete action.
           unset($form['terms'][$key]['operations']['#links']['edit']['query']['destination']);
         }
         else {
           $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit forum');
-          $form['terms'][$key]['operations']['#links']['edit']['route_name'] = 'forum.edit_forum';
+          $form['terms'][$key]['operations']['#links']['edit']['url'] = Url::fromRoute('forum.edit_forum');
           // We don't want the redirect from the link so we can redirect the
           // delete action.
           unset($form['terms'][$key]['operations']['#links']['edit']['query']['destination']);
diff --git a/core/modules/forum/src/Plugin/Block/ForumBlockBase.php b/core/modules/forum/src/Plugin/Block/ForumBlockBase.php
index f480892..17c3147 100644
--- a/core/modules/forum/src/Plugin/Block/ForumBlockBase.php
+++ b/core/modules/forum/src/Plugin/Block/ForumBlockBase.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Cache\Cache;
+use Drupal\Core\Url;
 
 /**
  * Provides a base class for Forum blocks.
@@ -27,7 +28,7 @@ public function build() {
       $elements['forum_list'] = $node_title_list;
       $elements['forum_more'] = array(
         '#type' => 'more_link',
-        '#href' => 'forum',
+        '#url' => Url::fromRoute('forum.index'),
         '#attributes' => array('title' => $this->t('Read the latest forum topics.')),
       );
     }
diff --git a/core/modules/forum/src/Tests/ForumTest.php b/core/modules/forum/src/Tests/ForumTest.php
index ebda138..adbd2a4 100644
--- a/core/modules/forum/src/Tests/ForumTest.php
+++ b/core/modules/forum/src/Tests/ForumTest.php
@@ -121,7 +121,8 @@ function testForum() {
     $this->drupalLogin($this->web_user);
     // Verify that this user is shown a message that they may not post content.
     $this->drupalGet('forum/' . $this->forum['tid']);
-    $this->assertText(t('You are not allowed to post new content in the forum'), "Authenticated user without permission to post forum content is shown message in local tasks to that effect.");
+    // @todo Restore test coverage in https://www.drupal.org/node/1853072.
+    //$this->assertText(t('You are not allowed to post new content in the forum'), "Authenticated user without permission to post forum content is shown message in local tasks to that effect.");
 
 
     // Log in, and do basic tests for a user with permission to edit any forum
diff --git a/core/modules/image/src/Form/ImageEffectFormBase.php b/core/modules/image/src/Form/ImageEffectFormBase.php
index 52d5b2c..addbcf6 100644
--- a/core/modules/image/src/Form/ImageEffectFormBase.php
+++ b/core/modules/image/src/Form/ImageEffectFormBase.php
@@ -96,7 +96,8 @@ public function buildForm(array $form, FormStateInterface $form_state, ImageStyl
     $form['actions']['cancel'] = array(
       '#type' => 'link',
       '#title' => $this->t('Cancel'),
-    ) + $this->imageStyle->urlInfo('edit-form')->toRenderArray();
+      '#url' => $this->imageStyle->urlInfo('edit-form'),
+    );
     return $form;
   }
 
diff --git a/core/modules/image/src/Form/ImageStyleEditForm.php b/core/modules/image/src/Form/ImageStyleEditForm.php
index 8703ee6..4edd31c 100644
--- a/core/modules/image/src/Form/ImageStyleEditForm.php
+++ b/core/modules/image/src/Form/ImageStyleEditForm.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\image\ConfigurableImageEffectInterface;
 use Drupal\image\ImageEffectManager;
 use Drupal\Component\Utility\String;
@@ -126,12 +127,18 @@ public function form(array $form, FormStateInterface $form_state) {
       if ($is_configurable) {
         $links['edit'] = array(
           'title' => $this->t('Edit'),
-          'href' => 'admin/config/media/image-styles/manage/' . $this->entity->id() . '/effects/' . $key,
+          'url' => Url::fromRoute('image.effect_edit_form', [
+            'image_style' => $this->entity->id(),
+            'image_effect' => $key,
+          ]),
         );
       }
       $links['delete'] = array(
         'title' => $this->t('Delete'),
-        'href' => 'admin/config/media/image-styles/manage/' . $this->entity->id() . '/effects/' . $key . '/delete',
+        'url' => Url::fromRoute('image.effect_delete', [
+          'image_style' => $this->entity->id(),
+          'image_effect' => $key,
+        ]),
       );
       $form['effects'][$key]['operations'] = array(
         '#type' => 'operations',
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index d5e5b7f..d7a0415 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -8,6 +8,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Url;
 
 /**
  * Prepares variables for language negotiation configuration form.
@@ -113,7 +114,7 @@ function theme_language_negotiation_configure_browser_form_table($variables) {
     $links = array();
     $links['delete'] = array(
       'title' => t('Delete'),
-      'href' => "admin/config/regional/language/detection/browser/delete/$key",
+      'url' => Url::fromRoute('language.negotiation_browser_delete', ['browser_langcode' => $key]),
       'attributes' => array(
         'class' => array('image-style-link'),
       ),
diff --git a/core/modules/language/src/ConfigurableLanguageManager.php b/core/modules/language/src/ConfigurableLanguageManager.php
index 6699ab0..6345029 100644
--- a/core/modules/language/src/ConfigurableLanguageManager.php
+++ b/core/modules/language/src/ConfigurableLanguageManager.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageDefault;
 use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Url;
 use Drupal\language\Config\LanguageConfigFactoryOverrideInterface;
 use Drupal\language\Entity\ConfigurableLanguage;
 use Symfony\Component\HttpFoundation\Request;
@@ -395,7 +396,7 @@ public function getFallbackCandidates(array $context = array()) {
   /**
    * {@inheritdoc}
    */
-  public function getLanguageSwitchLinks($type, $path) {
+  public function getLanguageSwitchLinks($type, Url $url) {
     $links = FALSE;
 
     if ($this->negotiator) {
@@ -403,7 +404,7 @@ public function getLanguageSwitchLinks($type, $path) {
         $reflector = new \ReflectionClass($method['class']);
 
         if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) {
-          $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $path);
+          $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $url);
 
           if (!empty($result)) {
             // Allow modules to provide translations for specific links.
diff --git a/core/modules/language/src/Form/NegotiationConfigureForm.php b/core/modules/language/src/Form/NegotiationConfigureForm.php
index 83eb567..c7495e9 100644
--- a/core/modules/language/src/Form/NegotiationConfigureForm.php
+++ b/core/modules/language/src/Form/NegotiationConfigureForm.php
@@ -16,6 +16,7 @@
 use Drupal\Core\Extension\ThemeHandlerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\language\ConfigurableLanguageManagerInterface;
 use Drupal\language\LanguageNegotiatorInterface;
 use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
@@ -295,10 +296,10 @@ protected function configureFormTable(array &$form, $type)  {
         $table_form['description'][$method_id] = array('#markup' => Xss::filterAdmin($method['description']));
 
         $config_op = array();
-        if (isset($method['config_path'])) {
+        if (isset($method['config_route_name'])) {
           $config_op['configure'] = array(
             'title' => $this->t('Configure'),
-            'href' => $method['config_path'],
+            'url' => Url::fromRoute($method['config_route_name']),
           );
           // If there is at least one operation enabled show the operation
           // column.
diff --git a/core/modules/language/src/LanguageSwitcherInterface.php b/core/modules/language/src/LanguageSwitcherInterface.php
index d6edd5b..0550dd4 100644
--- a/core/modules/language/src/LanguageSwitcherInterface.php
+++ b/core/modules/language/src/LanguageSwitcherInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\language;
 
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -21,12 +22,12 @@
    *   The current request.
    * @param string $type
    *   The language type.
-   * @param string $path
-   *   The path links should point to.
+   * @param \Drupal\Core\Url $url
+   *   The URL the switch links will be relative to.
    *
    * @return array
    *   An array of link arrays keyed by language code.
    */
-  public function getLanguageSwitchLinks(Request $request, $type, $path);
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url);
 
 }
diff --git a/core/modules/language/src/Plugin/Block/LanguageBlock.php b/core/modules/language/src/Plugin/Block/LanguageBlock.php
index a3aba84..0502d2d 100644
--- a/core/modules/language/src/Plugin/Block/LanguageBlock.php
+++ b/core/modules/language/src/Plugin/Block/LanguageBlock.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -75,9 +76,9 @@ protected function blockAccess(AccountInterface $account) {
    */
   public function build() {
     $build = array();
-    $path = drupal_is_front_page() ? '<front>' : current_path();
+    $route_name = drupal_is_front_page() ? '<front>' : '<current>';
     $type = $this->getDerivativeId();
-    $links = $this->languageManager->getLanguageSwitchLinks($type, $path);
+    $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute($route_name));
 
     if (isset($links->links)) {
       $build = array(
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
index 0ce2c86..e9fc38e 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -20,7 +20,7 @@
  *   cache = 0,
  *   name = @Translation("Browser"),
  *   description = @Translation("Language from the browser's language settings."),
- *   config_path = "admin/config/regional/language/detection/browser"
+ *   config_route_name = "language.negotiation_browser"
  * )
  */
 class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase {
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
index 4819abf..c4cd150 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -18,7 +18,7 @@
  *   weight = 12,
  *   name = @Translation("Selected language"),
  *   description = @Translation("Language based on a selected language."),
- *   config_path = "admin/config/regional/language/detection/selected"
+ *   config_route_name = "language.negotiation_selected"
  * )
  */
 class LanguageNegotiationSelected extends LanguageNegotiationMethodBase {
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
index dc5617d..7f0085d 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Url;
 use Drupal\language\LanguageNegotiationMethodBase;
 use Drupal\language\LanguageSwitcherInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -21,7 +22,7 @@
  *   weight = -6,
  *   name = @Translation("Session"),
  *   description = @Translation("Language from a request/session parameter."),
- *   config_path = "admin/config/regional/language/detection/session"
+ *   config_route_name = "language.negotiation_session"
  * )
  */
 class LanguageNegotiationSession extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface {
@@ -121,7 +122,7 @@ public function processOutbound($path, &$options = array(), Request $request = N
   /**
    * {@inheritdoc}
    */
-  function getLanguageSwitchLinks(Request $request, $type, $path) {
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = array();
     $config = $this->config->get('language.negotiation')->get('session');
     $param = $config['parameter'];
@@ -132,7 +133,7 @@ function getLanguageSwitchLinks(Request $request, $type, $path) {
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $langcode = $language->id;
       $links[$langcode] = array(
-        'href' => $path,
+        'url' => $url,
         'title' => $language->getName(),
         'attributes' => array('class' => array('language-link')),
         'query' => $query,
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
index be9f915..0b46727 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Url;
 use Drupal\language\LanguageNegotiationMethodBase;
 use Drupal\language\LanguageSwitcherInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -25,7 +26,7 @@
  *   weight = -8,
  *   name = @Translation("URL"),
  *   description = @Translation("Language from the URL (Path prefix or domain)."),
- *   config_path = "admin/config/regional/language/detection/url"
+ *   config_route_name = "language.negotiation_url"
  * )
  */
 class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {
@@ -187,12 +188,12 @@ public function processOutbound($path, &$options = array(), Request $request = N
   /**
    * {@inheritdoc}
    */
-  function getLanguageSwitchLinks(Request $request, $type, $path) {
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = array();
 
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $links[$language->id] = array(
-        'href' => $path,
+        'url' => $url,
         'title' => $language->getName(),
         'language' => $language,
         'attributes' => array('class' => array('language-link')),
diff --git a/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php b/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php
index a5cb033..6766d4a 100644
--- a/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php
+++ b/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -62,7 +63,7 @@ public function typeLinkActiveClass() {
       'no_language' => array(
         '#type' => 'link',
         '#title' => t('Link to the current path with no langcode provided.'),
-        '#route_name' => '<current>',
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'attributes' => array(
             'id' => 'no_lang_link',
@@ -73,7 +74,7 @@ public function typeLinkActiveClass() {
       'fr' => array(
         '#type' => 'link',
         '#title' => t('Link to a French version of the current path.'),
-        '#route_name' => '<current>',
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'language' => $languages['fr'],
           'attributes' => array(
@@ -85,7 +86,7 @@ public function typeLinkActiveClass() {
       'en' => array(
         '#type' => 'link',
         '#title' => t('Link to an English version of the current path.'),
-        '#route_name' => '<current>',
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'language' => $languages['en'],
           'attributes' => array(
diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index ca753f8..3aabb67 100644
--- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -163,13 +163,7 @@ public function viewElements(FieldItemListInterface $items) {
           '#title' => $link_title,
           '#options' => $url->getOptions(),
         );
-        if ($url->isExternal()) {
-          $element[$delta]['#href'] = $url->getUri();
-        }
-        else {
-          $element[$delta]['#route_name'] = $url->getRouteName();
-          $element[$delta]['#route_parameters'] = $url->getRouteParameters();
-        }
+        $element[$delta]['#url'] = $url;
 
         if (!empty($item->_attributes)) {
           $element[$delta]['#options'] += array ('attributes' => array());
diff --git a/core/modules/node/src/Controller/NodeController.php b/core/modules/node/src/Controller/NodeController.php
index 64fc4f6..27ee85d 100644
--- a/core/modules/node/src/Controller/NodeController.php
+++ b/core/modules/node/src/Controller/NodeController.php
@@ -184,16 +184,14 @@ public function revisionOverview(NodeInterface $node) {
           if ($revert_permission) {
             $links['revert'] = array(
               'title' => $this->t('Revert'),
-              'route_name' => 'node.revision_revert_confirm',
-              'route_parameters' => array('node' => $node->id(), 'node_revision' => $vid),
+              'url' => Url::fromRoute('node.revision_revert_confirm', array('node' => $node->id(), 'node_revision' => $vid)),
             );
           }
 
           if ($delete_permission) {
             $links['delete'] = array(
               'title' => $this->t('Delete'),
-              'route_name' => 'node.revision_delete_confirm',
-              'route_parameters' => array('node' => $node->id(), 'node_revision' => $vid),
+              'url' => Url::fromRoute('node.revision_delete_confirm', array('node' => $node->id(), 'node_revision' => $vid)),
             );
           }
 
diff --git a/core/modules/node/src/Form/NodePreviewForm.php b/core/modules/node/src/Form/NodePreviewForm.php
index 81fd61c..52f0267 100644
--- a/core/modules/node/src/Form/NodePreviewForm.php
+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -81,7 +82,7 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
     $form['backlink'] = array(
       '#type' => 'link',
       '#title' => $this->t('Back to content editing'),
-      '#href' => $node->isNew() ? 'node/add/' . $node->bundle() :  'node/' . $node->id() . '/edit',
+      '#url' => $node->isNew() ? Url::fromRoute('node.add', ['node_type' => $node->bundle()]) : $node->urlInfo('edit-form'),
       '#options' => array('attributes' => array('class' => array('node-preview-backlink'))) + $query_options,
     );
 
diff --git a/core/modules/node/src/NodeListBuilder.php b/core/modules/node/src/NodeListBuilder.php
index c78915d..606da79 100644
--- a/core/modules/node/src/NodeListBuilder.php
+++ b/core/modules/node/src/NodeListBuilder.php
@@ -105,7 +105,8 @@ public function buildRow(EntityInterface $entity) {
       '#type' => 'link',
       '#title' => $entity->label(),
       '#suffix' => ' ' . drupal_render($mark),
-    ) + $uri->toRenderArray();
+      '#url' => $uri,
+    );
     $row['type'] = String::checkPlain(node_get_type_label($entity));
     $row['author']['data'] = array(
       '#theme' => 'username',
diff --git a/core/modules/node/src/NodeViewBuilder.php b/core/modules/node/src/NodeViewBuilder.php
index 8cf1c04..1136428 100644
--- a/core/modules/node/src/NodeViewBuilder.php
+++ b/core/modules/node/src/NodeViewBuilder.php
@@ -146,7 +146,7 @@ protected static function buildLinks(NodeInterface $entity, $view_mode) {
         'title' => t('Read more<span class="visually-hidden"> about @title</span>', array(
           '@title' => $node_title_stripped,
         )),
-        'href' => 'node/' . $entity->id(),
+        'url' => $entity->urlInfo(),
         'language' => $entity->language(),
         'html' => TRUE,
         'attributes' => array(
diff --git a/core/modules/node/src/Plugin/views/area/ListingEmpty.php b/core/modules/node/src/Plugin/views/area/ListingEmpty.php
index cba41c2..8804f2e 100644
--- a/core/modules/node/src/Plugin/views/area/ListingEmpty.php
+++ b/core/modules/node/src/Plugin/views/area/ListingEmpty.php
@@ -8,6 +8,7 @@
 namespace Drupal\node\Plugin\views\area;
 
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Url;
 use Drupal\views\Plugin\views\area\AreaPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -67,7 +68,7 @@ public function render($empty = FALSE) {
         '#theme' => 'links',
         '#links' => array(
           array(
-            'href' => 'node/add',
+            'url' => Url::fromRoute('node.add_page'),
             'title' => $this->t('Add content'),
           ),
         ),
diff --git a/core/modules/path/src/Controller/PathController.php b/core/modules/path/src/Controller/PathController.php
index 96dd34b..5f6f3c6 100644
--- a/core/modules/path/src/Controller/PathController.php
+++ b/core/modules/path/src/Controller/PathController.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Path\AliasStorageInterface;
 use Drupal\Core\Path\AliasManagerInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -87,19 +88,11 @@ public function adminOverview($keys) {
       $operations = array();
       $operations['edit'] = array(
         'title' => $this->t('Edit'),
-        'route_name' => 'path.admin_edit',
-        'route_parameters' => array(
-          'pid' => $data->pid,
-        ),
-        'query' => $destination,
+        'url' => Url::fromRoute('path.admin_edit', ['pid' => $data->pid], ['query' => $destination]),
       );
       $operations['delete'] = array(
         'title' => $this->t('Delete'),
-        'route_name' => 'path.delete',
-        'route_parameters' => array(
-          'pid' => $data->pid,
-        ),
-        'query' => $destination,
+        'url' => Url::fromRoute('path.delete', ['pid' => $data->pid], ['query' => $destination]),
       );
       $row['data']['operations'] = array(
         'data' => array(
diff --git a/core/modules/search/src/SearchPageListBuilder.php b/core/modules/search/src/SearchPageListBuilder.php
index 5714f0a..4333064 100644
--- a/core/modules/search/src/SearchPageListBuilder.php
+++ b/core/modules/search/src/SearchPageListBuilder.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -118,7 +119,7 @@ public function buildRow(EntityInterface $entity) {
       $row['url'] = array(
         '#type' => 'link',
         '#title' => $row['url'],
-        '#route_name' => 'search.view_' . $entity->id(),
+        '#url' => Url::fromRoute('search.view_' . $entity->id()),
       );
     }
 
@@ -305,10 +306,9 @@ public function getDefaultOperations(EntityInterface $entity) {
     else {
       $operations['default'] = array(
         'title' => $this->t('Set as default'),
-        'route_name' => 'entity.search_page.set_default',
-        'route_parameters' => array(
+        'url' => Url::fromRoute('entity.search_page.set_default', array(
           'search_page' => $entity->id(),
-        ),
+        )),
         'weight' => 50,
       );
     }
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 2575128..8150bf2 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -258,7 +258,7 @@ function shortcut_renderable_links($shortcut_set = NULL) {
     $shortcut = \Drupal::entityManager()->getTranslationFromContext($shortcut);
     $links[] = array(
       'title' => $shortcut->label(),
-      'href' => $shortcut->path->value,
+      'url' => Url::fromUri('base://' . $shortcut->path->value),
     );
     $cache_tags = Cache::mergeTags($cache_tags, $shortcut->getCacheTag());
   }
@@ -343,8 +343,7 @@ function shortcut_preprocess_page(&$variables) {
         '#prefix' => '<div class="add-or-remove-shortcuts ' . $link_mode . '-shortcut">',
         '#type' => 'link',
         '#title' => '<span class="icon"></span><span class="text">'. $link_text .'</span>',
-        '#route_name' => $route_name,
-        '#route_parameters' => $route_parameters,
+        '#url' => Url::fromRoute($route_name, $route_parameters),
         '#options' => array('query' => $query, 'html' => TRUE),
         '#suffix' => '</div>',
       );
@@ -367,8 +366,7 @@ function shortcut_toolbar() {
       $configure_link = array(
         '#type' => 'link',
         '#title' => t('Edit shortcuts'),
-        '#route_name' => 'entity.shortcut_set.customize_form',
-        '#route_parameters' => array('shortcut_set' => $shortcut_set->id()),
+        '#url' => Url::fromRoute('entity.shortcut_set.customize_form', ['shortcut_set' => $shortcut_set->id()])
         '#options' => array('attributes' => array('class' => array('edit-shortcuts'))),
       );
     }
@@ -378,7 +376,7 @@ function shortcut_toolbar() {
         'tab' => array(
           '#type' => 'link',
           '#title' => t('Shortcuts'),
-          '#href' => 'admin/config/user-interface/shortcut',
+          '#url' => Url::fromRoute('shortcut.set_admin'),
           '#attributes' => array(
             'title' => t('Shortcuts'),
             'class' => array('toolbar-icon', 'toolbar-icon-shortcut'),
diff --git a/core/modules/shortcut/src/Form/SetCustomize.php b/core/modules/shortcut/src/Form/SetCustomize.php
index e778d8c..2d026b5 100644
--- a/core/modules/shortcut/src/Form/SetCustomize.php
+++ b/core/modules/shortcut/src/Form/SetCustomize.php
@@ -64,11 +64,11 @@ public function form(array $form, FormStateInterface $form_state) {
 
       $links['edit'] = array(
         'title' => t('Edit'),
-        'href' => "admin/config/user-interface/shortcut/link/$id",
+        'url' => $shortcut->urlInfo(),
       );
       $links['delete'] = array(
         'title' => t('Delete'),
-        'href' => "admin/config/user-interface/shortcut/link/$id/delete",
+        'url' => $shortcut->urlInfo('delete-form'),
       );
       $form['shortcuts']['links'][$id]['operations'] = array(
         '#type' => 'operations',
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index 56d4f59..7d39496 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\simpletest\TestDiscovery;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -238,7 +239,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $test_id
     $form['action']['return'] = array(
       '#type' => 'link',
       '#title' => $this->t('Return to list'),
-      '#href' => 'admin/config/development/testing',
+      '#url' => Url::fromRoute('simpletest.test_form'),
     );
 
     if (is_numeric($test_id)) {
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index da7e4c3..a86dd06 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -216,7 +216,8 @@ protected function info() {
       '#type' => 'link',
       '#title' => $this->t('Continue'),
       '#attributes' => array('class' => array('button', 'button--primary')),
-    ) + $url->toRenderArray();
+      '#url' => $url,
+    );
     return $build;
   }
 
@@ -620,12 +621,12 @@ public static function batchFinished($success, $results, $operations) {
   protected function helpfulLinks() {
     $links['front'] = array(
       'title' => $this->t('Front page'),
-      'href' => '<front>',
+      'url' => Url::fromRoute('<front>'),
     );
     if ($this->account->hasPermission('access administration pages')) {
       $links['admin-pages'] = array(
         'title' => $this->t('Administration pages'),
-        'href' => 'admin',
+        'url' => Url::fromRoute('system.admin'),
       );
     }
     return $links;
diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php
index 43ab34e..d634dd7 100644
--- a/core/modules/system/src/Controller/SystemController.php
+++ b/core/modules/system/src/Controller/SystemController.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Menu\MenuLinkTreeInterface;
 use Drupal\Core\Menu\MenuTreeParameters;
 use Drupal\Core\Theme\ThemeAccessCheck;
+use Drupal\Core\Url;
 use Drupal\system\SystemManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -242,8 +243,7 @@ public function themesPage() {
         if ($this->themeAccess->checkAccess($theme->getName())) {
           $theme->operations[] = array(
             'title' => $this->t('Settings'),
-            'route_name' => 'system.theme_settings_theme',
-            'route_parameters' => array('theme' => $theme->getName()),
+            'url' => Url::fromRoute('system.theme_settings_theme', ['theme' => $theme->getName()]),
             'attributes' => array('title' => $this->t('Settings for !theme theme', array('!theme' => $theme->info['name']))),
           );
         }
@@ -252,14 +252,14 @@ public function themesPage() {
             if ($theme->getName() != $admin_theme) {
               $theme->operations[] = array(
                 'title' => $this->t('Uninstall'),
-                'route_name' => 'system.theme_uninstall',
+                'url' => Url::fromRoute('system.theme_uninstall'),
                 'query' => $query,
                 'attributes' => array('title' => $this->t('Uninstall !theme theme', array('!theme' => $theme->info['name']))),
               );
             }
             $theme->operations[] = array(
               'title' => $this->t('Set as default'),
-              'route_name' => 'system.theme_set_default',
+              'url' => Url::fromRoute('system.theme_set_default'),
               'query' => $query,
               'attributes' => array('title' => $this->t('Set !theme as default theme', array('!theme' => $theme->info['name']))),
             );
@@ -269,13 +269,13 @@ public function themesPage() {
         else {
           $theme->operations[] = array(
             'title' => $this->t('Install'),
-            'route_name' => 'system.theme_install',
+            'url' => Url::fromRoute('system.theme_install'),
             'query' => $query,
             'attributes' => array('title' => $this->t('Install !theme theme', array('!theme' => $theme->info['name']))),
           );
           $theme->operations[] = array(
             'title' => $this->t('Install and set as default'),
-            'route_name' => 'system.theme_set_default',
+            'url' => Url::fromRoute('system.theme_set_default'),
             'query' => $query,
             'attributes' => array('title' => $this->t('Install !theme as default theme', array('!theme' => $theme->info['name']))),
           );
diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php
index c1b2b73..aaf73eb 100644
--- a/core/modules/system/src/Form/ModulesListForm.php
+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -22,6 +22,7 @@
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -258,7 +259,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) {
         $row['links']['help'] = array(
           '#type' => 'link',
           '#title' => $this->t('Help'),
-          '#href' => 'admin/help/' . $module->getName(),
+          '#url' => Url::fromRoute('help.page', ['name' => $module->getName()]),
           '#options' => array('attributes' => array('class' =>  array('module-link', 'module-link-help'), 'title' => $this->t('Help'))),
         );
       }
@@ -270,7 +271,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) {
       $row['links']['permissions'] = array(
         '#type' => 'link',
         '#title' => $this->t('Permissions'),
-        '#href' => 'admin/people/permissions',
+        '#url' => Url::fromRoute('user.admin_permissions'),
         '#options' => array('fragment' => 'module-' . $module->getName(), 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => $this->t('Configure permissions'))),
       );
     }
@@ -301,8 +302,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) {
         $row['links']['configure'] = array(
           '#type' => 'link',
           '#title' => $this->t('Configure'),
-          '#route_name' => $module->info['configure'],
-          '#route_parameters' => $route_parameters,
+          '#url' => Url::fromRoute($module->info['configure'], $route_parameters),
           '#options' => array(
             'attributes' => array(
               'class' => array('module-link', 'module-link-configure'),
diff --git a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
index 9757a2b..f3f27ab 100644
--- a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
+++ b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Common;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Url;
 use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
@@ -112,7 +113,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag generation without extra classes",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
         ),
         'expected' => '//div[@class="more-link"]/a[@href="http://drupal.org" and text()="More"]',
       ),
@@ -120,7 +121,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag generation with different link text",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#title' => 'More Titles',
         ),
         'expected' => '//div[@class="more-link"]/a[@href="http://drupal.org" and text()="More Titles"]',
@@ -129,7 +130,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag generation with attributes on wrapper",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#theme_wrappers' => array(
             'container' => array(
               '#attributes' => array(
@@ -145,16 +146,15 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag with a relative path",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'a/link',
+          '#url' => Url::fromRoute('router_test.1'),
         ),
-        'expected' => '//div[@class="more-link"]/a[@href="' . _url('a/link') . '" and text()="More"]',
+        'expected' => '//div[@class="more-link"]/a[@href="' . _url('router_test/test1') . '" and text()="More"]',
       ),
       array(
         'name' => "#type 'more_link' anchor tag with a route",
         'value' => array(
           '#type' => 'more_link',
-          '#route_name' => 'router_test.1',
-          '#route_parameters' => array(),
+          '#url' => Url::fromRoute('router_test.1'),
         ),
         'expected' => '//div[@class="more-link"]/a[@href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" and text()="More"]',
       ),
@@ -162,7 +162,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag with an absolute path",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'admin/content',
+          '#url' => Url::fromRoute('system.admin_content'),
           '#options' => array('absolute' => TRUE),
         ),
         'expected' => '//div[@class="more-link"]/a[@href="' . _url('admin/content', array('absolute' => TRUE)) . '" and text()="More"]',
@@ -171,7 +171,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag to the front page",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => '<front>',
+          '#url' => Url::fromRoute('<front>'),
         ),
         'expected' => '//div[@class="more-link"]/a[@href="' . _url('<front>') . '" and text()="More"]',
       ),
diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php
index d07085c..033845f 100644
--- a/core/modules/system/src/Tests/Common/RenderTest.php
+++ b/core/modules/system/src/Tests/Common/RenderTest.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
@@ -120,7 +121,7 @@ function testDrupalRenderBasics() {
             ),
           ),
           '#attributes' => array('id' => 'foo'),
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#title' => 'bar',
         ),
         'expected' => '<div class="baz"><a href="http://drupal.org" id="foo">bar</a></div>' . "\n",
@@ -131,7 +132,7 @@ function testDrupalRenderBasics() {
         'name' => '#theme_wrappers attribute disambiguation with undefined #theme attribute',
         'value' => array(
           '#type' => 'link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#title' => 'foo',
           '#theme_wrappers' => array(
             'container' => array(
diff --git a/core/modules/system/src/Tests/Common/RenderWebTest.php b/core/modules/system/src/Tests/Common/RenderWebTest.php
index 3e58fb0..5d7f36e 100644
--- a/core/modules/system/src/Tests/Common/RenderWebTest.php
+++ b/core/modules/system/src/Tests/Common/RenderWebTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Common;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -105,13 +106,13 @@ function testDrupalRenderFormElements() {
     $element = array(
       '#type' => 'link',
       '#title' => $this->randomMachineName(),
-      '#href' => $this->randomMachineName(),
+      '#url' => Url::fromRoute('common_test.destination'),
       '#options' => array(
         'absolute' => TRUE,
       ),
     );
     $this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', array(
-      ':href' => _url($element['#href'], array('absolute' => TRUE)),
+      ':href' => \Drupal::urlGenerator()->generateFromPath('common-test/destination', ['absolute' => TRUE]),
       ':title' => $element['#title'],
     ));
 
diff --git a/core/modules/system/src/Tests/Common/UrlTest.php b/core/modules/system/src/Tests/Common/UrlTest.php
index 3ac4bc4..488c31b 100644
--- a/core/modules/system/src/Tests/Common/UrlTest.php
+++ b/core/modules/system/src/Tests/Common/UrlTest.php
@@ -39,15 +39,10 @@ function testLinkXSS() {
     $sanitized_path = check_url(_url($path));
     $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by _l().', array('@path' => $path)));
 
-    // Test #type 'link'.
-    $link_array =  array(
-      '#type' => 'link',
-      '#title' => $this->randomMachineName(),
-      '#href' => $path,
-    );
-    $type_link = drupal_render($link_array);
+    // Test _url().
+    $link = _url($path);
     $sanitized_path = check_url(_url($path));
-    $this->assertTrue(strpos($type_link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', array('@path' => $path)));
+    $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', array('@path' => $path)));
   }
 
   /**
@@ -61,7 +56,7 @@ function testLinkAttributes() {
       '#options' => array(
         'language' => $language,
       ),
-      '#href' => 'http://drupal.org',
+      '#url' => Url::fromUri('http://drupal.org'),
       '#title' => 'bar',
     );
     $langcode = $language->id;
@@ -123,7 +118,7 @@ function testLinkAttributes() {
     $type_link = array(
       '#type' => 'link',
       '#title' => $this->randomMachineName(),
-      '#route_name' => '<current>',
+      '#url' => Url::fromRoute('<current>'),
       '#options' => array(
         'attributes' => array(
           'class' => array($class_theme),
@@ -150,7 +145,7 @@ function testLinkRenderArrayText() {
     $type_link_plain_array = array(
       '#type' => 'link',
       '#title' => 'foo',
-      '#href' => 'http://drupal.org',
+      '#url' => Url::fromUri('http://drupal.org'),
     );
     $type_link_plain = drupal_render($type_link_plain_array);
     $this->assertEqual($type_link_plain, $l);
@@ -159,7 +154,7 @@ function testLinkRenderArrayText() {
     $type_link_nested_array = array(
       '#type' => 'link',
       '#title' => array('#markup' => 'foo'),
-      '#href' => 'http://drupal.org',
+      '#url' => Url::fromUri('http://drupal.org'),
     );
     $type_link_nested = drupal_render($type_link_nested_array);
     $this->assertEqual($type_link_nested, $l);
diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/src/Tests/Theme/FunctionsTest.php
index c8d068c..9eeeb47 100644
--- a/core/modules/system/src/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/src/Tests/Theme/FunctionsTest.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Session\UserSession;
+use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -188,26 +189,20 @@ function testLinks() {
     $variables = array();
     $variables['attributes'] = array('id' => 'somelinks');
     $variables['links'] = array(
-      'a link' => array(
-        'title' => 'A <link>',
-        'href' => 'a/link',
-      ),
       'plain text' => array(
         'title' => 'Plain "text"',
       ),
       'front page' => array(
         'title' => 'Front page',
-        'href' => '<front>',
+        'url' => Url::fromRoute('<front>'),
       ),
       'router-test' => array(
         'title' => 'Test route',
-        'route_name' => 'router_test.1',
-        'route_parameters' => array(),
+        'url' => Url::fromRoute('router_test.1'),
       ),
       'query-test' => array(
         'title' => 'Query test route',
-        'route_name' => 'router_test.1',
-        'route_parameters' => array(),
+        'url' => Url::fromRoute('router_test.1'),
         'query' => array(
           'key' => 'value',
         )
@@ -216,7 +211,6 @@ function testLinks() {
 
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
-    $expected_links .= '<li class="a-link"><a href="' . _url('a/link') . '">' . String::checkPlain('A <link>') . '</a></li>';
     $expected_links .= '<li class="plain-text">' . String::checkPlain('Plain "text"') . '</li>';
     $expected_links .= '<li class="front-page"><a href="' . _url('<front>') . '">' . String::checkPlain('Front page') . '</a></li>';
     $expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . String::checkPlain('Test route') . '</a></li>';
@@ -250,15 +244,11 @@ function testLinks() {
     $this->assertThemeOutput('links', $variables, $expected);
 
     // Verify that passing attributes for the links work.
-    $variables['links']['a link']['attributes'] = array(
-      'class' => array('a/class'),
-    );
     $variables['links']['plain text']['attributes'] = array(
       'class' => array('a/class'),
     );
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
-    $expected_links .= '<li class="a-link"><a href="' . _url('a/link') . '" class="a/class">' . String::checkPlain('A <link>') . '</a></li>';
     $expected_links .= '<li class="plain-text"><span class="a/class">' . String::checkPlain('Plain "text"') . '</span></li>';
     $expected_links .= '<li class="front-page"><a href="' . _url('<front>') . '">' . String::checkPlain('Front page') . '</a></li>';
     $expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . String::checkPlain('Test route') . '</a></li>';
@@ -273,7 +263,6 @@ function testLinks() {
     $variables['set_active_class'] = TRUE;
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
-    $expected_links .= '<li class="a-link" data-drupal-link-system-path="a/link"><a href="' . _url('a/link') . '" class="a/class" data-drupal-link-system-path="a/link">' . String::checkPlain('A <link>') . '</a></li>';
     $expected_links .= '<li class="plain-text"><span class="a/class">' . String::checkPlain('Plain "text"') . '</span></li>';
     $expected_links .= '<li class="front-page" data-drupal-link-system-path="&lt;front&gt;"><a href="' . _url('<front>') . '" data-drupal-link-system-path="&lt;front&gt;">' . String::checkPlain('Front page') . '</a></li>';
     $expected_links .= '<li class="router-test" data-drupal-link-system-path="router_test/test1"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . String::checkPlain('Test route') . '</a></li>';
@@ -297,7 +286,7 @@ function testDrupalPreRenderLinks() {
       '#links' => array(
         'parent_link' => array(
           'title' => 'Parent link original',
-          'href' => 'parent-link-original',
+          'url' => Url::fromRoute('router_test.1'),
         ),
       ),
       'first_child' => array(
@@ -308,12 +297,12 @@ function testDrupalPreRenderLinks() {
           // one of the parent's links).
           'parent_link' => array(
             'title' => 'Parent link copy',
-            'href' => 'parent-link-copy',
+            'url' => Url::fromRoute('router_test.6'),
           ),
           // This should always be rendered.
           'first_child_link' => array(
             'title' => 'First child link',
-            'href' => 'first-child-link',
+            'url' => Url::fromRoute('router_test.7'),
           ),
         ),
       ),
@@ -323,7 +312,7 @@ function testDrupalPreRenderLinks() {
         '#links' => array(
           'second_child_link' => array(
             'title' => 'Second child link',
-            'href' => 'second-child-link',
+            'url' => Url::fromRoute('router_test.8'),
           ),
         ),
       ),
@@ -334,7 +323,7 @@ function testDrupalPreRenderLinks() {
         '#links' => array(
           'third_child_link' => array(
             'title' => 'Third child link',
-            'href' => 'third-child-link',
+            'url' => Url::fromRoute('router_test.9'),
           ),
         ),
         '#access' => FALSE,
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index f721626..2939ca8 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -412,12 +412,12 @@ function hook_menu_local_tasks(&$data, $route_name) {
     '#theme' => 'menu_local_action',
     '#link' => array(
       'title' => t('Add content'),
-      'href' => 'node/add',
-      'localized_options' => array(
+      'url' => Url::fromRoute('node.add_page', array(
         'attributes' => array(
           'title' => t('Add content'),
         ),
-      ),
+      )),
+      'localized_options' => ,
     ),
   );
 
@@ -426,7 +426,7 @@ function hook_menu_local_tasks(&$data, $route_name) {
     '#theme' => 'menu_local_task',
     '#link' => array(
       'title' => t('Example tab'),
-      'href' => 'node/add',
+      'url' => Url::fromRoute('node.add_page'),
       'localized_options' => array(
         'attributes' => array(
           'title' => t('Add content'),
@@ -2277,7 +2277,7 @@ function hook_system_themes_page_alter(&$theme_groups) {
       // Add a foo link to each list of theme operations.
       $theme->operations[] = array(
         'title' => t('Foo'),
-        'href' => 'admin/appearance/foo',
+        'url' => Url::fromRoute('system.themes_page'),
         'query' => array('theme' => $theme->getName())
       );
     }
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module
index 6127b2e..870a157 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.module
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module
@@ -11,6 +11,7 @@
 use Drupal\Core\Ajax\OpenModalDialogCommand;
 use Drupal\Core\Ajax\CloseDialogCommand;
 use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Url;
 
 /**
  * Menu callback: Returns an element suitable for use by
@@ -108,7 +109,7 @@ function ajax_test_dialog_contents() {
     'cancel' => array(
       '#type' => 'link',
       '#title' => 'Cancel',
-      '#href' => '',
+      '#url' => Url::fromRoute('<front>'),
       '#attributes' => array(
         // This is a special class to which JavaScript assigns dialog closing
         // behavior.
diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
index ac0399f..ab25fde 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\ajax_test\Controller;
 
+use Drupal\Core\Url;
+
 /**
  * Provides content for dialog tests.
  */
@@ -56,7 +58,7 @@ public function dialog() {
     $build['link'] = array(
       '#type' => 'link',
       '#title' => 'Link 1 (modal)',
-      '#href' => 'ajax-test/dialog-contents',
+      '#url' => Url::fromRoute('ajax_test.dialog_contents'),
       '#attributes' => array(
         'class' => array('use-ajax'),
         'data-accepts' => 'application/vnd.drupal-modal',
@@ -69,7 +71,7 @@ public function dialog() {
       '#links' => array(
         'link2' => array(
           'title' => 'Link 2 (modal)',
-          'href' => 'ajax-test/dialog-contents',
+          'url' => Url::fromRoute('ajax_test.dialog_contents'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-modal',
@@ -80,7 +82,7 @@ public function dialog() {
         ),
         'link3' => array(
           'title' => 'Link 3 (non-modal)',
-          'href' => 'ajax-test/dialog-contents',
+          'url' => Url::fromRoute('ajax_test.dialog_contents'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-dialog',
@@ -92,14 +94,14 @@ public function dialog() {
         ),
         'link4' => array(
           'title' => 'Link 4 (close non-modal if open)',
-          'href' => 'ajax-test/dialog-close',
+          'url' => Url::fromRoute('ajax_test.dialog_close'),
           'attributes' => array(
             'class' => array('use-ajax'),
           ),
         ),
         'link5' => array(
           'title' => 'Link 5 (form)',
-          'href' => 'ajax-test/dialog-form',
+          'url' => Url::fromRoute('ajax_test.dialog_form'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-modal',
@@ -107,7 +109,7 @@ public function dialog() {
         ),
         'link6' => array(
           'title' => 'Link 6 (entity form)',
-          'href' => 'admin/structure/contact/add',
+          'url' => Url::fromRoute('contact.form_add'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-modal',
@@ -119,7 +121,7 @@ public function dialog() {
         ),
         'link7' => array(
           'title' => 'Link 7 (non-modal, no target)',
-          'href' => 'ajax-test/dialog-contents',
+          'url' => Url::fromRoute('ajax_test.dialog_contents'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-dialog',
diff --git a/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php b/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
index 161aabe..cf82dba 100644
--- a/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
+++ b/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
@@ -8,6 +8,7 @@
 namespace Drupal\common_test\Controller;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Response;
 
 /**
@@ -25,7 +26,7 @@ public function typeLinkActiveClass() {
       'no_query' => array(
         '#type' => 'link',
         '#title' => t('Link with no query string'),
-        '#route_name' => '<current>',
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'set_active_class' => TRUE,
         ),
@@ -33,7 +34,7 @@ public function typeLinkActiveClass() {
       'with_query' => array(
         '#type' => 'link',
         '#title' => t('Link with a query string'),
-        '#route_name' => '<current>',
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'query' => array(
             'foo' => 'bar',
@@ -45,7 +46,7 @@ public function typeLinkActiveClass() {
       'with_query_reversed' => array(
         '#type' => 'link',
         '#title' => t('Link with the same query string in reverse order'),
-        '#route_name' => '<current>',
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'query' => array(
             'one' => 'two',
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index e17eb01..e158350 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -14,6 +14,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Url;
 
 /**
  * Filter that limits test entity list to revisable ones.
@@ -384,11 +385,19 @@ function entity_test_entity_predelete(EntityInterface $entity) {
  * Implements hook_entity_operation_alter().
  */
 function entity_test_entity_operation_alter(array &$operations, EntityInterface $entity) {
-  $operations['test_operation'] = array(
-    'title' => format_string('Test Operation: @label', array('@label' => $entity->label())),
-    'href' => $entity->url() . '/test_operation',
-    'weight' => 50,
-  );
+  $valid_entity_type_ids = [
+    'user_role',
+    'block',
+  ];
+  if (in_array($entity->getEntityTypeId(), $valid_entity_type_ids)) {
+    if (\Drupal::service('router.route_provider')->getRouteByName("entity.{$entity->getEntityTypeId()}.test_operation")) {
+      $operations['test_operation'] = array(
+        'title' => format_string('Test Operation: @label', array('@label' => $entity->label())),
+        'url' => Url::fromRoute("entity.{$entity->getEntityTypeId()}.test_operation", [$entity->getEntityTypeId() => $entity->id()]),
+        'weight' => 50,
+      );
+    }
+  }
 }
 
 /**
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
index e3d0adc..34cbc56 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
+++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
@@ -76,6 +76,19 @@ entity.entity_test.list_empty:
   requirements:
     _access: 'TRUE'
 
+entity.block.test_operation:
+  path: '/admin/structure/block/manage/{block}/test_operation'
+  defaults:
+    _entity_view: 'entity_test'
+  requirements:
+    _access: 'TRUE'
+
+entity.user_role.test_operation:
+  path: '/admin/people/roles/manage/{user_role}/test_operation'
+  defaults:
+    _entity_view: 'entity_test'
+  requirements:
+    _access: 'TRUE'
 
 route_callbacks:
   - '\Drupal\entity_test\Routing\EntityTestRoutes::routes'
diff --git a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
index a02be18..b4e4393 100644
--- a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
+++ b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\theme_test\EventSubscriber;
 
+use Drupal\Core\Url;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -44,7 +45,7 @@ public function onRequest(GetResponseEvent $event) {
       // returning output and theming the page as a whole.
       $more_link = array(
         '#type' => 'more_link',
-        '#href' => 'user',
+        '#url' => Url::fromRoute('user.page'),
         '#attributes' => array('title' => 'Themed output generated in a KernelEvents::REQUEST listener'),
       );
       $GLOBALS['theme_test_output'] = drupal_render($more_link);
diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index 95e6178..ffe0ea1 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -225,7 +225,8 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
         '#prefix' => !empty($indentation) ? drupal_render($indentation) : '',
         '#type' => 'link',
         '#title' => $term->getName(),
-      ) + $term->urlInfo()->toRenderArray();
+        '#url' => $term->urlInfo(),
+      );
       if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
         $parent_fields = TRUE;
         $form['terms'][$key]['term']['tid'] = array(
diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index 856b73e..00b2e8f 100644
--- a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -42,11 +42,14 @@ public function viewElements(FieldItemListInterface $items) {
         $elements[$delta] = array(
           '#type' => 'link',
           '#title' => $term->getName(),
-        ) + $term->urlInfo()->toRenderArray();
+          '#url' => $term->urlInfo(),
+        );
 
         if (!empty($item->_attributes)) {
-          $elements[$delta]['#options'] += array('attributes' => array());
-          $elements[$delta]['#options']['attributes'] += $item->_attributes;
+          $options = $elements[$delta]['#url']->getOptions();
+          $options += array('attributes' => array());
+          $options['attributes'] += $item->_attributes;
+          $elements[$delta]['#url']->setOptions($options);
           // Unset field item attributes since they have been included in the
           // formatter output and should not be rendered in the field template.
           unset($item->_attributes);
diff --git a/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php b/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php
index ef45d4e..5d83b7c 100644
--- a/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php
+++ b/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'telephone_link' formatter.
@@ -78,7 +79,7 @@ public function viewElements(FieldItemListInterface $items) {
         // itself as title.
         '#title' => $title_setting ?: $item->value,
         // Prepend 'tel:' to the telephone number.
-        '#href' => 'tel:' . rawurlencode(preg_replace('/\s+/', '', $item->value)),
+        '#url' => Url::fromUri('tel:' . rawurlencode(preg_replace('/\s+/', '', $item->value))),
         '#options' => array('external' => TRUE),
       );
 
diff --git a/core/modules/toolbar/src/Element/ToolbarItem.php b/core/modules/toolbar/src/Element/ToolbarItem.php
index 18ade46..29efe32 100644
--- a/core/modules/toolbar/src/Element/ToolbarItem.php
+++ b/core/modules/toolbar/src/Element/ToolbarItem.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Render\Element\RenderElement;
+use Drupal\Core\Url;
 
 /**
  * Provides a toolbar item that is wrapped in markup for common styling.
@@ -33,7 +34,7 @@ public function getInfo() {
       'tab' => array(
         '#type' => 'link',
         '#title' => NULL,
-        '#href' => '',
+        '#url' => Url::fromRoute('<front>'),
       ),
     );
   }
diff --git a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
index 69dac7a..9d4e89f 100644
--- a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
+++ b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
@@ -17,7 +17,7 @@ function toolbar_test_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Test tab'),
-      '#href' => '',
+      '#url' => Url::fromRoute('<front>'),
       '#options' => array(
         'html' => FALSE,
         'attributes' => array(
diff --git a/core/modules/toolbar/toolbar.api.php b/core/modules/toolbar/toolbar.api.php
index ab9b75a..bce69ae 100644
--- a/core/modules/toolbar/toolbar.api.php
+++ b/core/modules/toolbar/toolbar.api.php
@@ -4,6 +4,7 @@
  * @file
  * Hooks provided by the toolbar module.
  */
+use Drupal\Core\Url;
 
 /**
  * @addtogroup hooks
@@ -72,7 +73,7 @@ function hook_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Home'),
-      '#href' => '<front>',
+      '#url' => Url::fromRoute('<front>'),
       '#options' => array(
         'attributes' => array(
           'title' => t('Home page'),
@@ -96,7 +97,7 @@ function hook_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Shopping cart'),
-      '#href' => '/cart',
+      '#url' => Url::fromRoute('cart'),
       '#options' => array(
         'html' => FALSE,
         'attributes' => array(
@@ -131,7 +132,7 @@ function hook_toolbar() {
       '#theme' => 'user_message_toolbar_tab',
       '#theme_wrappers' => array(),
       '#title' => t('Messages'),
-      '#href' => '/user/messages',
+      '#url' => Url::fromRoute('user.message'),
       '#options' => array(
         'attributes' => array(
           'title' => t('Messages'),
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index 2fbf64d..d7121d5 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -14,6 +14,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\String;
 use Drupal\user\Entity\Role;
+use Drupal\Core\Url;
 
 /**
  * Implements hook_help().
@@ -94,7 +95,7 @@ function toolbar_element_info() {
     'tab' => array(
       '#type' => 'link',
       '#title' => NULL,
-      '#href' => '',
+      '#url' => Url::fromRoute('<front>'),
     ),
   );
   return $elements;
@@ -179,7 +180,7 @@ function toolbar_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Back to site'),
-      '#href' => '<front>',
+      '#url' => Url::fromRoute('<front>'),
       '#attributes' => array(
         'title' => t('Return to site content'),
         'class' => array('toolbar-icon', 'toolbar-icon-escape-admin'),
@@ -218,7 +219,7 @@ function toolbar_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Manage'),
-      '#href' => 'admin',
+      '#url' => Url::fromRoute('system.admin'),
       '#attributes' => array(
         'title' => t('Admin menu'),
         'class' => array('toolbar-icon', 'toolbar-icon-menu'),
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 82aca31..a5c99e1 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1450,7 +1450,7 @@ function user_toolbar() {
     $links = array(
       'account' => array(
         'title' => t('View profile'),
-        'href' => 'user',
+        'url' => Url::fromRoute('user.page'),
         'html' => TRUE,
         'attributes' => array(
           'title' => t('User account'),
@@ -1458,7 +1458,7 @@ function user_toolbar() {
       ),
       'account_edit' => array(
         'title' => t('Edit profile'),
-        'href' => 'user/' . $user->id() . '/edit',
+        'url' => Url::fromRoute('entity.user.edit_form', ['user' => $user->id()]),
         'html' => TRUE,
         'attributes' => array(
           'title' => t('Edit user account'),
@@ -1466,7 +1466,7 @@ function user_toolbar() {
       ),
       'logout' => array(
         'title' => t('Log out'),
-        'href' => 'user/logout',
+        'url' => Url::fromRoute('user.logout'),
       ),
     );
   }
@@ -1474,7 +1474,7 @@ function user_toolbar() {
      $links = array(
       'login' => array(
         'title' => t('Log in'),
-        'href' => 'user',
+        'url' => Url::fromRoute('user.page'),
       ),
     );
   }
@@ -1484,7 +1484,7 @@ function user_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => $user->getUsername(),
-      '#href' => 'user',
+      '#url' => Url::fromRoute('user.page'),
       '#attributes' => array(
         'title' => t('My account'),
         'class' => array('toolbar-icon', 'toolbar-icon-user'),
diff --git a/core/modules/views/src/Plugin/views/field/Links.php b/core/modules/views/src/Plugin/views/field/Links.php
index 7f699f7..3516346 100644
--- a/core/modules/views/src/Plugin/views/field/Links.php
+++ b/core/modules/views/src/Plugin/views/field/Links.php
@@ -8,6 +8,7 @@
 namespace Drupal\views\Plugin\views\field;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url as UrlObject;
 
 /**
  * A abstract handler which provides a collection of links.
@@ -79,7 +80,7 @@ protected function getLinks() {
       $path = strip_tags(decode_entities(strtr($path, $tokens)));
 
       $links[$field] = array(
-        'href' => $path,
+        'url' => UrlObject::fromUri('base://' . $path),
         'title' => $title,
       );
       if (!empty($this->options['destination'])) {
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 02f0175..f089f56 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -1049,7 +1049,7 @@ function template_preprocess_views_mini_pager(&$variables) {
     $li_previous = array(
       '#type' => 'link',
       '#title' => $tags[1],
-      '#route_name' => '<current>',
+      '#url' => Url::fromRoute('<current>'),
       '#options' => array(
         'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] - 1),
         'attributes' => array(
@@ -1072,7 +1072,7 @@ function template_preprocess_views_mini_pager(&$variables) {
     $li_next = array(
       '#type' => 'link',
       '#title' => $tags[3],
-      '#route_name' => '<current>',
+      '#url' => Url::fromRoute('<current>'),
       '#options' => array(
         'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] + 1),
         'attributes' => array(
diff --git a/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php b/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
index c6e24db..72dae7c 100644
--- a/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
+++ b/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
@@ -8,6 +8,7 @@
 namespace Drupal\views_ui\Form\Ajax;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\views_ui\ViewUI;
 
 /**
@@ -120,7 +121,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         'link' => array(
           '#type' => 'link',
           '#title' => '<span>' . $this->t('Remove') . '</span>',
-          '#href' => 'javascript:void()',
+          '#url' => Url::fromRoute('<none>'),
           '#options' => array(
             'html' => TRUE,
           ),
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index e621513..2ca0dca 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -427,7 +427,7 @@ public function getDisplayDetails($view, $display) {
               '#type' => 'link',
               '#title' => $this->t('View !display_title', array('!display_title' => $display_title)),
               '#options' => array('alt' => array($this->t("Go to the real page for this display"))),
-              '#href' => $path,
+              '#url' => Url::fromUri("base://$path"),
               '#prefix' => '<li class="view">',
               "#suffix" => '</li>',
             );
@@ -695,12 +695,12 @@ public function renderDisplayTop(ViewUI $view) {
       '#links' => array(
         'edit-details' => array(
           'title' => $this->t('Edit view name/description'),
-          'href' => "admin/structure/views/nojs/edit-details/{$view->id()}/$display_id",
+          'url' => Url::fromRoute('views_ui.form_edit_details', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display_id]),
           'attributes' => array('class' => array('views-ajax-link')),
         ),
         'analyze' => array(
           'title' => $this->t('Analyze view'),
-          'href' => "admin/structure/views/nojs/analyze/{$view->id()}/$display_id",
+          'url' => Url::fromRoute('views_ui.form_analyze', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display_id]),
           'attributes' => array('class' => array('views-ajax-link')),
         ),
         'duplicate' => array(
@@ -708,7 +708,7 @@ public function renderDisplayTop(ViewUI $view) {
         ) + $view->urlInfo('duplicate-form')->toArray(),
         'reorder' => array(
           'title' => $this->t('Reorder displays'),
-          'href' => "admin/structure/views/nojs/reorder-displays/{$view->id()}/$display_id",
+          'url' => Url::fromRoute('views_ui.form_reorder_displays', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display_id]),
           'attributes' => array('class' => array('views-ajax-link')),
         ),
       ),
@@ -734,7 +734,7 @@ public function renderDisplayTop(ViewUI $view) {
       else {
         $element['extra_actions']['#links']['delete'] = array(
           'title' => $this->t('Delete view'),
-          'href' => "admin/structure/views/view/{$view->id()}/delete",
+          'url' => $view->urlInfo('delete-form'),
         );
       }
     }
@@ -944,7 +944,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
 
     $build['#name'] = $build['#title'] = $types[$type]['title'];
 
-    $rearrange_url = "admin/structure/views/nojs/rearrange/{$view->id()}/{$display['id']}/$type";
+    $rearrange_url = Url::fromRoute('views_ui.form_rearrange', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display['id'], 'type' => $type]);
     $class = 'icon compact rearrange';
 
     // Different types now have different rearrange forms, so we use this switch
@@ -953,7 +953,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
       case 'filter':
         // The rearrange form for filters contains the and/or UI, so override
         // the used path.
-        $rearrange_url = "admin/structure/views/nojs/rearrange-filter/{$view->id()}/{$display['id']}";
+        $rearrange_url = Url::fromRoute('views_ui.form_rearrange_filter', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display['id']]);
         // TODO: Add another class to have another symbol for filter rearrange.
         $class = 'icon compact rearrange';
         break;
@@ -993,7 +993,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
 
     $actions['add'] = array(
       'title' => $add_text,
-      'href' => "admin/structure/views/nojs/add-handler/{$view->id()}/{$display['id']}/$type",
+      'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display['id'], 'type' => $type]),
       'attributes' => array('class' => array('icon compact add', 'views-ajax-link'), 'id' => 'views-add-' . $type),
       'html' => TRUE,
     );
@@ -1003,7 +1003,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
 
       $actions['rearrange'] = array(
         'title' => $rearrange_text,
-        'href' => $rearrange_url,
+        'url' => $rearrange_url,
         'attributes' => array('class' => array($class, 'views-ajax-link'), 'id' => 'views-rearrange-' . $type),
         'html' => TRUE,
       );
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 3960294..ca61352 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
 use Drupal\views\Views;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewStorageInterface;
@@ -180,13 +181,13 @@ function views_ui_view_preview_section_handler_links(ViewExecutable $view, $type
     $field_name = $handler->adminLabel(TRUE);
     $links[$type . '-edit-' . $id] = array(
       'title' => t('Edit @section', array('@section' => $field_name)),
-      'href' => "admin/structure/views/nojs/handler/{$view->storage->id()}/{$display['id']}/$type/$id",
+      'url' => Url::fromRoute('views_ui.form_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type, 'id' => $id]),
       'attributes' => array('class' => array('views-ajax-link')),
     );
   }
   $links[$type . '-add'] = array(
     'title' => t('Add new'),
-    'href' => "admin/structure/views/nojs/add-handler/{$view->storage->id()}/{$display['id']}/$type",
+    'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
     'attributes' => array('class' => array('views-ajax-link')),
   );
 
@@ -201,7 +202,7 @@ function views_ui_view_preview_section_display_category_links(ViewExecutable $vi
   $links = array(
     $type . '-edit' => array(
       'title' => t('Edit @section', array('@section' => $title)),
-      'href' => "admin/structure/views/nojs/display/{$view->storage->id()}/{$display['id']}/$type",
+      'url' => Url::fromRoute('views_ui.form_display', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
       'attributes' => array('class' => array('views-ajax-link')),
     ),
   );
diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc
index 80f2f45..4528490 100644
--- a/core/modules/views_ui/views_ui.theme.inc
+++ b/core/modules/views_ui/views_ui.theme.inc
@@ -9,6 +9,7 @@
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Url;
 
 /**
  * Prepares variables for Views UI display tab setting templates.
@@ -283,7 +284,7 @@ function template_preprocess_views_ui_rearrange_filter_form(&$variables) {
 
         $remove_link = array(
           '#type' => 'link',
-          '#href' => '',
+          '#url' => Url::fromRoute('<none>'),
           '#title' => '<span>' . t('Remove') . '</span>',
           '#weight' => '1',
           '#options' => array(
diff --git a/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php b/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
index 176d5b4..842304c 100644
--- a/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
@@ -47,7 +47,7 @@ public function testCancelLinkRoute() {
       ->method('getCancelUrl')
       ->will($this->returnValue($cancel_route));
     $link = ConfirmFormHelper::buildCancelLink($form, new Request());
-    $this->assertSame($route_name, $link['#route_name']);
+    $this->assertEquals(Url::fromRoute($route_name), $link['#url']);
   }
 
   /**
@@ -65,14 +65,13 @@ public function testCancelLinkRouteWithParams() {
         'absolute' => TRUE,
       ),
     );
+    $expected = Url::fromRoute($cancel_route['route_name'], $cancel_route['route_parameters'], $cancel_route['options']);
     $form = $this->getMock('Drupal\Core\Form\ConfirmFormInterface');
     $form->expects($this->any())
       ->method('getCancelUrl')
-      ->will($this->returnValue(new Url($cancel_route['route_name'], $cancel_route['route_parameters'], $cancel_route['options'])));
+      ->will($this->returnValue($expected));
     $link = ConfirmFormHelper::buildCancelLink($form, new Request());
-    $this->assertSame($cancel_route['route_name'], $link['#route_name']);
-    $this->assertSame($cancel_route['route_parameters'], $link['#route_parameters']);
-    $this->assertSame($cancel_route['options'], $link['#options']);
+    $this->assertEquals($expected, $link['#url']);
   }
 
   /**
@@ -94,9 +93,7 @@ public function testCancelLinkRouteWithUrl() {
       ->method('getCancelUrl')
       ->will($this->returnValue($cancel_route));
     $link = ConfirmFormHelper::buildCancelLink($form, new Request());
-    $this->assertSame($cancel_route->getRouteName(), $link['#route_name']);
-    $this->assertSame($cancel_route->getRouteParameters(), $link['#route_parameters']);
-    $this->assertSame($cancel_route->getOptions(), $link['#options']);
+    $this->assertSame($cancel_route, $link['#url']);
   }
 
   /**
@@ -108,7 +105,7 @@ public function testCancelLinkDestination() {
     $query = array('destination' => 'baz');
     $form = $this->getMock('Drupal\Core\Form\ConfirmFormInterface');
     $link = ConfirmFormHelper::buildCancelLink($form, new Request($query));
-    $this->assertSame($query['destination'], $link['#href']);
+    $this->assertSame('base://' . $query['destination'], $link['#url']->getUri());
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index cf0496f..75c4768 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -350,9 +350,7 @@ public function testAccess($access) {
    */
   public function testRenderAccess($access) {
     $element = array(
-      '#route_name' => 'entity.node.canonical',
-      '#route_parameters' => ['node' => 3],
-      '#options' => [],
+      '#url' => Url::fromRoute('entity.node.canonical', ['node' => 3]),
     );
     $this->container->set('current_user', $this->getMock('Drupal\Core\Session\AccountInterface'));
     $this->container->set('access_manager', $this->getMockAccessManager($access));
diff --git a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
index 03b3375..d24e1f0 100644
--- a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
@@ -47,7 +47,11 @@ protected function setUp() {
 
     $this->requestStack = new RequestStack();
     $this->configFactory = $this->getConfigFactoryStub(['system.filter' => []]);
-    $this->unroutedUrlAssembler = new UnroutedUrlAssembler($this->requestStack, $this->configFactory);
+    $path_processor = $this->getMock('Drupal\Core\PathProcessor\OutboundPathProcessorInterface');
+    $path_processor->expects($this->any())
+      ->method('processOutbound')
+      ->willReturnArgument(0);
+    $this->unroutedUrlAssembler = new UnroutedUrlAssembler($path_processor, $this->requestStack, $this->configFactory);
   }
 
   /**
