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 c4c7593..b993e6c 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -349,6 +349,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..8a95237
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Element/EntityAutocomplete.php
@@ -0,0 +1,181 @@
+<?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.
+        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, $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);
+  }
+
+  /**
+   * 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/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 5bdc3e6..645cf6c 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/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/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/system.routing.yml b/core/modules/system/system.routing.yml
index 2bb9e62..7c87bb9 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -460,3 +460,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:
