diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index 33f91fc..193a121 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -169,7 +169,7 @@ protected function setExternal() {
     $this->path = $this->routeName;
 
     // Set empty route name and parameters.
-    $this->routeName = '';
+    $this->routeName = NULL;
     $this->routeParameters = array();
 
     return $this;
@@ -291,6 +291,15 @@ public function setOption($name, $value) {
   }
 
   /**
+   * Returns the path of the URL if it is external.
+   *
+   * @return string
+   */
+  public function getPath() {
+    return $this->path;
+  }
+
+  /**
    * Sets the absolute value for this Url.
    *
    * @param bool $absolute
@@ -308,7 +317,7 @@ public function setAbsolute($absolute = TRUE) {
    */
   public function toString() {
     if ($this->isExternal()) {
-      return $this->urlGenerator()->generateFromPath($this->path, $this->getOptions());
+      return $this->urlGenerator()->generateFromPath($this->getPath(), $this->getOptions());
     }
 
     return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions());
@@ -321,11 +330,19 @@ public function toString() {
    *   An associative array containing all the properties of the route.
    */
   public function toArray() {
-    return array(
-      'route_name' => $this->getRouteName(),
-      'route_parameters' => $this->getRouteParameters(),
-      'options' => $this->getOptions(),
-    );
+    if ($this->external) {
+      return array(
+        'path' => $this->getPath(),
+        'options' => $this->getOptions(),
+      );
+    }
+    else {
+      return array(
+        'route_name' => $this->getRouteName(),
+        'route_parameters' => $this->getRouteParameters(),
+        'options' => $this->getOptions(),
+      );
+    }
   }
 
   /**
@@ -335,11 +352,19 @@ public function toArray() {
    *   An associative array suitable for a render array.
    */
   public function toRenderArray() {
-    return array(
-      '#route_name' => $this->getRouteName(),
-      '#route_parameters' => $this->getRouteParameters(),
-      '#options' => $this->getOptions(),
-    );
+    if ($this->external) {
+      return array(
+        '#href' => $this->getPath(),
+        '#options' => $this->getOptions(),
+      );
+    }
+    else {
+      return array(
+        '#route_name' => $this->getRouteName(),
+        '#route_parameters' => $this->getRouteParameters(),
+        '#options' => $this->getOptions(),
+      );
+    }
   }
 
   /**
diff --git a/core/modules/link/lib/Drupal/link/LinkItemInterface.php b/core/modules/link/lib/Drupal/link/LinkItemInterface.php
new file mode 100644
index 0000000..0a1e55d
--- /dev/null
+++ b/core/modules/link/lib/Drupal/link/LinkItemInterface.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * Contains \Drupal\link\LinkItemInterface.
+ */
+
+namespace Drupal\link;
+
+use Drupal\Core\Field\ConfigFieldItemInterface;
+
+/**
+ * Interface for the link field item.
+ */
+interface LinkItemInterface extends ConfigFieldItemInterface {
+
+  /**
+   * Specifies whether the field supports only internal URLs.
+   */
+  const LINK_INTERNAL = 0x01;
+
+  /**
+   * Specifies whether the field supports only external URLs.
+   */
+  const LINK_EXTERNAL = 0x10;
+
+  /**
+   * Specifies whether the field supports both internal and external URLs.
+   */
+  const LINK_GENERIC = 0x11;
+
+  /**
+   * Determines if a link is external.
+   *
+   * @return bool
+   *   TRUE if the link is external, FALSE otherwise.
+   */
+  public function isExternal();
+
+}
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
index e97fab3..01052db 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -7,11 +7,11 @@
 
 namespace Drupal\link\Plugin\Field\FieldFormatter;
 
-use Drupal\Component\Utility\Url;
 use Drupal\Component\Utility\String;
 use Drupal\Core\Field\FieldItemListInterface;
-use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Url;
+use Drupal\link\LinkItemInterface;
 
 /**
  * Plugin implementation of the 'link' formatter.
@@ -122,7 +122,8 @@ public function viewElements(FieldItemListInterface $items) {
 
     foreach ($items as $delta => $item) {
       // By default use the full URL as the link text.
-      $link_title = $item->url;
+      $url = $this->buildUrl($item);
+      $link_title = $url->toString();
 
       // If the title field value is available, use it for the link text.
       if (empty($settings['url_only']) && !empty($item->title)) {
@@ -142,13 +143,10 @@ public function viewElements(FieldItemListInterface $items) {
         );
       }
       else {
-        $link = $this->buildLink($item);
         $element[$delta] = array(
           '#type' => 'link',
           '#title' => $link_title,
-          '#href' => $link['path'],
-          '#options' => $link['options'],
-        );
+        ) + $url->toRenderArray();
       }
     }
 
@@ -156,41 +154,36 @@ public function viewElements(FieldItemListInterface $items) {
   }
 
   /**
-   * Builds the link information for a link field item.
+   * Builds the \Drupal\Core\Url object for a link field item.
    *
-   * @param \Drupal\Core\Field\FieldItemInterface $item
+   * @param \Drupal\link\LinkItemInterface $item
    *   The link field item being rendered.
    *
-   * @return array
-   *   An array with the following key/value pairs:
-   *   - 'path': a string suitable for the $path parameter in l().
-   *   - 'options': an array suitable for the $options parameter in l().
+   * @return \Drupal\Core\Url
+   *   An Url object.
    */
-  protected function buildLink(FieldItemInterface $item) {
+  protected function buildUrl(LinkItemInterface $item) {
     $settings = $this->getSettings();
-
-    // Split out the link into the parts required for url(): path and options.
-    $parsed_url = Url::parse($item->url);
-    $result = array(
-      'path' => $parsed_url['path'],
-      'options' => array(
-        'query' => $parsed_url['query'],
-        'fragment' => $parsed_url['fragment'],
-        'attributes' => $item->attributes,
-      ),
-    );
+    $options = $item->options;
 
     // Add optional 'rel' attribute to link options.
     if (!empty($settings['rel'])) {
-      $result['options']['attributes']['rel'] = $settings['rel'];
+      $options['attributes']['rel'] = $settings['rel'];
     }
     // Add optional 'target' attribute to link options.
     if (!empty($settings['target'])) {
-      $result['options']['attributes']['target'] = $settings['target'];
+      $options['attributes']['target'] = $settings['target'];
     }
 
-    return $result;
+    if ($item->isExternal()) {
+      $url = Url::createFromPath($item->url);
+      $url->setOptions($options);
+    }
+    else {
+      $url = new Url($item->route_name, (array) $item->route_parameters, (array) $options);
+    }
+
+    return $url;
   }
 
 }
-
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php
index 4392123..9ac9282 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php
@@ -42,7 +42,8 @@ public function viewElements(FieldItemListInterface $items) {
 
     foreach ($items as $delta => $item) {
       // By default use the full URL as the link text.
-      $link_title = $item->url;
+      $url = $this->buildUrl($item);
+      $link_title = $url->toString();
 
       // If the link text field value is available, use it for the text.
       if (empty($settings['url_only']) && !empty($item->title)) {
@@ -58,19 +59,17 @@ public function viewElements(FieldItemListInterface $items) {
       if (empty($item->title)) {
         $link_title = NULL;
       }
-      $url_title = $item->url;
+      $url_title = $url->toString();
       if (!empty($settings['trim_length'])) {
         $link_title = truncate_utf8($link_title, $settings['trim_length'], FALSE, TRUE);
-        $url_title = truncate_utf8($item->url, $settings['trim_length'], FALSE, TRUE);
+        $url_title = truncate_utf8($url_title, $settings['trim_length'], FALSE, TRUE);
       }
 
-      $link = $this->buildLink($item);
       $element[$delta] = array(
         '#theme' => 'link_formatter_link_separate',
         '#title' => $link_title,
         '#url_title' => $url_title,
-        '#href' => $link['path'],
-        '#options' => $link['options'],
+        '#url' => $url,
       );
     }
     return $element;
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
index 17853c3..105a358 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\link\Plugin\Field\FieldType;
 
+use Drupal\Component\Utility\Url as UrlHelper;
 use Drupal\Core\Field\ConfigFieldItemBase;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\TypedData\MapDataDefinition;
+use Drupal\link\LinkItemInterface;
 
 /**
  * Plugin implementation of the 'link' field type.
@@ -18,28 +20,35 @@
  * @FieldType(
  *   id = "link",
  *   label = @Translation("Link"),
- *   description = @Translation("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."),
+ *   description = @Translation("Stores a URL string, optional varchar link text, and optional blob of options to assemble a link."),
  *   instance_settings = {
+ *     "url_type" = \Drupal\link\LinkItemInterface::LINK_GENERIC,
  *     "title" = "1"
  *   },
  *   default_widget = "link_default",
  *   default_formatter = "link"
  * )
  */
-class LinkItem extends ConfigFieldItemBase {
+class LinkItem extends ConfigFieldItemBase implements LinkItemInterface {
 
   /**
    * {@inheritdoc}
    */
   public static function propertyDefinitions(FieldDefinitionInterface $field_definition) {
-    $properties['url'] = DataDefinition::create('uri')
+    $properties['url'] = DataDefinition::create('string')
       ->setLabel(t('URL'));
 
     $properties['title'] = DataDefinition::create('string')
       ->setLabel(t('Link text'));
 
-    $properties['attributes'] = MapDataDefinition::create()
-      ->setLabel(t('Attributes'));
+    $properties['route_name'] = DataDefinition::create('string')
+      ->setLabel(t('Route name'));
+
+    $properties['route_parameters'] = MapDataDefinition::create()
+      ->setLabel(t('Route parameters'));
+
+    $properties['options'] = MapDataDefinition::create()
+      ->setLabel(t('Options'));
 
     return $properties;
   }
@@ -62,8 +71,21 @@ public static function schema(FieldDefinitionInterface $field_definition) {
           'length' => 255,
           'not null' => FALSE,
         ),
-        'attributes' => array(
-          'description' => 'Serialized array of attributes for the link.',
+        'route_name' => array(
+          'description' => 'The machine name of a defined Route this link represents.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => FALSE,
+        ),
+        'route_parameters' => array(
+          'description' => 'Serialized array of route parameters of the link.',
+          'type' => 'blob',
+          'size' => 'big',
+          'not null' => FALSE,
+          'serialize' => TRUE,
+        ),
+        'options' => array(
+          'description' => 'Serialized array of options for the link.',
           'type' => 'blob',
           'size' => 'big',
           'not null' => FALSE,
@@ -79,6 +101,17 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   public function instanceSettingsForm(array $form, array &$form_state) {
     $element = array();
 
+    $element['url_type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Allowed url type'),
+      '#default_value' => $this->getSetting('url_type'),
+      '#options' => array(
+        static::LINK_INTERNAL => t('Internal urls only'),
+        static::LINK_EXTERNAL => t('External urls only'),
+        static::LINK_GENERIC => t('Both internal and external urls'),
+      ),
+    );
+
     $element['title'] = array(
       '#type' => 'radios',
       '#title' => t('Allow link text'),
@@ -97,9 +130,16 @@ public function instanceSettingsForm(array $form, array &$form_state) {
    * {@inheritdoc}
    */
   public function preSave() {
-    // Trim any spaces around the URL and link text.
-    $this->url = trim($this->url);
+    // Trim any spaces around the link text.
     $this->title = trim($this->title);
+
+    // Split out the link 'query' and 'fragment' parts.
+    $parsed_url = UrlHelper::parse(trim($this->url));
+    $this->url = $parsed_url['path'];
+    $this->options = array(
+      'query' => $parsed_url['query'],
+      'fragment' => $parsed_url['fragment'],
+    ) + $this->options;
   }
 
   /**
@@ -110,4 +150,12 @@ public function isEmpty() {
     return $value === NULL || $value === '';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isExternal() {
+    // External URLs don't have a route_name value.
+    return empty($this->route_name);
+  }
+
 }
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php
index dc10574..4cfecef 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php
@@ -7,8 +7,14 @@
 
 namespace Drupal\link\Plugin\Field\FieldWidget;
 
+use Drupal\Component\Utility\Url as UrlHelper;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
+use Drupal\Core\ParamConverter\ParamNotConvertedException;
+use Drupal\Core\Routing\MatchingRouteNotFoundException;
+use Drupal\Core\Url;
+use Drupal\link\LinkItemInterface;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Plugin implementation of the 'link' widget.
@@ -31,17 +37,44 @@ class LinkWidget extends WidgetBase {
    * {@inheritdoc}
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
+    $url_type = $this->getFieldSetting('url_type');
+
+    $default_url_value = NULL;
+    if (isset($items[$delta]->url)) {
+      $url = Url::createFromPath($items[$delta]->url);
+      $url->setOptions($items[$delta]->options);
+      $default_url_value = ltrim($url->toString(), '/');
+    }
     $element['url'] = array(
       '#type' => 'url',
-      '#title' => t('URL'),
+      '#title' => $this->t('URL'),
       '#placeholder' => $this->getSetting('placeholder_url'),
-      '#default_value' => isset($items[$delta]->url) ? $items[$delta]->url : NULL,
+      '#default_value' => $default_url_value,
       '#maxlength' => 2048,
       '#required' => $element['#required'],
     );
+
+    // If the field is configured to allows internal paths, it cannot use the
+    // 'url' form element and we have to do the validation ourselves.
+    if ($url_type & LinkItemInterface::LINK_INTERNAL) {
+      $element['url']['#type'] = 'textfield';
+      $element['#element_validate'][] = array($this, 'validateUrl');
+    }
+
+    // If the field is configured to allow only internal paths, add a useful
+    // element prefix.
+    if ($url_type == LinkItemInterface::LINK_INTERNAL) {
+      $element['url']['#field_prefix'] = \Drupal::url('<front>', array(), array('absolute' => TRUE));
+    }
+    // If the field is configured to allow both internal and external paths,
+    // show a useful description.
+    elseif ($url_type == LinkItemInterface::LINK_GENERIC) {
+      $element['url']['#description'] = $this->t('This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org'));
+    }
+
     $element['title'] = array(
       '#type' => 'textfield',
-      '#title' => t('Link text'),
+      '#title' => $this->t('Link text'),
       '#placeholder' => $this->getSetting('placeholder_title'),
       '#default_value' => isset($items[$delta]->title) ? $items[$delta]->title : NULL,
       '#maxlength' => 255,
@@ -52,7 +85,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     // settings cannot be saved otherwise.
     $is_field_edit_form = ($element['#entity'] === NULL);
     if (!$is_field_edit_form && $this->getFieldSetting('title') == DRUPAL_REQUIRED) {
-      $element['#element_validate'] = array(array($this, 'validateTitle'));
+      $element['#element_validate'][] = array($this, 'validateTitle');
     }
 
     // Exposing the attributes array in the widget is left for alternate and more
@@ -60,7 +93,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     $element['attributes'] = array(
       '#type' => 'value',
       '#tree' => TRUE,
-      '#value' => !empty($items[$delta]->attributes) ? $items[$delta]->attributes : array(),
+      '#value' => !empty($items[$delta]->options['attributes']) ? $items[$delta]->options['attributes'] : array(),
       '#attributes' => array('class' => array('link-field-widget-attributes')),
     );
 
@@ -83,15 +116,15 @@ public function settingsForm(array $form, array &$form_state) {
 
     $elements['placeholder_url'] = array(
       '#type' => 'textfield',
-      '#title' => t('Placeholder for URL'),
+      '#title' => $this->t('Placeholder for URL'),
       '#default_value' => $this->getSetting('placeholder_url'),
-      '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
+      '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
     );
     $elements['placeholder_title'] = array(
       '#type' => 'textfield',
-      '#title' => t('Placeholder for link text'),
+      '#title' => $this->t('Placeholder for link text'),
       '#default_value' => $this->getSetting('placeholder_title'),
-      '#description' => t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
+      '#description' => $this->t('Text that will be shown inside the field until a value is entered. This hint is usually a sample value or a brief description of the expected format.'),
       '#states' => array(
         'invisible' => array(
           ':input[name="instance[settings][title]"]' => array('value' => DRUPAL_DISABLED),
@@ -111,31 +144,91 @@ public function settingsSummary() {
     $placeholder_title = $this->getSetting('placeholder_title');
     $placeholder_url = $this->getSetting('placeholder_url');
     if (empty($placeholder_title) && empty($placeholder_url)) {
-      $summary[] = t('No placeholders');
+      $summary[] = $this->t('No placeholders');
     }
     else {
       if (!empty($placeholder_title)) {
-        $summary[] = t('Title placeholder: @placeholder_title', array('@placeholder_title' => $placeholder_title));
+        $summary[] = $this->t('Title placeholder: @placeholder_title', array('@placeholder_title' => $placeholder_title));
       }
       if (!empty($placeholder_url)) {
-        $summary[] = t('URL placeholder: @placeholder_url', array('@placeholder_url' => $placeholder_url));
+        $summary[] = $this->t('URL placeholder: @placeholder_url', array('@placeholder_url' => $placeholder_url));
       }
     }
 
     return $summary;
   }
 
-
   /**
-   * Form element validation handler for link_field_widget_form().
+   * Form element validation handler; Validates the title property.
    *
    * Conditionally requires the link title if a URL value was filled in.
    */
-  function validateTitle(&$element, &$form_state, $form) {
+  public function validateTitle(&$element, &$form_state, $form) {
     if ($element['url']['#value'] !== '' && $element['title']['#value'] === '') {
       $element['title']['#required'] = TRUE;
-      form_error($element['title'], $form_state, t('!name field is required.', array('!name' => $element['title']['#title'])));
+      \Drupal::formBuilder()->setError($element['title'], $form_state, $this->t('!name field is required.', array('!name' => $element['title']['#title'])));
     }
   }
+
+  /**
+   * Form element validation handler; Validates the url property.
+   */
+  public function validateUrl(&$element, &$form_state, $form) {
+    $url_type = $this->getFieldSetting('url_type');
+    $url_is_valid = TRUE;
+
+    // Validate only if the field type supports internal URLs.
+    if ($element['url']['#value'] !== '' && $url_type & LinkItemInterface::LINK_INTERNAL) {
+      try {
+        $url = Url::createFromPath($element['url']['#value']);
+
+        if ($url->isExternal() && !UrlHelper::isValid($element['url']['#value'], TRUE)) {
+          $url_is_valid = FALSE;
+        }
+        elseif ($url->isExternal() && $url_type == LinkItemInterface::LINK_INTERNAL) {
+          $url_is_valid = FALSE;
+        }
+      }
+      catch (NotFoundHttpException $e) {
+        $url_is_valid = FALSE;
+      }
+      catch (MatchingRouteNotFoundException $e) {
+        $url_is_valid = FALSE;
+      }
+      catch (ParamNotConvertedException $e) {
+        $url_is_valid = FALSE;
+      }
+    }
+
+    if (!$url_is_valid) {
+      \Drupal::formBuilder()->setError($element['url'], $form_state, $this->t('The URL %url is not valid.', array('%url' => $element['url']['#value'])));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, array &$form_state) {
+    foreach ($values as &$value) {
+      if (!empty($value['url'])) {
+        try {
+          $url = Url::createFromPath($value['url']);
+          $url->setOption('attributes', $value['attributes']);
+
+          $value += $url->toArray();
+        }
+        catch (NotFoundHttpException $e) {
+          // Nothing to do here, validateUrl() emits form validation errors.
+        }
+        catch (MatchingRouteNotFoundException $e) {
+          // Nothing to do here, validateUrl() emits form validation errors.
+        }
+        catch (ParamNotConvertedException $e) {
+          // Nothing to do here, validateUrl() emits form validation errors.
+        }
+      }
+    }
+    return $values;
+  }
+
 }
-
diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
index 1be52d5..b347a9a 100644
--- a/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
+++ b/core/modules/link/lib/Drupal/link/Tests/LinkFieldTest.php
@@ -7,8 +7,9 @@
 
 namespace Drupal\link\Tests;
 
-use Drupal\simpletest\WebTestBase;
 use Drupal\Component\Utility\String;
+use Drupal\link\LinkItemInterface;
+use Drupal\simpletest\WebTestBase;
 
 /**
  * Tests link field widgets and formatters.
@@ -73,14 +74,16 @@ function testURLValidation() {
       'type' => 'link',
     ));
     $this->field->save();
-    entity_create('field_instance_config', array(
+    $this->instance = entity_create('field_instance_config', array(
       'field_name' => $field_name,
       'entity_type' => 'entity_test',
       'bundle' => 'entity_test',
       'settings' => array(
         'title' => DRUPAL_DISABLED,
+        'url_type' => LinkItemInterface::LINK_GENERIC,
       ),
-    ))->save();
+    ));
+    $this->instance->save();
     entity_get_form_display('entity_test', 'entity_test', 'default')
       ->setComponent($field_name, array(
         'type' => 'link_default',
@@ -100,21 +103,16 @@ function testURLValidation() {
     $this->assertFieldByName("{$field_name}[0][url]", '', 'Link URL field is displayed');
     $this->assertRaw('placeholder="http://example.com"');
 
-    // Verify that a valid URL can be submitted.
-    $value = 'http://www.example.com/';
-    $edit = array(
-      'user_id' => 1,
-      'name' => $this->randomName(),
-      "{$field_name}[0][url]" => $value,
+    // Define some valid URLs.
+    $valid_external_entries = array(
+      'http://www.example.com/'
+    );
+    $valid_internal_entries = array(
+      'entity_test/add'
     );
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
-    $id = $match[1];
-    $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
-    $this->assertRaw($value);
 
-    // Verify that invalid URLs cannot be submitted.
-    $wrong_entries = array(
+    // Define some invalid URLs.
+    $invalid_external_entries = array(
       // Missing protcol
       'not-an-url',
       // Invalid protocol
@@ -122,14 +120,66 @@ function testURLValidation() {
       // Missing host name
       'http://',
     );
-    $this->drupalGet('entity_test/add');
-    foreach ($wrong_entries as $invalid_value) {
+    $invalid_internal_entries = array(
+      'non/existing/path'
+    );
+
+    // Test external and internal URLs for 'url_type' = LinkItemInterface::LINK_GENERIC.
+    $this->assertValidEntries($field_name, $valid_external_entries + $valid_internal_entries);
+    $this->assertInvalidEntries($field_name, $invalid_external_entries + $invalid_internal_entries);
+
+    // Test external URLs for 'url_type' = LinkItemInterface::LINK_EXTERNAL.
+    $this->instance->settings['url_type'] = LinkItemInterface::LINK_EXTERNAL;
+    $this->instance->save();
+    $this->assertValidEntries($field_name, $valid_external_entries);
+    $this->assertInvalidEntries($field_name, $valid_internal_entries + $invalid_external_entries);
+
+    // Test external URLs for 'url_type' = LinkItemInterface::LINK_INTERNAL.
+    $this->instance->settings['url_type'] = LinkItemInterface::LINK_INTERNAL;
+    $this->instance->save();
+    $this->assertValidEntries($field_name, $valid_internal_entries);
+    $this->assertInvalidEntries($field_name, $valid_external_entries + $invalid_internal_entries);
+  }
+
+  /**
+   * Asserts that valid URLs can be submitted.
+   *
+   * @param string $field_name
+   *   The field name
+   * @param array $valid_entries
+   *   An array of valid URL entries.
+   */
+  protected function assertValidEntries($field_name, array $valid_entries) {
+    foreach ($valid_entries as $value) {
+      $edit = array(
+        'user_id' => 1,
+        'name' => $this->randomName(),
+        "{$field_name}[0][url]" => $value,
+      );
+      $this->drupalPostForm('entity_test/add', $edit, t('Save'));
+      preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+      $id = $match[1];
+      $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
+      $this->assertRaw($value);
+    }
+  }
+
+  /**
+   * Asserts that invalid URLs cannot be submitted.
+   *
+   * @param string $field_name
+   *   The field name
+   * @param array $invalid_entries
+   *   An array of invalid URL entries.
+   */
+  protected function assertInvalidEntries($field_name, array $invalid_entries) {
+    foreach ($invalid_entries as $invalid_value) {
       $edit = array(
         'user_id' => 1,
         'name' => $this->randomName(),
         "{$field_name}[0][url]" => $invalid_value,
       );
-      $this->drupalPostForm(NULL, $edit, t('Save'));
+      $this->drupalPostForm('entity_test/add', $edit, t('Save'));
       $this->assertText(t('The URL @url is not valid.', array('@url' => $invalid_value)));
     }
   }
diff --git a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php
index d1e4ea6..4279753 100644
--- a/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php
+++ b/core/modules/link/lib/Drupal/link/Tests/LinkItemTest.php
@@ -58,7 +58,7 @@ public function testLinkItem() {
     $class = $this->randomName();
     $entity->field_test->url = $url;
     $entity->field_test->title = $title;
-    $entity->field_test->first()->get('attributes')->set('class', $class);
+    $entity->field_test->first()->get('options')->set('attributes', array('class' => $class));
     $entity->name->value = $this->randomName();
     $entity->save();
 
@@ -71,7 +71,7 @@ public function testLinkItem() {
     $this->assertEqual($entity->field_test[0]->url, $url);
     $this->assertEqual($entity->field_test->title, $title);
     $this->assertEqual($entity->field_test[0]->title, $title);
-    $this->assertEqual($entity->field_test->attributes['class'], $class);
+    $this->assertEqual($entity->field_test->options['attributes']['class'], $class);
 
     // Verify changing the field value.
     $new_url = 'http://drupal.org';
@@ -79,17 +79,17 @@ public function testLinkItem() {
     $new_class = $this->randomName();
     $entity->field_test->url = $new_url;
     $entity->field_test->title = $new_title;
-    $entity->field_test->first()->get('attributes')->set('class', $new_class);
+    $entity->field_test->first()->get('options')->set('attributes', array('class' => $new_class));
     $this->assertEqual($entity->field_test->url, $new_url);
     $this->assertEqual($entity->field_test->title, $new_title);
-    $this->assertEqual($entity->field_test->attributes['class'], $new_class);
+    $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class);
 
     // Read changed entity and assert changed values.
     $entity->save();
     $entity = entity_load('entity_test', $id);
     $this->assertEqual($entity->field_test->url, $new_url);
     $this->assertEqual($entity->field_test->title, $new_title);
-    $this->assertEqual($entity->field_test->attributes['class'], $new_class);
+    $this->assertEqual($entity->field_test->options['attributes']['class'], $new_class);
   }
 
 }
diff --git a/core/modules/link/link.module b/core/modules/link/link.module
index 0e830df..2c06c5d 100644
--- a/core/modules/link/link.module
+++ b/core/modules/link/link.module
@@ -15,7 +15,7 @@ function link_help($path, $arg) {
     case 'admin/help#link':
       $output = '';
       $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Link module allows you to create fields that contain external URLs and optional link text. See the <a href="!field">Field module help</a> and the <a href="!field_ui">Field UI help</a> pages for general information on fields and how to create and manage them. For more information, see the <a href="!link_documentation">online documentation for the Link module</a>.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!link_documentation' => 'https://drupal.org/documentation/modules/link')) . '</p>';
+      $output .= '<p>' . t('The Link module allows you to create fields that contain internal or external URLs and optional link text. See the <a href="!field">Field module help</a> and the <a href="!field_ui">Field UI help</a> pages for general information on fields and how to create and manage them. For more information, see the <a href="!link_documentation">online documentation for the Link module</a>.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!link_documentation' => 'https://drupal.org/documentation/modules/link')) . '</p>';
       $output .= '<h3>' . t('Uses') . '</h3>';
       $output .= '<dl>';
       $output .= '<dt>' . t('Managing and displaying link fields') . '</dt>';
@@ -27,7 +27,7 @@ function link_help($path, $arg) {
       $output .= '<dt>' . t('Adding attributes to links') . '</dt>';
       $output .= '<dd>' . t('You can add attributes to links, by changing the <em>Format settings</em> in the <em>Manage display</em> page. Adding <em>rel="nofollow"</em> notifies search engines that links should not be followed.') . '</dd>';
       $output .= '<dt>' . t('Validating URLs') . '</dt>';
-      $output .= '<dd>' . t('All links are validated after a link field is filled in. They can include anchors or query strings. Links need to start with either the scheme name <em>http</em> or <em>https</em>; other scheme names (for example ftp or git) are not supported.') . '</dd>';
+      $output .= '<dd>' . t('All links are validated after a link field is filled in. They can include anchors or query strings.') . '</dd>';
       $output .= '</dl>';
     return $output;
   }
@@ -39,7 +39,7 @@ function link_help($path, $arg) {
 function link_theme() {
   return array(
     'link_formatter_link_separate' => array(
-      'variables' => array('title' => NULL, 'url_title' => NULL, 'href' => NULL, 'options' => array()),
+      'variables' => array('title' => NULL, 'url_title' => NULL, 'url' => NULL),
       'template' => 'link-formatter-link-separate',
     ),
   );
@@ -57,12 +57,17 @@ function link_theme() {
  *   - title: (optional) A descriptive or alternate title for the link, which
  *     may be different than the actual link text.
  *   - url_title: The anchor text for the link.
- *   - href: The link URL.
- *   - options: (optional) An array of options to pass to l().
+ *   - url: A \Drupal\Core\Url object.
  */
 function template_preprocess_link_formatter_link_separate(&$variables) {
   if (!empty($variables['title'])) {
     $variables['title'] = String::checkPlain($variables['title']);
   }
-  $variables['link'] = l($variables['url_title'], $variables['href'], $variables['options']);
+
+  if (!$variables['url']->isExternal()) {
+    $variables['link'] = \Drupal::linkGenerator()->generateFromUrl($variables['url_title'], $variables['url']);
+  }
+  else {
+    $variables['link'] = l($variables['url_title'], $variables['url']->getPath(), $variables['url']->getOptions());
+  }
 }
diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
index ac867c3..6e7a163 100644
--- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
+++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
@@ -142,8 +142,7 @@ public function testToString(Url $url) {
    */
   public function testToArray(Url $url) {
     $expected = array(
-      'route_name' => '',
-      'route_parameters' => array(),
+      'path' => $this->path,
       'options' => array(),
     );
     $this->assertSame($expected, $url->toArray());
@@ -157,7 +156,7 @@ public function testToArray(Url $url) {
    * @covers ::getRouteName()
    */
   public function testGetRouteName(Url $url) {
-    $this->assertSame('', $url->getRouteName());
+    $this->assertSame(NULL, $url->getRouteName());
   }
 
   /**
