diff --git a/core/config/schema/core.entity.schema.yml b/core/config/schema/core.entity.schema.yml
index 2d1b7dc..8fa60f8 100644
--- a/core/config/schema/core.entity.schema.yml
+++ b/core/config/schema/core.entity.schema.yml
@@ -222,6 +222,34 @@ field.widget.settings.checkbox:
       type: boolean
       label: 'Use field label instead of the "On value" as label'
 
+field.widget.settings.entity_reference_autocomplete_tags:
+  type: mapping
+  label: 'Entity reference autocomplete (Tags style) display format settings'
+  mapping:
+    match_operator:
+      type: string
+      label: 'Autocomplete matching'
+    size:
+      type: integer
+      label: 'Size of textfield'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
+field.widget.settings.entity_reference_autocomplete:
+  type: mapping
+  label: 'Entity reference autocomplete display format settings'
+  mapping:
+    match_operator:
+      type: string
+      label: 'Autocomplete matching'
+    size:
+      type: integer
+      label: 'Size of textfield'
+    placeholder:
+      type: label
+      label: 'Placeholder'
+
 field.formatter.settings.string:
   type: mapping
   mapping:
diff --git a/core/core.services.yml b/core/core.services.yml
index 29a734a..b972add 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -357,6 +357,9 @@ services:
     arguments: ['@config.manager', '@entity.manager']
     tags:
       - { name: event_subscriber }
+  entity.autocomplete_matcher:
+    class: Drupal\Core\Entity\EntityAutocompleteMatcher
+    arguments: ['@plugin.manager.entity_reference_selection']
   plugin.manager.entity_reference_selection:
     class: Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManager
     parent: default_plugin_manager
diff --git a/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
new file mode 100644
index 0000000..27d814f
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -0,0 +1,196 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Element\EntityAutocomplete.
+ */
+
+namespace Drupal\Core\Entity\Element;
+
+use Drupal\Component\Utility\Tags;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\Textfield;
+use Drupal\user\EntityOwnerInterface;
+
+/**
+ * Provides an entity autocomplete form element.
+ *
+ * @FormElement("entity_autocomplete")
+ */
+class EntityAutocomplete extends Textfield {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $info = parent::getInfo();
+    $class = get_class($this);
+
+    // Apply default form element properties.
+    $info['#target_type'] = NULL;
+    $info['#selection_handler'] = 'default';
+    $info['#selection_settings'] = array();
+    $info['#tags'] = FALSE;
+    $info['#autocreate'] = NULL;
+
+    $info['#element_validate'] = array(array($class, 'validateEntityAutocomplete'));
+    array_unshift($info['#process'], array($class, 'processEntityAutocomplete'));
+
+    // @todo What do with #default_value? Should we impose an
+    // array('label' => .., 'value' => ..) structure instead of manually
+    // composing the textfield string?
+
+    return $info;
+  }
+
+  /**
+   * Adds entity autocomplete functionality to a form element.
+   *
+   * @param array $element
+   *   The form element to process. Properties used:
+   *   - #target_type: The ID of the target entity type.
+   *   - #selection_handler: The plugin ID of the entity reference selection
+   *     handler.
+   *   - #selection_settings: An array of settings that will be passed to the
+   *     selection handler.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   *
+   * @return array
+   *   The form element.
+   *
+   * @throws \InvalidArgumentException
+   *   Exception thrown when the #target_type or #autocreate['bundle'] are
+   *   missing.
+   */
+  public static function processEntityAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
+    // Nothing to do if there is no target entity type.
+    if (empty($element['#target_type'])) {
+      throw new \InvalidArgumentException('Missing required #target_type parameter.');
+    }
+
+    // Provide default values and sanity checks for the #autocreate parameter.
+    if ($element['#autocreate']) {
+      if (!isset($element['#autocreate']['bundle'])) {
+        throw new \InvalidArgumentException("Missing required #autocreate['bundle'] parameter.");
+      }
+      // Default the autocreate user ID to the current user.
+      $element['#autocreate']['uid'] = $element['#autocreate']['uid'] ?: \Drupal::currentUser()->id();
+    }
+
+    $element['#autocomplete_route_name'] = 'system.entity_autocomplete';
+    $element['#autocomplete_route_parameters'] = array(
+      'target_type' => $element['#target_type'],
+      'selection_handler' => $element['#selection_handler'],
+      'selection_settings' => $element['#selection_settings'] ? base64_encode(serialize($element['#selection_settings'])) : '',
+    );
+
+    return $element;
+  }
+
+  /**
+   * Form element validation handler for entity_autocomplete elements.
+   */
+  public static function validateEntityAutocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
+    $value = NULL;
+    if (!empty($element['#value'])) {
+      $options = array(
+        'target_type' => $element['#target_type'],
+        'handler' => $element['#selection_handler'],
+        'handler_settings' => $element['#selection_settings'],
+      );
+      $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getInstance($options);
+      $autocreate = (bool) $element['#autocreate'];
+
+      foreach (Tags::explode($element['#value']) as $input) {
+        // Take "label (entity id)', match the ID from parenthesis when it's a
+        // number.
+        $match = static::extractEntityIdFormAutocompletionResult($input);
+        if ($match === NULL) {
+          // Try to get a match from the input string when the user didn't use
+          // the autocomplete but filled in a value manually.
+          $match = $handler->validateAutocompleteInput($input, $element, $form_state, $complete_form, !$autocreate);
+        }
+
+        if ($match !== NULL) {
+          $value[] = array(
+            'target_id' => $match,
+          );
+        }
+        elseif ($autocreate) {
+          // Auto-create item. See
+          // \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
+          $value[] = array(
+            'entity' => static::createNewEntity($element['#target_type'], $element['#autocreate']['bundle'], $input, $element['#autocreate']['uid'])
+          );
+        }
+      }
+
+      // Use only the last value if the form element does not support multiple
+      // matches (tags).
+      if (!$element['#tags'] && !empty($value)) {
+        $last_value = $value[count($value) - 1];
+        $value = isset($last_value['target_id']) ? $last_value['target_id'] : $last_value;
+      }
+    };
+
+    $form_state->setValueForElement($element, $value);
+  }
+
+  /**
+   * Extracts the entity ID from the autocompletion result.
+   *
+   * @param string $input
+   *   The input coming from the autocompletion result.
+   *
+   * @return mixed|null
+   *   NULL if no entity could be extracted, otherwise the entity ID.
+   */
+  public static function extractEntityIdFormAutocompletionResult($input) {
+    $match = NULL;
+    if (preg_match("/.+\((\d+)\)/", $input, $matches)) {
+      $match = $matches[1];
+    }
+    // Match the ID when it's a string (e.g. for config entity types).
+    elseif (preg_match("/.+\(([\w.]+)\)/", $input, $matches)) {
+      $match = $matches[1];
+    }
+    return $match;
+  }
+
+  /**
+   * Creates a new entity from a label entered in the autocomplete input.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   * @param string $bundle
+   *   The bundle name.
+   * @param string $label
+   *   The entity label.
+   * @param int $uid
+   *   The entity owner ID.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   */
+  protected static function createNewEntity($entity_type_id, $bundle, $label, $uid) {
+    $entity_manager = \Drupal::entityManager();
+
+    $entity_type = $entity_manager->getDefinition($entity_type_id);
+    $bundle_key = $entity_type->getKey('bundle');
+    $label_key = $entity_type->getKey('label');
+
+    $entity = $entity_manager->getStorage($entity_type_id)->create(array(
+      $bundle_key => $bundle,
+      $label_key => $label,
+    ));
+
+    if ($entity instanceof EntityOwnerInterface) {
+      $entity->setOwnerId($uid);
+    }
+
+    return $entity;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php
new file mode 100644
index 0000000..859f613
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityAutocompleteMatcher.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityAutocompleteMatcher.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Component\Utility\String;
+use Drupal\Component\Utility\Tags;
+use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
+
+/**
+ * Matcher class to get autocompletion results for entity reference.
+ */
+class EntityAutocompleteMatcher {
+
+  /**
+   * The entity reference selection handler plugin manager.
+   *
+   * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
+   */
+  protected $selectionHandlerManager;
+
+  /**
+   * Constructs a EntityAutocompleteMatcher object.
+   *
+   * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager
+   *   The entity reference selection handler plugin manager.
+   */
+  public function __construct(SelectionPluginManagerInterface $selection_manager) {
+    $this->selectionHandlerManager = $selection_manager;
+  }
+
+  /**
+   * Returns matched labels based on a given search string.
+   *
+   * @param string $target_type
+   *   The ID of the target entity type.
+   * @param string $selection_handler
+   *   The plugin ID of the entity reference selection handler.
+   * @param array $selection_settings
+   *   An array of settings that will be passed to the selection handler.
+   * @param string $string
+   *   (optional) The label of the entity to query by.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   *   Thrown when the current user doesn't have access to the specifies entity.
+   *
+   * @return array
+   *   An array of matched entity labels, in the format required by the AJAX
+   *   autocomplete API (e.g. array('value' => $value, 'label' => $label)).
+   *
+   * @see \Drupal\system\Controller\EntityAutocompleteController
+   */
+  public function getMatches($target_type, $selection_handler, $selection_settings, $string = '') {
+    $matches = array();
+
+    $options = array(
+      'target_type' => $target_type,
+      'handler' => $selection_handler,
+      'handler_settings' => $selection_settings,
+    );
+    $handler = $this->selectionHandlerManager->getInstance($options);
+
+    if (isset($string)) {
+      // Get an array of matching entities.
+      $match_operator = !empty($selection_settings['match_operator']) ? $selection_settings['match_operator'] : 'CONTAINS';
+      $entity_labels = $handler->getReferenceableEntities($string, $match_operator, 10);
+
+      // Loop through the entities and convert them into autocomplete output.
+      foreach ($entity_labels as $values) {
+        foreach ($values as $entity_id => $label) {
+          $key = "$label ($entity_id)";
+          // Strip things like starting/trailing white spaces, line breaks and
+          // tags.
+          $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(String::decodeEntities(strip_tags($key)))));
+          // Names containing commas or quotes must be wrapped in quotes.
+          $key = Tags::encode($key);
+          $matches[] = array('value' => $key, 'label' => $label);
+        }
+      }
+    }
+
+    return $matches;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteTagsWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteTagsWidget.php
new file mode 100644
index 0000000..268d10d
--- /dev/null
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteTagsWidget.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\AutocompleteTagsWidget.
+ */
+
+namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Plugin implementation of the 'entity_reference_autocomplete_tags' widget.
+ *
+ * @FieldWidget(
+ *   id = "entity_reference_autocomplete_tags",
+ *   label = @Translation("Autocomplete (Tags style)"),
+ *   description = @Translation("An autocomplete text field with tagging support."),
+ *   field_types = {
+ *     "entity_reference"
+ *   },
+ *   multiple_values = TRUE
+ * )
+ */
+class AutocompleteTagsWidget extends AutocompleteWidget {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+    $element = parent::formElement($items, $delta, $element, $form, $form_state);
+
+    $element['target_id']['#tags'] = TRUE;
+
+    return $element;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    return $values['target_id'];
+  }
+
+}
diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php
similarity index 61%
rename from core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php
rename to core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php
index 40d0aaf..bc646ec 100644
--- a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidgetBase.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/AutocompleteWidget.php
@@ -2,10 +2,10 @@
 
 /**
  * @file
- * Contains \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidgetBase.
+ * Contains \Drupal\Core\Field\Plugin\Field\FieldWidget\AutocompleteWidget.
  */
 
-namespace Drupal\entity_reference\Plugin\Field\FieldWidget;
+namespace Drupal\Core\Field\Plugin\Field\FieldWidget;
 
 use Drupal\Component\Utility\Tags;
 use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
@@ -16,9 +16,29 @@
 use Symfony\Component\Validator\ConstraintViolationInterface;
 
 /**
- * Parent plugin for entity reference autocomplete widgets.
+ * Plugin implementation of the 'entity_reference_autocomplete' widget.
+ *
+ * @FieldWidget(
+ *   id = "entity_reference_autocomplete",
+ *   label = @Translation("Autocomplete"),
+ *   description = @Translation("An autocomplete text field."),
+ *   field_types = {
+ *     "entity_reference"
+ *   }
+ * )
  */
-abstract class AutocompleteWidgetBase extends WidgetBase {
+class AutocompleteWidget extends WidgetBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function defaultSettings() {
+    return array(
+      'match_operator' => 'CONTAINS',
+      'size' => '60',
+      'placeholder' => '',
+    ) + parent::defaultSettings();
+  }
 
   /**
    * {@inheritdoc}
@@ -75,30 +95,24 @@ public function settingsSummary() {
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
     $entity = $items->getEntity();
 
-    // Prepare the autocomplete route parameters.
-    $autocomplete_route_parameters = array(
-      'type' => $this->getSetting('autocomplete_type'),
-      'field_name' => $this->fieldDefinition->getName(),
-      'entity_type' => $entity->getEntityTypeId(),
-      'bundle_name' => $entity->bundle(),
-    );
-
-    if ($entity_id = $entity->id()) {
-      $autocomplete_route_parameters['entity_id'] = $entity_id;
-    }
-
     $element += array(
-      '#type' => 'textfield',
+      '#type' => 'entity_autocomplete',
+      '#target_type' => $this->getFieldSetting('target_type'),
+      '#selection_handler' => $this->getFieldSetting('handler'),
+      '#selection_settings' => $this->getFieldSetting('handler_settings'),
       '#maxlength' => 1024,
       '#default_value' => implode(', ', $this->getLabels($items, $delta)),
-      '#autocomplete_route_name' => 'entity_reference.autocomplete',
-      '#autocomplete_route_parameters' => $autocomplete_route_parameters,
       '#size' => $this->getSetting('size'),
       '#placeholder' => $this->getSetting('placeholder'),
-      '#element_validate' => array(array($this, 'elementValidate')),
-      '#autocreate_uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id(),
     );
 
+    if ($this->getSelectionHandlerSetting('auto_create')) {
+      $element['#autocreate'] = array(
+        'bundle' => $this->getAutocreateBundle(),
+        'uid' => ($entity instanceof EntityOwnerInterface) ? $entity->getOwnerId() : \Drupal::currentUser()->id()
+      );
+    }
+
     return array('target_id' => $element);
   }
 
@@ -110,9 +124,20 @@ public function errorElement(array $element, ConstraintViolationInterface $error
   }
 
   /**
-   * Validates an element.
+   * {@inheritdoc}
    */
-  public function elementValidate($element, FormStateInterface $form_state, $form) { }
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    foreach ($values as $key => $value) {
+      // The entity_autocomplete form element returns an array when an entity
+      // was "autocreated", so we need to move it up a level.
+      if (is_array($value['target_id'])) {
+        unset($values[$key]['target_id']);
+        $values[$key] += $value['target_id'];
+      }
+    }
+
+    return $values;
+  }
 
   /**
    * Gets the entity labels.
@@ -144,43 +169,29 @@ protected function getLabels(EntityReferenceFieldItemListInterface $items, $delt
   }
 
   /**
-   * Creates a new entity from a label entered in the autocomplete input.
-   *
-   * @param string $label
-   *   The entity label.
-   * @param int $uid
-   *   The entity uid.
+   * Returns the name of the bundle which will be used for autocreated entities.
    *
-   * @return \Drupal\Core\Entity\EntityInterface
+   * @return string
+   *   The bundle name.
    */
-  protected function createNewEntity($label, $uid) {
-    $entity_manager = \Drupal::entityManager();
-    $target_type = $this->getFieldSetting('target_type');
-    $target_bundles = $this->getSelectionHandlerSetting('target_bundles');
-
-    // Get the bundle.
-    if (!empty($target_bundles)) {
-      $bundle = reset($target_bundles);
-    }
-    else {
-      $bundles = entity_get_bundles($target_type);
-      $bundle = reset($bundles);
-    }
-
-    $entity_type = $entity_manager->getDefinition($target_type);
-    $bundle_key = $entity_type->getKey('bundle');
-    $label_key = $entity_type->getKey('label');
-
-    $entity = $entity_manager->getStorage($target_type)->create(array(
-      $label_key => $label,
-      $bundle_key => $bundle,
-    ));
-
-    if ($entity instanceof EntityOwnerInterface) {
-      $entity->setOwnerId($uid);
+  protected function getAutocreateBundle() {
+    $bundle = NULL;
+    if ($this->getSelectionHandlerSetting('auto_create')) {
+      // If the 'target_bundles' setting is restricted to a single choice, we
+      // can use that.
+      if (($target_bundles = $this->getSelectionHandlerSetting('target_bundles')) && count($target_bundles) == 1) {
+        $bundle = reset($target_bundles);
+      }
+      // Otherwise use the first bundle as a fallback.
+      else {
+        // @todo Expose a proper UI for choosing the bundle for autocreated
+        // entities in https://www.drupal.org/node/2412569.
+        $bundles = entity_get_bundles($this->getFieldSetting('target_type'));
+        $bundle = key($bundles);
+      }
     }
 
-    return $entity;
+    return $bundle;
   }
 
   /**
@@ -197,15 +208,4 @@ protected function getSelectionHandlerSetting($setting_name) {
     return isset($settings[$setting_name]) ? $settings[$setting_name] : NULL;
   }
 
-  /**
-   * Checks whether a content entity is referenced.
-   *
-   * @return bool
-   */
-  protected function isContentReferenced() {
-    $target_type = $this->getFieldSetting('target_type');
-    $target_type_info = \Drupal::entityManager()->getDefinition($target_type);
-    return $target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface');
-  }
-
 }
diff --git a/core/lib/Drupal/Core/Path/PathValidator.php b/core/lib/Drupal/Core/Path/PathValidator.php
index 51cdc2f..fcf0968 100644
--- a/core/lib/Drupal/Core/Path/PathValidator.php
+++ b/core/lib/Drupal/Core/Path/PathValidator.php
@@ -109,6 +109,9 @@ protected function getUrl($path, $access_check) {
     if ($parsed_url['path'] == '<front>') {
       return new Url('<front>', [], $options);
     }
+    elseif ($parsed_url['path'] == '<none>') {
+      return new Url('<none>', [], $options);
+    }
     elseif (UrlHelper::isExternal($path) && UrlHelper::isValid($path)) {
       if (empty($parsed_url['path'])) {
         return FALSE;
diff --git a/core/modules/book/config/install/core.entity_form_display.node.book.default.yml b/core/modules/book/config/install/core.entity_form_display.node.book.default.yml
index ac9c4ee..0b7ccf6 100644
--- a/core/modules/book/config/install/core.entity_form_display.node.book.default.yml
+++ b/core/modules/book/config/install/core.entity_form_display.node.book.default.yml
@@ -25,7 +25,6 @@ content:
     settings:
       match_operator: CONTAINS
       size: 60
-      autocomplete_type: tags
       placeholder: ''
     third_party_settings: {  }
   created:
diff --git a/core/modules/entity_reference/config/schema/entity_reference.schema.yml b/core/modules/entity_reference/config/schema/entity_reference.schema.yml
index 1ee04d1..ec291fa 100644
--- a/core/modules/entity_reference/config/schema/entity_reference.schema.yml
+++ b/core/modules/entity_reference/config/schema/entity_reference.schema.yml
@@ -36,37 +36,3 @@ entity_reference.default.handler_settings:
     auto_create:
       type: boolean
       label: 'Create referenced entities if they don''t already exist'
-
-field.widget.settings.entity_reference_autocomplete_tags:
-  type: mapping
-  label: 'Entity reference autocomplete (Tags style) display format settings'
-  mapping:
-    match_operator:
-      type: string
-      label: 'Autocomplete matching'
-    size:
-      type: integer
-      label: 'Size of textfield'
-    autocomplete_type:
-      type: string
-      label: 'Autocomplete type'
-    placeholder:
-      type: label
-      label: 'Placeholder'
-
-field.widget.settings.entity_reference_autocomplete:
-  type: mapping
-  label: 'Entity reference autocomplete display format settings'
-  mapping:
-    match_operator:
-      type: string
-      label: 'Autocomplete matching'
-    size:
-      type: integer
-      label: 'Size of textfield'
-    autocomplete_type:
-      type: string
-      label: 'Autocomplete type'
-    placeholder:
-      type: label
-      label: 'Placeholder'
diff --git a/core/modules/entity_reference/entity_reference.routing.yml b/core/modules/entity_reference/entity_reference.routing.yml
deleted file mode 100644
index b2f73ff..0000000
--- a/core/modules/entity_reference/entity_reference.routing.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-entity_reference.autocomplete:
-  path: '/entity_reference/autocomplete/{type}/{field_name}/{entity_type}/{bundle_name}/{entity_id}'
-  defaults:
-    _controller: '\Drupal\entity_reference\EntityReferenceController::handleAutocomplete'
-    entity_id: 'NULL'
-  requirements:
-    _access: 'TRUE'
diff --git a/core/modules/entity_reference/entity_reference.services.yml b/core/modules/entity_reference/entity_reference.services.yml
deleted file mode 100644
index 680cdf2..0000000
--- a/core/modules/entity_reference/entity_reference.services.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-services:
-  entity_reference.autocomplete:
-    class: Drupal\entity_reference\EntityReferenceAutocomplete
-    arguments: ['@entity.manager', '@plugin.manager.entity_reference_selection']
diff --git a/core/modules/entity_reference/src/EntityReferenceAutocomplete.php b/core/modules/entity_reference/src/EntityReferenceAutocomplete.php
deleted file mode 100644
index f0a6157..0000000
--- a/core/modules/entity_reference/src/EntityReferenceAutocomplete.php
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\entity_reference/EntityReferenceAutocomplete.
- */
-
-namespace Drupal\entity_reference;
-
-use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Tags;
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface;
-use Drupal\Core\Field\FieldDefinitionInterface;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
-/**
- * Helper class to get autocompletion results for entity reference.
- */
-class EntityReferenceAutocomplete {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * The Entity reference selection handler plugin manager.
-   *
-   * @var \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface
-   */
-  protected $selectionHandlerManager;
-
-  /**
-   * Constructs a EntityReferenceAutocomplete object.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   * @param \Drupal\Core\Entity\EntityReferenceSelection\SelectionPluginManagerInterface $selection_manager
-   *   The Entity reference selection handler plugin manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager, SelectionPluginManagerInterface $selection_manager) {
-    $this->entityManager = $entity_manager;
-    $this->selectionHandlerManager = $selection_manager;
-  }
-
-  /**
-   * Returns matched labels based on a given search string.
-   *
-   * This function can be used by other modules that wish to pass a mocked
-   * definition of the field on instance.
-   *
-   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
-   *   The field definition.
-   * @param string $entity_type
-   *   The entity type.
-   * @param string $bundle
-   *   The entity bundle.
-   * @param string $entity_id
-   *   (optional) The entity ID the entity reference field is attached to.
-   *   Defaults to ''.
-   * @param string $prefix
-   *   (optional) A prefix for all the keys returned by this function.
-   * @param string $string
-   *   (optional) The label of the entity to query by.
-   *
-   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
-   *   Thrown when the current user doesn't have access to the specifies entity.
-   *
-   * @return array
-   *   A list of matched entity labels.
-   *
-   * @see \Drupal\entity_reference\EntityReferenceController
-   */
-  public function getMatches(FieldDefinitionInterface $field_definition, $entity_type, $bundle, $entity_id = '', $prefix = '', $string = '') {
-    $matches = array();
-    $entity = NULL;
-
-    if ($entity_id !== 'NULL') {
-      $entity = $this->entityManager->getStorage($entity_type)->load($entity_id);
-      if (!$entity || !$entity->access('view')) {
-        throw new AccessDeniedHttpException();
-      }
-    }
-    $handler = $this->selectionHandlerManager->getSelectionHandler($field_definition, $entity);
-
-    if (isset($string)) {
-      // Get an array of matching entities.
-      $widget = entity_get_form_display($entity_type, $bundle, 'default')->getComponent($field_definition->getName());
-      $match_operator = !empty($widget['settings']['match_operator']) ? $widget['settings']['match_operator'] : 'CONTAINS';
-      $entity_labels = $handler->getReferenceableEntities($string, $match_operator, 10);
-
-      // Loop through the entities and convert them into autocomplete output.
-      foreach ($entity_labels as $values) {
-        foreach ($values as $entity_id => $label) {
-          $key = "$label ($entity_id)";
-          // Strip things like starting/trailing white spaces, line breaks and
-          // tags.
-          $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(String::decodeEntities(strip_tags($key)))));
-          // Names containing commas or quotes must be wrapped in quotes.
-          $key = Tags::encode($key);
-          $matches[] = array('value' => $prefix . $key, 'label' => $label);
-        }
-      }
-    }
-
-    return $matches;
-  }
-
-}
diff --git a/core/modules/entity_reference/src/EntityReferenceController.php b/core/modules/entity_reference/src/EntityReferenceController.php
deleted file mode 100644
index afc4cc0..0000000
--- a/core/modules/entity_reference/src/EntityReferenceController.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\entity_reference/EntityReferenceController.
- */
-
-namespace Drupal\entity_reference;
-
-use Drupal\Component\Utility\Tags;
-use Drupal\Component\Utility\Unicode;
-use Drupal\Core\Controller\ControllerBase;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
-
-/**
- * Defines route controller for entity reference.
- */
-class EntityReferenceController extends ControllerBase {
-
-  /**
-   * The autocomplete helper for entity references.
-   *
-   * @var \Drupal\entity_reference\EntityReferenceAutocomplete
-   */
-  protected $entityReferenceAutocomplete;
-
-  /**
-   * Constructs a EntityReferenceController object.
-   *
-   * @param \Drupal\entity_reference\EntityReferenceAutocomplete $entity_reference_autocompletion
-   *   The autocompletion helper for entity references.
-   */
-  public function __construct(EntityReferenceAutocomplete $entity_reference_autocompletion) {
-    $this->entityReferenceAutocomplete = $entity_reference_autocompletion;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static(
-      $container->get('entity_reference.autocomplete')
-    );
-  }
-
-  /**
-   * Autocomplete the label of an entity.
-   *
-   * @param Request $request
-   *   The request object that contains the typed tags.
-   * @param string $type
-   *   The widget type (i.e. 'single' or 'tags').
-   * @param string $field_name
-   *   The name of the entity reference field.
-   * @param string $entity_type
-   *   The entity type.
-   * @param string $bundle_name
-   *   The bundle name.
-   * @param string $entity_id
-   *   (optional) The entity ID the entity reference field is attached to.
-   *   Defaults to ''.
-   *
-   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
-   *   Throws access denied when either the field storage or field does not
-   *   exists or the user does not have access to edit the field.
-   *
-   * @return \Symfony\Component\HttpFoundation\JsonResponse
-   *   The matched labels as json.
-   */
-  public function handleAutocomplete(Request $request, $type, $field_name, $entity_type, $bundle_name, $entity_id) {
-    $definitions = $this->entityManager()->getFieldDefinitions($entity_type, $bundle_name);
-
-    if (!isset($definitions[$field_name])) {
-      throw new AccessDeniedHttpException();
-    }
-
-    $field_definition = $definitions[$field_name];
-    $access_control_handler = $this->entityManager()->getAccessControlHandler($entity_type);
-    if ($field_definition->getType() != 'entity_reference' || !$access_control_handler->fieldAccess('edit', $field_definition)) {
-      throw new AccessDeniedHttpException();
-    }
-
-    // Get the typed string, if exists from the URL.
-    $items_typed = $request->query->get('q');
-    $items_typed = Tags::explode($items_typed);
-    $last_item = Unicode::strtolower(array_pop($items_typed));
-
-    $prefix = '';
-    // The user entered a comma-separated list of entity labels, so we generate
-    // a prefix.
-    if ($type == 'tags' && !empty($last_item)) {
-      $prefix = count($items_typed) ? Tags::implode($items_typed) . ', ' : '';
-    }
-
-    $matches = $this->entityReferenceAutocomplete->getMatches($field_definition, $entity_type, $bundle_name, $entity_id, $prefix, $last_item);
-
-    return new JsonResponse($matches);
-  }
-}
diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteTagsWidget.php b/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteTagsWidget.php
deleted file mode 100644
index f7b5ff3..0000000
--- a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteTagsWidget.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteTagsWidget.
- */
-
-namespace Drupal\entity_reference\Plugin\Field\FieldWidget;
-
-use Drupal\Component\Utility\Tags;
-use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * Plugin implementation of the 'entity_reference autocomplete-tags' widget.
- *
- * @FieldWidget(
- *   id = "entity_reference_autocomplete_tags",
- *   label = @Translation("Autocomplete (Tags style)"),
- *   description = @Translation("An autocomplete text field."),
- *   field_types = {
- *     "entity_reference"
- *   },
- *   multiple_values = TRUE
- * )
- */
-class AutocompleteTagsWidget extends AutocompleteWidgetBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function defaultSettings() {
-    return array(
-      'match_operator' => 'CONTAINS',
-      'size' => '60',
-      'autocomplete_type' => 'tags',
-      'placeholder' => '',
-    ) + parent::defaultSettings();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function elementValidate($element, FormStateInterface $form_state, $form) {
-    $value = array();
-    // If a value was entered into the autocomplete.
-    $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($this->fieldDefinition);
-    $bundles = entity_get_bundles($this->getFieldSetting('target_type'));
-    $auto_create = $this->getSelectionHandlerSetting('auto_create');
-
-    if (!empty($element['#value'])) {
-      $value = array();
-      foreach (Tags::explode($element['#value']) as $input) {
-        $match = FALSE;
-
-        // Take "label (entity id)', match the ID from parenthesis when it's a
-        // number.
-        if (preg_match("/.+\((\d+)\)/", $input, $matches)) {
-          $match = $matches[1];
-        }
-        // Match the ID when it's a string (e.g. for config entity types).
-        elseif (preg_match("/.+\(([\w.]+)\)/", $input, $matches)) {
-          $match = $matches[1];
-        }
-        else {
-          // Try to get a match from the input string when the user didn't use
-          // the autocomplete but filled in a value manually.
-          $match = $handler->validateAutocompleteInput($input, $element, $form_state, $form, !$auto_create);
-        }
-
-        if ($match) {
-          $value[] = array('target_id' => $match);
-        }
-        elseif ($auto_create && (count($this->getSelectionHandlerSetting('target_bundles')) == 1 || count($bundles) == 1)) {
-          // Auto-create item. See
-          // \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
-          $value[] = array('entity' => $this->createNewEntity($input, $element['#autocreate_uid']));
-        }
-      }
-    };
-    // Change the element['#parents'], so in setValueForElement() we populate
-    // the correct key.
-    array_pop($element['#parents']);
-    $form_state->setValueForElement($element, $value);
-  }
-}
diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidget.php b/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidget.php
deleted file mode 100644
index 1e33d80..0000000
--- a/core/modules/entity_reference/src/Plugin/Field/FieldWidget/AutocompleteWidget.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\entity_reference\Plugin\Field\FieldWidget\AutocompleteWidget.
- */
-
-namespace Drupal\entity_reference\Plugin\Field\FieldWidget;
-
-use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
-use Drupal\Core\Form\FormStateInterface;
-
-/**
- * Plugin implementation of the 'entity_reference autocomplete' widget.
- *
- * @todo: Check if the following statement is still correct
- * The autocomplete path doesn't have a default here, because it's not the
- * the two widgets, and the Field API doesn't update default settings when
- * the widget changes.
- *
- * @FieldWidget(
- *   id = "entity_reference_autocomplete",
- *   label = @Translation("Autocomplete"),
- *   description = @Translation("An autocomplete text field."),
- *   field_types = {
- *     "entity_reference"
- *   }
- * )
- */
-class AutocompleteWidget extends AutocompleteWidgetBase {
-
-  protected $usesOptions = TRUE;
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function defaultSettings() {
-    return array(
-      'match_operator' => 'CONTAINS',
-      'size' => '60',
-      'autocomplete_type' => 'tags',
-      'placeholder' => '',
-    ) + parent::defaultSettings();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function elementValidate($element, FormStateInterface $form_state, $form) {
-    $auto_create = $this->getSelectionHandlerSetting('auto_create');
-
-    // If a value was entered into the autocomplete.
-    $value = NULL;
-    if (!empty($element['#value'])) {
-      // Take "label (entity id)', match the id from parenthesis.
-      // @todo: Lookup the entity type's ID data type and use it here.
-      // https://drupal.org/node/2107249
-      if ($this->isContentReferenced() && preg_match("/.+\((\d+)\)/", $element['#value'], $matches)) {
-        $value = $matches[1];
-      }
-      elseif (preg_match("/.+\(([\w.]+)\)/", $element['#value'], $matches)) {
-        $value = $matches[1];
-      }
-      if ($value === NULL) {
-        // Try to get a match from the input string when the user didn't use the
-        // autocomplete but filled in a value manually.
-        $handler = \Drupal::service('plugin.manager.entity_reference_selection')->getSelectionHandler($this->fieldDefinition);
-        $value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form, !$auto_create);
-      }
-
-      if (!$value && $auto_create && (count($this->getSelectionHandlerSetting('target_bundles')) == 1)) {
-        // Auto-create item. See
-        // \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::presave().
-        $value = array(
-          'entity' => $this->createNewEntity($element['#value'], $element['#autocreate_uid']),
-          // Keep the weight property.
-          '_weight' => $element['#weight'],
-        );
-        // Change the element['#parents'], so in setValueForElement() we
-        // populate the correct key.
-        array_pop($element['#parents']);
-      }
-    }
-    $form_state->setValueForElement($element, $value);
-  }
-}
diff --git a/core/modules/field_ui/src/Tests/EntityDisplayTest.php b/core/modules/field_ui/src/Tests/EntityDisplayTest.php
index 09887da..7753d41 100644
--- a/core/modules/field_ui/src/Tests/EntityDisplayTest.php
+++ b/core/modules/field_ui/src/Tests/EntityDisplayTest.php
@@ -17,7 +17,7 @@
  */
 class EntityDisplayTest extends KernelTestBase {
 
-  public static $modules = array('field_ui', 'field', 'entity_test', 'user', 'text', 'field_test', 'node', 'system', 'entity_reference');
+  public static $modules = array('field_ui', 'field', 'entity_test', 'user', 'text', 'field_test', 'node', 'system');
 
   protected function setUp() {
     parent::setUp();
@@ -307,7 +307,7 @@ public function testRenameDeleteBundle() {
     $dependencies = $new_form_display->calculateDependencies();
     $expected_form_dependencies = array(
       'config' => array('field.field.node.article_rename.body', 'node.type.article_rename'),
-      'module' => array('entity_reference', 'text')
+      'module' => array('text')
     );
     $this->assertEqual($expected_form_dependencies, $dependencies);
 
diff --git a/core/modules/forum/config/install/core.entity_form_display.node.forum.default.yml b/core/modules/forum/config/install/core.entity_form_display.node.forum.default.yml
index cc84152..8731335 100644
--- a/core/modules/forum/config/install/core.entity_form_display.node.forum.default.yml
+++ b/core/modules/forum/config/install/core.entity_form_display.node.forum.default.yml
@@ -28,7 +28,6 @@ content:
     settings:
       match_operator: CONTAINS
       size: 60
-      autocomplete_type: tags
       placeholder: ''
     third_party_settings: {  }
   created:
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index 0606f42..b6bb75d 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Entity\Element\EntityAutocomplete;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
@@ -44,15 +45,29 @@ public static function defaultSettings() {
   /**
    * Gets the URI without the 'user-path:' scheme, for display while editing.
    *
+   * This method is the inverse of ::getUserEnteredStringAsUri().
+   *
    * @param string $uri
    *   The URI to get the displayable string for.
    *
    * @return string
+   *
+   * @see getUserEnteredStringAsUri
    */
   protected static function getUriAsDisplayableString($uri) {
     $scheme = parse_url($uri, PHP_URL_SCHEME);
+    $uri = str_replace('<none>', '', $uri);
+    $uri = str_replace('<front>', '/', $uri);
+
     if ($scheme === 'user-path') {
-      $uri_reference = explode(':', $uri, 2)[1];
+      $uri_reference = ltrim(explode(':', $uri, 2)[1], '/');
+      if ($uri_reference && !in_array($uri_reference[0], ['?', '#', '/'])) {
+        $uri_reference = '/' . $uri_reference;
+      }
+    }
+    elseif ($scheme === 'entity') {
+      list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2);
+      $uri_reference = \Drupal::entityManager()->getStorage($entity_type)->load($entity_id)->label() . ' (' . $entity_id . ')';
     }
     else {
       $uri_reference = $uri;
@@ -65,18 +80,33 @@ protected static function getUriAsDisplayableString($uri) {
    *
    * Schemeless URIs are treated as 'user-path:' URIs.
    *
+   * This method is the inverse of ::getUriAsDisplayableString().
+   *
    * @param string $string
    *   The user-entered string.
    *
    * @return string
-   *   The URI, if a non-empty $string was passed.
+   *   The URI, if a non-empty $uri was passed.
+   *
+   * @see getUriAsDisplayableString
    */
   protected static function getUserEnteredStringAsUri($string) {
+    $entity_id = EntityAutocomplete::extractEntityIdFormAutocompletionResult($string);
+    if ($entity_id !== NULL) {
+      $string = 'entity:node/' . $entity_id;
+    }
+
     if (!empty($string)) {
       // Users can enter relative URLs, but we need a valid URI, so add an
       // explicit scheme when necessary.
       if (parse_url($string, PHP_URL_SCHEME) === NULL) {
-        return 'user-path:' . $string;
+        if (parse_url($string, PHP_URL_PATH) === NULL) {
+          $string = '<none>' . $string;
+        }
+        if (parse_url($string, PHP_URL_PATH) === '/') {
+          $string = '<front>' . ltrim($string, '/');
+        }
+        $string = 'user-path:' . $string;
       }
     }
     return $string;
@@ -87,6 +117,15 @@ protected static function getUserEnteredStringAsUri($string) {
    */
   public static function validateUriElement($element, FormStateInterface $form_state, $form) {
     $uri = static::getUserEnteredStringAsUri($element['#value']);
+    $form_state->setValueForElement($element, $uri);
+
+    // Figure out the user entered value after user-path. This might contain a
+    // fragment so we cannot use parse_url($string, PHP_URL_PATH), so we strip
+    // out the 10 chars of user-path:.
+    if (parse_url($uri, PHP_URL_SCHEME) === 'user-path' && !in_array(substr($uri, 10)[0], ['#', '/', '?', '<'], TRUE)) {
+      //
+      $form_state->setError($element, t('Manually entered paths should start with /, # or ?'));
+    }
 
     // If the URI is empty or not well-formed, the link field type's validation
     // constraint will detect it.
@@ -131,18 +170,20 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     // If the field is configured to support internal links, it cannot use the
     // 'url' form element and we have to do the validation ourselves.
     if ($this->supportsInternalLinks()) {
-      $element['uri']['#type'] = 'textfield';
+      $element['uri']['#type'] = 'entity_autocomplete';
+      // @todo This should be a setting?
+      $element['uri']['#target_type'] = 'node';
     }
 
     // If the field is configured to allow only internal links, add a useful
     // element prefix.
     if (!$this->supportsExternalLinks()) {
-      $element['uri']['#field_prefix'] = \Drupal::url('<front>', array(), array('absolute' => TRUE));
+      $element['uri']['#field_prefix'] = rtrim(\Drupal::url('<front>', array(), array('absolute' => TRUE)), '/');
     }
     // If the field is configured to allow both internal and external links,
     // show a useful description.
     elseif ($this->supportsExternalLinks() && $this->supportsInternalLinks()) {
-      $element['uri']['#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['uri']['#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(
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index 05a427e..7fed262 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -98,9 +98,11 @@ function testURLValidation() {
       'http://www.example.com/',
     );
     $valid_internal_entries = array(
-      'entity_test/add',
-      'a/path/alias',
+      '/entity_test/add',
+      '/a/path/alias',
       'entity:user/1',
+      '#example',
+      '?example=llama',
     );
 
     // Define some invalid URLs.
@@ -113,7 +115,10 @@ function testURLValidation() {
       'http://',
     );
     $invalid_internal_entries = array(
-      'non/existing/path',
+      // No existing.
+      '/non/existing/path',
+      // Missing start with /
+      'no-slash-start',
     );
 
     // Test external and internal URLs for 'link_type' = LinkItemInterface::LINK_GENERIC.
diff --git a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
index 67411f2..cfd0e3e 100644
--- a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
@@ -70,7 +70,7 @@ function testMenuLanguage() {
     $this->assertOptionSelected('edit-langcode', $edit['langcode'], 'The menu language was correctly selected.');
 
     // Test menu link language.
-    $link_path = '<front>';
+    $link_path = '/';
 
     // Add a menu link.
     $link_title = $this->randomString();
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index 8763132..120ff28 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -106,7 +106,7 @@ function testMenu() {
 
     foreach ($this->items as $item) {
       // Paths were set as 'node/$nid'.
-      $node = Node::load(str_replace('user-path:node/', '', $item->link->uri));
+      $node = Node::load(str_replace('user-path:/node/', '', $item->link->uri));
       $this->verifyMenuLink($item, $node);
     }
 
@@ -266,7 +266,7 @@ function doMenuTests() {
 
     $this->clickLink(t('Add link'));
     $link_title = $this->randomString();
-    $this->drupalPostForm(NULL, array('link[0][uri]' => '<front>', 'title[0][value]' => $link_title), t('Save'));
+    $this->drupalPostForm(NULL, array('link[0][uri]' => '/', 'title[0][value]' => $link_title), t('Save'));
     $this->assertUrl(Url::fromRoute('entity.menu.edit_form',  ['menu' => $menu_name]));
     // Test the 'Edit' operation.
     $this->clickLink(t('Edit'));
@@ -301,9 +301,9 @@ function doMenuTests() {
     $this->doMenuLinkFormDefaultsTest();
 
     // Add menu links.
-    $item1 = $this->addMenuLink('', 'node/' . $node1->id(), $menu_name, TRUE);
-    $item2 = $this->addMenuLink($item1->getPluginId(), 'node/' . $node2->id(), $menu_name, FALSE);
-    $item3 = $this->addMenuLink($item2->getPluginId(), 'node/' . $node3->id(), $menu_name);
+    $item1 = $this->addMenuLink('', '/node/' . $node1->id(), $menu_name, TRUE);
+    $item2 = $this->addMenuLink($item1->getPluginId(), '/node/' . $node2->id(), $menu_name, FALSE);
+    $item3 = $this->addMenuLink($item2->getPluginId(), '/node/' . $node3->id(), $menu_name);
 
     // Hierarchy
     // <$menu_name>
@@ -337,10 +337,10 @@ function doMenuTests() {
     $this->verifyMenuLink($item3, $node3, $item2, $node2);
 
     // Add more menu links.
-    $item4 = $this->addMenuLink('', 'node/' . $node4->id(), $menu_name);
-    $item5 = $this->addMenuLink($item4->getPluginId(), 'node/' . $node5->id(), $menu_name);
+    $item4 = $this->addMenuLink('', '/node/' . $node4->id(), $menu_name);
+    $item5 = $this->addMenuLink($item4->getPluginId(), '/node/' . $node5->id(), $menu_name);
     // Create a menu link pointing to an alias.
-    $item6 = $this->addMenuLink($item4->getPluginId(), 'node5', $menu_name, TRUE, '0');
+    $item6 = $this->addMenuLink($item4->getPluginId(), '/node5', $menu_name, TRUE, '0');
 
     // Hierarchy
     // <$menu_name>
@@ -427,7 +427,7 @@ function doMenuTests() {
     // item's weight doesn't get changed because of the old hardcoded delta=50.
     $items = array();
     for ($i = -50; $i <= 51; $i++) {
-      $items[$i] = $this->addMenuLink('', 'node/' . $node1->id(), $menu_name, TRUE, strval($i));
+      $items[$i] = $this->addMenuLink('', '/node/' . $node1->id(), $menu_name, TRUE, strval($i));
     }
     $this->assertMenuLink($items[51]->getPluginId(), array('weight' => '51'));
 
@@ -454,7 +454,7 @@ function doMenuTests() {
     $this->assertMenuLink($item7->getPluginId(), array('url' => 'http://drupal.org'));
 
     // Add <front> menu item.
-    $item8 = $this->addMenuLink('', '<front>', $menu_name);
+    $item8 = $this->addMenuLink('', '/', $menu_name);
     $this->assertMenuLink($item8->getPluginId(), array('route_name' => '<front>'));
     $this->drupalGet('');
     $this->assertResponse(200);
@@ -491,21 +491,21 @@ function testMenuQueryAndFragment() {
     $this->drupalLogin($this->admin_user);
 
     // Make a path with query and fragment on.
-    $path = 'test-page?arg1=value1&arg2=value2';
+    $path = '/test-page?arg1=value1&arg2=value2';
     $item = $this->addMenuLink('', $path);
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
     $this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
 
     // Now change the path to something without query and fragment.
-    $path = 'test-page';
+    $path = '/test-page';
     $this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array('link[0][uri]' => $path), t('Save'));
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
     $this->assertFieldByName('link[0][uri]', $path, 'Path no longer has query or fragment.');
 
     // Use <front>#fragment and ensure that saving it does not loose its
     // content.
-    $path = '<front>?arg1=value#fragment';
+    $path = '/?arg1=value#fragment';
     $item = $this->addMenuLink('', $path);
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
@@ -544,7 +544,7 @@ function testUnpublishedNodeMenuItem() {
       'status' => NODE_NOT_PUBLISHED,
     ));
 
-    $item = $this->addMenuLink('', 'node/' . $node->id());
+    $item = $this->addMenuLink('', '/node/' . $node->id());
     $this->modifyMenuLink($item);
 
     // Test that a user with 'administer menu' but without 'bypass node access'
@@ -561,7 +561,7 @@ function testUnpublishedNodeMenuItem() {
   public function testBlockContextualLinks() {
     $this->drupalLogin($this->drupalCreateUser(array('administer menu', 'access contextual links', 'administer blocks')));
     $custom_menu = $this->addCustomMenu();
-    $this->addMenuLink('', '<front>', $custom_menu->id());
+    $this->addMenuLink('', '/', $custom_menu->id());
     $block = $this->drupalPlaceBlock('system_menu_block:' . $custom_menu->id(), array('label' => 'Custom menu', 'provider' => 'system'));
     $this->drupalGet('test-page');
 
@@ -603,7 +603,7 @@ public function testBlockContextualLinks() {
    * @return \Drupal\menu_link_content\Entity\MenuLinkContent
    *   A menu link entity.
    */
-  function addMenuLink($parent = '', $path = '<front>', $menu_name = 'tools', $expanded = FALSE, $weight = '0') {
+  function addMenuLink($parent = '', $path = '/', $menu_name = 'tools', $expanded = FALSE, $weight = '0') {
     // View add menu link page.
     $this->drupalGet("admin/structure/menu/manage/$menu_name/add");
     $this->assertResponse(200);
@@ -637,7 +637,7 @@ function addMenuLink($parent = '', $path = '<front>', $menu_name = 'tools', $exp
    * Attempts to add menu link with invalid path or no access permission.
    */
   function addInvalidMenuLink() {
-    foreach (array('-&-', 'admin/people/permissions', '#') as $link_path) {
+    foreach (array('/-&-', '/admin/people/permissions', '/#') as $link_path) {
       $edit = array(
         'link[0][uri]' => $link_path,
         'title[0][value]' => 'title',
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 40d0053..bf6ff6a 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -400,7 +400,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
         'settings' => array(
           'match_operator' => 'CONTAINS',
           'size' => '60',
-          'autocomplete_type' => 'tags',
           'placeholder' => '',
         ),
       ))
diff --git a/core/modules/node/src/Tests/NodeCreationTest.php b/core/modules/node/src/Tests/NodeCreationTest.php
index 7ec85eb..3caf0bd 100644
--- a/core/modules/node/src/Tests/NodeCreationTest.php
+++ b/core/modules/node/src/Tests/NodeCreationTest.php
@@ -158,7 +158,7 @@ public function testAuthorAutocomplete() {
 
     $this->drupalGet('node/add/page');
 
-    $result = $this->xpath('//input[@id="edit-uid-0-target-id" and contains(@data-autocomplete-path, "/entity_reference/autocomplete/tags/uid/node/page")]');
+    $result = $this->xpath('//input[@id="edit-uid-0-target-id" and contains(@data-autocomplete-path, "/entity_reference_autocomplete/user/default")]');
     $this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
   }
 
diff --git a/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml b/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml
index c3540f9..6d0f3e8 100644
--- a/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml
+++ b/core/modules/options/tests/options_config_install_test/config/install/core.entity_form_display.node.options_install_test.default.yml
@@ -25,7 +25,6 @@ content:
     settings:
       match_operator: CONTAINS
       size: 60
-      autocomplete_type: tags
       placeholder: ''
     third_party_settings: {  }
   created:
diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
index 40c230d..89962b2 100644
--- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -41,13 +41,13 @@ public function testShortcutLinkAdd() {
 
     // Create some paths to test.
     $test_cases = [
-      '<front>',
-      'admin',
-      'admin/config/system/site-information',
-      'node/' . $this->node->id() . '/edit',
-      $path['alias'],
-      'router_test/test2',
-      'router_test/test3/value',
+      '/',
+      '/admin',
+      '/admin/config/system/site-information',
+      '/node/' . $this->node->id() . '/edit',
+      '/' . $path['alias'],
+      '/router_test/test2',
+      '/router_test/test3/value',
     ];
 
     // Check that each new shortcut links where it should.
@@ -61,7 +61,7 @@ public function testShortcutLinkAdd() {
       $this->assertResponse(200);
       $saved_set = ShortcutSet::load($set->id());
       $paths = $this->getShortcutInformation($saved_set, 'link');
-      $this->assertTrue(in_array('user-path:' . $test_path, $paths), 'Shortcut created: ' . $test_path);
+      $this->assertTrue(in_array('user-path:' . ($test_path != '/' ?: '<front>'), $paths), 'Shortcut created: ' . $test_path);
       $this->assertLink($title, 0, String::format('Shortcut link %url found on the page.', ['%url' => $test_path]));
     }
     $saved_set = ShortcutSet::load($set->id());
@@ -81,15 +81,15 @@ public function testShortcutLinkAdd() {
     $title = $this->randomMachineName();
     $form_data = [
       'title[0][value]' => $title,
-      'link[0][uri]' => 'admin',
+      'link[0][uri]' => '/admin',
     ];
     $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
     $this->assertResponse(200);
-    $this->assertRaw(t("The path '@link_path' is either invalid or you do not have access to it.", ['@link_path' => 'admin']));
+    $this->assertRaw(t("The path '@link_path' is either invalid or you do not have access to it.", ['@link_path' => '/admin']));
 
     $form_data = [
       'title[0][value]' => $title,
-      'link[0][uri]' => 'node',
+      'link[0][uri]' => '/node',
     ];
     $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
     $this->assertLink($title, 0, 'Shortcut link found on the page.');
@@ -136,7 +136,7 @@ public function testShortcutLinkRename() {
 
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $new_link_name, 'link[0][uri]' => $shortcut->link->uri), t('Save'));
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $new_link_name, 'link[0][uri]' => str_replace('user-path:', '', $shortcut->link->uri)), t('Save'));
     $saved_set = ShortcutSet::load($set->id());
     $titles = $this->getShortcutInformation($saved_set, 'title');
     $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
@@ -150,7 +150,7 @@ public function testShortcutLinkChangePath() {
     $set = $this->set;
 
     // Tests changing a shortcut path.
-    $new_link_path = 'admin/config';
+    $new_link_path = '/admin/config';
 
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
diff --git a/core/modules/system/src/Controller/EntityAutocompleteController.php b/core/modules/system/src/Controller/EntityAutocompleteController.php
new file mode 100644
index 0000000..bd47031
--- /dev/null
+++ b/core/modules/system/src/Controller/EntityAutocompleteController.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Controller\EntityAutocompleteController.
+ */
+
+namespace Drupal\system\Controller;
+
+use Drupal\Component\Utility\Tags;
+use Drupal\Component\Utility\Unicode;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Entity\EntityAutocompleteMatcher;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Defines a route controller for entity autocomplete form elements.
+ */
+class EntityAutocompleteController extends ControllerBase {
+
+  /**
+   * The autocomplete matcher for entity references.
+   *
+   * @var \Drupal\Core\Entity\EntityAutocompleteMatcher
+   */
+  protected $entityAutocompleteMatcher;
+
+  /**
+   * Constructs a EntityAutocompleteController object.
+   *
+   * @param \Drupal\Core\Entity\EntityAutocompleteMatcher $entity_autocomplete_matcher
+   *   The autocomplete matcher for entity references.
+   */
+  public function __construct(EntityAutocompleteMatcher $entity_autocomplete_matcher) {
+    $this->entityAutocompleteMatcher = $entity_autocomplete_matcher;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.autocomplete_matcher')
+    );
+  }
+
+  /**
+   * Autocomplete the label of an entity.
+   *
+   * @param Request $request
+   *   The request object that contains the typed tags.
+   * @param string $target_type
+   *   The ID of the target entity type.
+   * @param string $selection_handler
+   *   The plugin ID of the entity reference selection handler.
+   * @param string $selection_settings
+   *   The settings that will be passed to the selection handler.
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   *   The matched entity labels as a JSON response.
+   */
+  public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings = '') {
+    $matches = array();
+    // Get the typed string from the URL, if it exists.
+    if ($input = $request->query->get('q')) {
+      $typed_string = Tags::explode($input);
+      $typed_string = Unicode::strtolower(array_pop($typed_string));
+
+      // Selection settings are passed in as an encoded serialized array.
+      $selection_settings = $selection_settings ? unserialize(base64_decode($selection_settings)) : array();
+
+      $matches = $this->entityAutocompleteMatcher->getMatches($target_type, $selection_handler, $selection_settings, $typed_string);
+    }
+
+    return new JsonResponse($matches);
+  }
+
+}
diff --git a/core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php b/core/modules/system/src/Tests/Entity/EntityAutocompleteTest.php
similarity index 50%
rename from core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php
rename to core/modules/system/src/Tests/Entity/EntityAutocompleteTest.php
index ae9ac0c..36e9d72 100644
--- a/core/modules/entity_reference/src/Tests/EntityReferenceAutocompleteTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityAutocompleteTest.php
@@ -2,25 +2,23 @@
 
 /**
  * @file
- * Contains \Drupal\entity_reference\Tests\EntityReferenceAutocompleteTest.
+ * Contains \Drupal\system\Tests\Entity\EntityAutocompleteTest.
  */
 
-namespace Drupal\entity_reference\Tests;
+namespace Drupal\system\Tests\Entity;
 
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Tags;
-use Drupal\entity_reference\EntityReferenceController;
-use Drupal\system\Tests\Entity\EntityUnitTestBase;
+use Drupal\system\Controller\EntityAutocompleteController;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
  * Tests the autocomplete functionality.
  *
  * @group entity_reference
  */
-class EntityReferenceAutocompleteTest extends EntityUnitTestBase {
+class EntityAutocompleteTest extends EntityUnitTestBase {
 
   /**
    * The entity type used in this test.
@@ -37,52 +35,32 @@ class EntityReferenceAutocompleteTest extends EntityUnitTestBase {
   protected $bundle = 'entity_test';
 
   /**
-   * The name of the field used in this test.
-   *
-   * @var string
-   */
-  protected $fieldName = 'field_test';
-
-  /**
-   * Modules to install.
-   *
-   * @var array
-   */
-  public static $modules = array('entity_reference', 'entity_reference_test');
-
-  protected function setUp() {
-    parent::setUp();
-
-    entity_reference_create_field($this->entityType, $this->bundle, $this->fieldName, 'Field test', $this->entityType);
-  }
-
-  /**
    * Tests autocompletion edge cases with slashes in the names.
    */
   function testEntityReferenceAutocompletion() {
     // Add an entity with a slash in its name.
-    $entity_1 = entity_create($this->entityType, array('name' => '10/16/2011', $this->fieldName => NULL));
+    $entity_1 = entity_create($this->entityType, array('name' => '10/16/2011'));
     $entity_1->save();
 
     // Add another entity that differs after the slash character.
-    $entity_2 = entity_create($this->entityType, array('name' => '10/17/2011', $this->fieldName => NULL));
+    $entity_2 = entity_create($this->entityType, array('name' => '10/17/2011'));
     $entity_2->save();
 
     // Add another entity that has both a comma and a slash character.
-    $entity_3 = entity_create($this->entityType, array('name' => 'label with, and / test', $this->fieldName => NULL));
+    $entity_3 = entity_create($this->entityType, array('name' => 'label with, and / test'));
     $entity_3->save();
 
     // Try to autocomplete a entity label that matches both entities.
     // We should get both entities in a JSON encoded string.
     $input = '10/';
-    $data = $this->getAutocompleteResult('single', $input);
+    $data = $this->getAutocompleteResult($input);
     $this->assertIdentical($data[0]['label'], String::checkPlain($entity_1->name->value), 'Autocomplete returned the first matching entity');
     $this->assertIdentical($data[1]['label'], String::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity');
 
     // Try to autocomplete a entity label that matches the first entity.
     // We should only get the first entity in a JSON encoded string.
     $input = '10/16';
-    $data = $this->getAutocompleteResult('single', $input);
+    $data = $this->getAutocompleteResult($input);
     $target = array(
       'value' => $entity_1->name->value . ' (1)',
       'label' => String::checkPlain($entity_1->name->value),
@@ -92,12 +70,12 @@ function testEntityReferenceAutocompletion() {
     // Try to autocomplete a entity label that matches the second entity, and
     // the first entity  is already typed in the autocomplete (tags) widget.
     $input = $entity_1->name->value . ' (1), 10/17';
-    $data = $this->getAutocompleteResult('tags', $input);
+    $data = $this->getAutocompleteResult($input);
     $this->assertIdentical($data[0]['label'], String::checkPlain($entity_2->name->value), 'Autocomplete returned the second matching entity');
 
     // Try to autocomplete a entity label with both a comma and a slash.
     $input = '"label with, and / t';
-    $data = $this->getAutocompleteResult('single', $input);
+    $data = $this->getAutocompleteResult($input);
     $n = $entity_3->name->value . ' (3)';
     // Entity labels containing commas or quotes must be wrapped in quotes.
     $n = Tags::encode($n);
@@ -111,52 +89,20 @@ function testEntityReferenceAutocompletion() {
   /**
    * Returns the result of an Entity reference autocomplete request.
    *
-   * @param string $type
-   *   The Entity reference autocomplete type (e.g. 'single', 'tags').
    * @param string $input
    *   The label of the entity to query by.
    *
    * @return mixed
    *  The JSON value encoded in its appropriate PHP type.
    */
-  protected function getAutocompleteResult($type, $input) {
-    $request = Request::create('entity_reference/autocomplete/' . $type . '/' . $this->fieldName . '/node/article/NULL');
+  protected function getAutocompleteResult($input) {
+    $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');
     $request->query->set('q', $input);
 
-    $entity_reference_controller = EntityReferenceController::create($this->container);
-    $result = $entity_reference_controller->handleAutocomplete($request, $type, $this->fieldName, $this->entityType, $this->bundle, 'NULL')->getContent();
+    $entity_reference_controller = EntityAutocompleteController::create($this->container);
+    $result = $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default')->getContent();
 
     return Json::decode($result);
   }
 
-  /**
-   * Tests autocomplete for entity base fields.
-   */
-  public function testBaseField() {
-    // Add two users.
-    $user_1 = entity_create('user', array('name' => 'auto1', 'status' => TRUE));
-    $user_1->save();
-    $user_2 = entity_create('user', array('name' => 'auto2', 'status' => TRUE));
-    $user_2->save();
-
-    $request = Request::create('entity_reference/autocomplete/single/user_id/entity_test/entity_test/NULL');
-    $request->query->set('q', 'auto');
-
-    $entity_reference_controller = EntityReferenceController::create($this->container);
-    $result = $entity_reference_controller->handleAutocomplete($request, 'single', 'user_id', 'entity_test', 'entity_test', 'NULL')->getContent();
-
-    $data = Json::decode($result);
-    $this->assertIdentical($data[0]['label'], String::checkPlain($user_1->getUsername()), 'Autocomplete returned the first matching entity');
-    $this->assertIdentical($data[1]['label'], String::checkPlain($user_2->getUsername()), 'Autocomplete returned the second matching entity');
-
-    // Checks that exception thrown for unknown field.
-    try {
-      $entity_reference_controller->handleAutocomplete($request, 'single', 'unknown_field', 'entity_test', 'entity_test', 'NULL')->getContent();
-      $this->fail('Autocomplete throws exception for unknown field.');
-    }
-    catch (AccessDeniedHttpException $e) {
-      $this->pass('Autocomplete throws exception for unknown field.');
-    }
-  }
-
 }
diff --git a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
index e0e37f3..4a56198 100644
--- a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
@@ -18,7 +18,19 @@
  */
 class FieldWidgetConstraintValidatorTest extends KernelTestBase {
 
-  public static $modules = array('entity_test', 'field', 'user');
+  public static $modules = array('entity_test', 'field', 'user', 'system');
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('system', 'router');
+    $this->container->get('router.builder')->rebuild();
+
+    $this->installEntitySchema('user');
+  }
 
   /**
    * Tests widget constraint validation.
@@ -37,6 +49,8 @@ public function testValidation() {
     \Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state);
 
     // Validate the field constraint.
+    $form_state->getFormObject()->setEntity($entity)->setFormDisplay($display, $form_state);
+    $entity = $form_state->getFormObject()->buildEntity($form, $form_state);
     $display->validateFormValues($entity, $form, $form_state);
 
     $errors = $form_state->getErrors();
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index fff190b..1755091 100644
--- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -187,7 +187,7 @@ function testBreadCrumbs() {
     $menu = 'tools';
     $edit = array(
       'title[0][value]' => 'Root',
-      'link[0][uri]' => 'node',
+      'link[0][uri]' => '/node',
     );
     $this->drupalPostForm("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
     $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => 'Root'));
@@ -240,7 +240,7 @@ function testBreadCrumbs() {
       $term = $data['term'];
       $edit = array(
         'title[0][value]' => "$name link",
-        'link[0][uri]' => "taxonomy/term/{$term->id()}",
+        'link[0][uri]' => "/taxonomy/term/{$term->id()}",
         'menu_parent' => "$menu:{$parent_mlid}",
         'enabled[value]' => 1,
       );
@@ -248,7 +248,7 @@ function testBreadCrumbs() {
       $menu_links = entity_load_multiple_by_properties('menu_link_content', array(
         'title' => $edit['title[0][value]'],
         // @todo Use link.uri once https://www.drupal.org/node/2391217 is in.
-        'link__uri' => 'user-path:taxonomy/term/' . $term->id(),
+        'link__uri' => 'user-path:/taxonomy/term/' . $term->id(),
       ));
       $tags[$name]['link'] = reset($menu_links);
       $parent_mlid = $tags[$name]['link']->getPluginId();
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 2bb9e62..fecfd249 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -382,7 +382,8 @@ system.theme_settings_theme:
   path: ''
   options:
     _only_fragment: TRUE
-
+  requirements:
+    _access: 'TRUE'
 
 '<current>':
   path: '<current>'
@@ -460,3 +461,11 @@ system.admin_content:
     _title: 'Content'
   requirements:
     _permission: 'access administration pages'
+
+system.entity_autocomplete:
+  path: '/entity_reference_autocomplete/{target_type}/{selection_handler}/{selection_settings}'
+  defaults:
+    _controller: '\Drupal\system\Controller\EntityAutocompleteController::handleAutocomplete'
+    selection_settings: ''
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
index 3b9309d..0d6234d 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
@@ -115,7 +115,6 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
         'settings' => array(
           'match_operator' => 'CONTAINS',
           'size' => '60',
-          'autocomplete_type' => 'tags',
           'placeholder' => '',
         ),
       ));
diff --git a/core/modules/user/src/Tests/UserEntityReferenceTest.php b/core/modules/user/src/Tests/UserEntityReferenceTest.php
index b511203..215bf98 100644
--- a/core/modules/user/src/Tests/UserEntityReferenceTest.php
+++ b/core/modules/user/src/Tests/UserEntityReferenceTest.php
@@ -79,10 +79,10 @@ function testUserSelectionByRole() {
     $user3->save();
 
 
-    /** @var \Drupal\entity_reference\EntityReferenceAutocomplete $autocomplete */
-    $autocomplete = \Drupal::service('entity_reference.autocomplete');
+    /** @var \Drupal\Core\Entity\EntityAutocompleteMatcher $autocomplete */
+    $autocomplete = \Drupal::service('entity.autocomplete_matcher');
 
-    $matches = $autocomplete->getMatches($field_definition, 'user', 'user', 'NULL', '', 'aabb');
+    $matches = $autocomplete->getMatches('user', 'default', $field_definition->getSetting('handler_settings'), 'aabb');
     $this->assertEqual(count($matches), 2);
     $users = array();
     foreach ($matches as $match) {
@@ -92,7 +92,7 @@ function testUserSelectionByRole() {
     $this->assertTrue(in_array($user2->label(), $users));
     $this->assertFalse(in_array($user3->label(), $users));
 
-    $matches = $autocomplete->getMatches($field_definition, 'user', 'user', 'NULL', '', 'aabbbb');
+    $matches = $autocomplete->getMatches('user', 'default', $field_definition->getSetting('handler_settings'), 'aabbbb');
     $this->assertEqual(count($matches), 0, '');
   }
 }
diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
index a121cf2..9e54f2a 100644
--- a/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
+++ b/core/profiles/standard/config/install/core.entity_form_display.node.article.default.yml
@@ -52,7 +52,6 @@ content:
     settings:
       match_operator: CONTAINS
       size: 60
-      autocomplete_type: tags
       placeholder: ''
     third_party_settings: {  }
   created:
diff --git a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
index 32ffd5b..967c74e 100644
--- a/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
+++ b/core/profiles/standard/config/install/core.entity_form_display.node.page.default.yml
@@ -26,7 +26,6 @@ content:
     settings:
       match_operator: CONTAINS
       size: 60
-      autocomplete_type: tags
       placeholder: ''
     third_party_settings: {  }
   created:
