diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 73c4a83..aa4479c 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -689,13 +689,19 @@ function hook_field_is_empty($item, $field) {
  * Field API widgets specify how fields are displayed in edit forms. Fields of a
  * given @link field_types field type @endlink may be edited using more than one
  * widget. In this case, the Field UI module allows the site builder to choose
- * which widget to use. Widget types are defined by implementing
- * hook_field_widget_info().
+ * which widget to use.
+ *
+ * Widgets are Plugins managed by the
+ * Drupal\field\Plugin\Type\Widget\WidgetPluginManager class. A widget is
+ * implemented by providing a class that implements
+ * Drupal\field\Plugin\Type\Widget\WidgetInterface (in most cases, by
+ * subclassing Drupal\field\Plugin\Type\Widget\WidgetBase), and provides the
+ * proper annotation block.
  *
  * Widgets are @link forms_api_reference.html Form API @endlink
- * elements with additional processing capabilities. Widget hooks are typically
- * called by the Field Attach API during the creation of the field form
- * structure with field_attach_form().
+ * elements with additional processing capabilities. The methods of the
+ * WidgetInterface object are typically called by the Field Attach API during
+ * the creation of the field form structure with field_attach_form().
  *
  * @see field
  * @see field_types
@@ -703,84 +709,11 @@ function hook_field_is_empty($item, $field) {
  */
 
 /**
- * Expose Field API widget types.
- *
- * @return
- *   An array describing the widget types implemented by the module.
- *   The keys are widget type names. To avoid name clashes, widget type
- *   names should be prefixed with the name of the module that exposes them.
- *   The values are arrays describing the widget type, with the following
- *   key/value pairs:
- *   - label: The human-readable name of the widget type.
- *   - description: A short description for the widget type.
- *   - field types: An array of field types the widget supports.
- *   - settings: An array whose keys are the names of the settings available
- *     for the widget type, and whose values are the default values for those
- *     settings.
- *   - behaviors: (optional) An array describing behaviors of the widget, with
- *     the following elements:
- *     - multiple values: One of the following constants:
- *       - FIELD_BEHAVIOR_DEFAULT: (default) If the widget allows the input of
- *         one single field value (most common case). The widget will be
- *         repeated for each value input.
- *       - FIELD_BEHAVIOR_CUSTOM: If one single copy of the widget can receive
- *         several field values. Examples: checkboxes, multiple select,
- *         comma-separated textfield.
- *     - default value: One of the following constants:
- *       - FIELD_BEHAVIOR_DEFAULT: (default) If the widget accepts default
- *         values.
- *       - FIELD_BEHAVIOR_NONE: if the widget does not support default values.
- *   - weight: (optional) An integer to determine the weight of this widget
- *     relative to other widgets in the Field UI when selecting a widget for a
- *     given field instance.
- *
- * @see hook_field_widget_info_alter()
- * @see hook_field_widget_form()
- * @see hook_field_widget_form_alter()
- * @see hook_field_widget_WIDGET_TYPE_form_alter()
- * @see hook_field_widget_error()
- * @see hook_field_widget_settings_form()
- */
-function hook_field_widget_info() {
-  return array(
-    'text_textfield' => array(
-      'label' => t('Text field'),
-      'field types' => array('text'),
-      'settings' => array('size' => 60),
-      'behaviors' => array(
-        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
-        'default value' => FIELD_BEHAVIOR_DEFAULT,
-      ),
-    ),
-    'text_textarea' => array(
-      'label' => t('Text area (multiple rows)'),
-      'field types' => array('text_long'),
-      'settings' => array('rows' => 5),
-      'behaviors' => array(
-        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
-        'default value' => FIELD_BEHAVIOR_DEFAULT,
-      ),
-    ),
-    'text_textarea_with_summary' => array(
-      'label' => t('Text area with a summary'),
-      'field types' => array('text_with_summary'),
-      'settings' => array('rows' => 9, 'summary_rows' => 3),
-      'behaviors' => array(
-        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
-        'default value' => FIELD_BEHAVIOR_DEFAULT,
-      ),
-      // As an advanced widget, force it to sink to the bottom of the choices.
-      'weight' => 2,
-    ),
-  );
-}
-
-/**
  * Perform alterations on Field API widget types.
  *
  * @param $info
- *   Array of informations on widget types exposed by hook_field_widget_info()
- *   implementations.
+ *   Array of informations on existing widget types, as collected by the
+ *   annotation discovery mechanism.
  */
 function hook_field_widget_info_alter(&$info) {
   // Add a setting to a widget type.
@@ -789,93 +722,7 @@ function hook_field_widget_info_alter(&$info) {
   );
 
   // Let a new field type re-use an existing widget.
-  $info['options_select']['field types'][] = 'my_field_type';
-}
-
-/**
- * Return the form for a single field widget.
- *
- * Field widget form elements should be based on the passed-in $element, which
- * contains the base form element properties derived from the field
- * configuration.
- *
- * Field API will set the weight, field name and delta values for each form
- * element. If there are multiple values for this field, the Field API will
- * invoke this hook as many times as needed.
- *
- * Note that, depending on the context in which the widget is being included
- * (regular entity form, field configuration form, advanced search form...),
- * the values for $field and $instance might be different from the "official"
- * definitions returned by field_info_field() and field_info_instance().
- * Examples: mono-value widget even if the field is multi-valued, non-required
- * widget even if the field is 'required'...
- *
- * Therefore, the FAPI element callbacks (such as #process, #element_validate,
- * #value_callback...) used by the widget cannot use the field_info_field()
- * or field_info_instance() functions to retrieve the $field or $instance
- * definitions they should operate on. The field_widget_field() and
- * field_widget_instance() functions should be used instead to fetch the
- * current working definitions from $form_state, where Field API stores them.
- *
- * Alternatively, hook_field_widget_form() can extract the needed specific
- * properties from $field and $instance and set them as ad-hoc
- * $element['#custom'] properties, for later use by its element callbacks.
- *
- * Other modules may alter the form element provided by this function using
- * hook_field_widget_form_alter().
- *
- * @param $form
- *   The form structure where widgets are being attached to. This might be a
- *   full form structure, or a sub-element of a larger form.
- * @param $form_state
- *   An associative array containing the current state of the form.
- * @param $field
- *   The field structure.
- * @param $instance
- *   The field instance.
- * @param $langcode
- *   The language associated with $items.
- * @param $items
- *   Array of default values for this field.
- * @param $delta
- *   The order of this item in the array of subelements (0, 1, 2, etc).
- * @param $element
- *   A form element array containing basic properties for the widget:
- *   - #entity_type: The name of the entity the field is attached to.
- *   - #bundle: The name of the field bundle the field is contained in.
- *   - #field_name: The name of the field.
- *   - #language: The language the field is being edited in.
- *   - #field_parents: The 'parents' space for the field in the form. Most
- *       widgets can simply overlook this property. This identifies the
- *       location where the field values are placed within
- *       $form_state['values'], and is used to access processing information
- *       for the field through the field_form_get_state() and
- *       field_form_set_state() functions.
- *   - #columns: A list of field storage columns of the field.
- *   - #title: The sanitized element label for the field instance, ready for
- *     output.
- *   - #description: The sanitized element description for the field instance,
- *     ready for output.
- *   - #required: A Boolean indicating whether the element value is required;
- *     for required multiple value fields, only the first widget's values are
- *     required.
- *   - #delta: The order of this item in the array of subelements; see $delta
- *     above.
- *
- * @return
- *   The form elements for a single widget for this field.
- *
- * @see field_widget_field()
- * @see field_widget_instance()
- * @see hook_field_widget_form_alter()
- * @see hook_field_widget_WIDGET_TYPE_form_alter()
- */
-function hook_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
-  $element += array(
-    '#type' => $instance['widget']['type'],
-    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
-  );
-  return $element;
+  $info['options_select']['field_types'][] = 'my_field_type';
 }
 
 /**
@@ -956,53 +803,27 @@ function hook_field_widget_WIDGET_TYPE_form_alter(&$element, &$form_state, $cont
  * of the hook involves reading from the database, it is highly recommended to
  * statically cache the information.
  *
- * @param $widget
+ * @param array $widget_properties
  *   The instance's widget properties.
- * @param $context
+ * @param array $context
  *   An associative array containing:
  *   - entity_type: The entity type; e.g., 'node' or 'user'.
- *   - entity: The entity object.
+ *   - bundle: The bundle: e.g., 'page' or 'article'.
  *   - field: The field that the widget belongs to.
  *   - instance: The instance of the field.
- *   - default: A boolean indicating whether the form is being shown as a dummy
- *     form to set default values.
  *
  * @see hook_field_widget_properties_ENTITY_TYPE_alter()
  */
-function hook_field_widget_properties_alter(&$widget, $context) {
+function hook_field_widget_properties_alter(array &$widget_properties, array $context) {
   // Change a widget's type according to the time of day.
   $field = $context['field'];
   if ($context['entity_type'] == 'node' && $field['field_name'] == 'field_foo') {
     $time = date('H');
-    $widget['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
+    $widget_properties['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
   }
 }
 
 /**
- * Flag a field-level validation error.
- *
- * @param $element
- *   An array containing the form element for the widget. The error needs to be
- *   flagged on the right sub-element, according to the widget's internal
- *   structure.
- * @param $error
- *   An associative array with the following key-value pairs, as returned by
- *   hook_field_validate():
- *   - error: the error code. Complex widgets might need to report different
- *     errors to different form elements inside the widget.
- *   - message: the human readable message to be displayed.
- * @param $form
- *   The form structure where field elements are attached to. This might be a
- *   full form structure, or a sub-element of a larger form.
- * @param $form_state
- *   An associative array containing the current state of the form.
- */
-function hook_field_widget_error($element, $error, $form, &$form_state) {
-  form_error($element, $error['message']);
-}
-
-
-/**
  * @} End of "defgroup field_widget".
  */
 
@@ -2381,25 +2202,23 @@ function hook_field_extra_fields_display_alter(&$displays, $context) {
  * of the hook involves reading from the database, it is highly recommended to
  * statically cache the information.
  *
- * @param $widget
+ * @param array $widget_properties
  *   The instance's widget properties.
- * @param $context
+ * @param array $context
  *   An associative array containing:
  *   - entity_type: The entity type; e.g., 'node' or 'user'.
- *   - entity: The entity object.
+ *   - bundle: The bundle: e.g., 'page' or 'article'.
  *   - field: The field that the widget belongs to.
  *   - instance: The instance of the field.
- *   - default: A boolean indicating whether the form is being shown as a dummy
- *     form to set default values.
  *
  * @see hook_field_widget_properties_alter()
  */
-function hook_field_widget_properties_ENTITY_TYPE_alter(&$widget, $context) {
+function hook_field_widget_properties_ENTITY_TYPE_alter(array &$widget_properties, array $context) {
   // Change a widget's type according to the time of day.
   $field = $context['field'];
   if ($field['field_name'] == 'field_foo') {
     $time = date('H');
-    $widget['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
+    $widget_properties['type'] = $time < 12 ? 'widget_am' : 'widget_pm';
   }
 }
 
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 7c2acdb..87bd5c2 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -108,6 +108,95 @@ const FIELD_STORAGE_INSERT = 'insert';
  */
 
 /**
+ * Invoke a method on all the fields of a given entity.
+ *
+ * @todo Remove _field_invoke() and friends when field types and formatters are
+ * turned into plugins.
+ *
+ * @param string $method
+ *   The name of the method to invoke.
+ * @param Closure $target
+ *   A closure that receives an $instance object and returns the object on
+ *   which the method should be invoked.
+ * @param Drupal\Core\Entity\EntityInterface $entity
+ *   The fully formed $entity_type entity.
+ * @param mixed $a
+ *   A parameter for the invoked method. Defaults to NULL.
+ * @param mixed $b
+ *   A parameter for the invoked method. Defaults to NULL.
+ * @param array $options
+ *   An associative array of additional options, with the following keys:
+ *   - field_name: The name of the field whose operation should be invoked. By
+ *     default, the operation is invoked on all the fields in the entity's
+ *     bundle. NOTE: This option is not compatible with the 'deleted' option;
+ *     the 'field_id' option should be used instead.
+ *   - field_id: The id of the field whose operation should be invoked. By
+ *     default, the operation is invoked on all the fields in the entity's'
+ *     bundles.
+ *   - deleted: If TRUE, the function will operate on deleted fields as well
+ *     as non-deleted fields. If unset or FALSE, only non-deleted fields are
+ *     operated on.
+ *   - langcode: A language code or an array of language codes keyed by field
+ *     name. It will be used to narrow down to a single value the available
+ *     languages to act on.
+ */
+function field_invoke_method($method, \Closure $target_closure, EntityInterface $entity, &$a = NULL, &$b = NULL, array $options = array()) {
+  // Merge default options.
+  $default_options = array(
+    'deleted' => FALSE,
+    'langcode' => NULL,
+  );
+  $options += $default_options;
+
+  $entity_type = $entity->entityType();
+  // Determine the list of instances to iterate on.
+  $instances = _field_invoke_get_instances($entity_type, $entity->bundle(), $options);
+
+  // Iterate through the instances and collect results.
+  $return = array();
+  foreach ($instances as $instance) {
+
+    // Let the closure determine the target object on which the method should be
+    // called.
+    $target = $target_closure($instance);
+
+    if (method_exists($target, $method)) {
+      $field = field_info_field_by_id($instance['field_id']);
+      $field_name = $field['field_name'];
+
+      // Determine the list of languages to iterate on.
+      $available_langcodes = field_available_languages($entity_type, $field);
+      $langcodes = _field_language_suggestion($available_langcodes, $options['langcode'], $field_name);
+
+      foreach ($langcodes as $langcode) {
+        $items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array();
+
+        $result = $target->$method($entity, $langcode, $items, $a, $b);
+
+        if (isset($result)) {
+          // For methods with array results, we merge results together.
+          // For methods with scalar results, we collect results in an array.
+          if (is_array($result)) {
+            $return = array_merge($return, $result);
+          }
+          else {
+            $return[] = $result;
+          }
+        }
+
+        // Populate $items back in the field values, but avoid replacing missing
+        // fields with an empty array (those are not equivalent on update).
+        if ($items !== array() || isset($entity->{$field_name}[$langcode])) {
+          $entity->{$field_name}[$langcode] = $items;
+        }
+      }
+    }
+  }
+
+  return $return;
+}
+
+/**
  * Invoke a field hook.
  *
  * @param $op
@@ -432,6 +521,20 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
 }
 
 /**
+ * Defines a 'target closure' for field_invoke_method().
+ *
+ * Used to invoke methods on an instance's widget.
+ *
+ * @return Closure
+ *   A 'target closure' for field_invoke_method().
+ */
+function _field_invoke_widget_target() {
+  return function ($instance) {
+    return $instance->getWidget();
+  };
+}
+
+/**
  * Add form elements for all fields for an entity to a form structure.
  *
  * The form elements for the entity's fields are added by reference as direct
@@ -543,7 +646,7 @@ function field_attach_form($entity_type, EntityInterface $entity, &$form, &$form
 
   // If no language is provided use the default site language.
   $options = array('langcode' => field_valid_language($langcode));
-  $form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
+  $form += (array) field_invoke_method('form', _field_invoke_widget_target(), $entity, $form, $form_state, $options);
 
   // Add custom weight handling.
   $form['#pre_render'][] = '_field_extra_fields_pre_render';
@@ -795,9 +898,6 @@ function field_attach_validate($entity_type, $entity) {
  *   An associative array containing the current state of the form.
  */
 function field_attach_form_validate($entity_type, EntityInterface $entity, $form, &$form_state) {
-  // Extract field values from submitted values.
-  _field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
-
   // Perform field_level validation.
   try {
     field_attach_validate($entity_type, $entity);
@@ -812,7 +912,7 @@ function field_attach_form_validate($entity_type, EntityInterface $entity, $form
         field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
       }
     }
-    _field_invoke_default('form_errors', $entity_type, $entity, $form, $form_state);
+    field_invoke_method('flagErrors', _field_invoke_widget_target(), $entity, $form, $form_state);
   }
 }
 
@@ -836,9 +936,7 @@ function field_attach_form_validate($entity_type, EntityInterface $entity, $form
  */
 function field_attach_submit($entity_type, EntityInterface $entity, $form, &$form_state) {
   // Extract field values from submitted values.
-  _field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
-
-  _field_invoke_default('submit', $entity_type, $entity, $form, $form_state);
+  field_invoke_method('submit', _field_invoke_widget_target(), $entity, $form, $form_state);
 
   // Let other modules act on submitting the entity.
   // Avoid module_invoke_all() to let $form_state be taken by reference.
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index f1e5481..6ac93fa 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -559,6 +559,14 @@ function _field_write_instance($instance, $update = FALSE) {
   $field = field_read_field($instance['field_name']);
   $field_type = field_info_field_types($field['type']);
 
+  // Temporary workaround to allow incoming $instance as arrays or classed
+  // objects.
+  // @todo remove once the external APIs have been converted to use
+  // FieldInstance objects.
+  if (is_object($instance) && get_class($instance) == 'Drupal\field\FieldInstance') {
+    $instance = $instance->getArray();
+  }
+
   // Set defaults.
   $instance += array(
     'settings' => array(),
diff --git a/core/modules/field/field.default.inc b/core/modules/field/field.default.inc
index b4a6f50..7d1a309 100644
--- a/core/modules/field/field.default.inc
+++ b/core/modules/field/field.default.inc
@@ -11,39 +11,6 @@
  */
 
 /**
- * Extracts field values from submitted form values.
- *
- * @param $entity_type
- *   The type of $entity.
- * @param $entity
- *   The entity for the operation.
- * @param $field
- *   The field structure for the operation.
- * @param $instance
- *   The instance structure for $field on $entity's bundle.
- * @param $langcode
- *   The language associated to $items.
- * @param $items
- *   The field values. This parameter is altered by reference to receive the
- *   incoming form values.
- * @param $form
- *   The form structure where field elements are attached to. This might be a
- *   full form structure, or a sub-element of a larger form.
- * @param $form_state
- *   The form state.
- */
-function field_default_extract_form_values($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) {
-  $path = array_merge($form['#parents'], array($field['field_name'], $langcode));
-  $key_exists = NULL;
-  $values = drupal_array_get_nested_value($form_state['values'], $path, $key_exists);
-  if ($key_exists) {
-    // Remove the 'value' of the 'add more' button.
-    unset($values['add_more']);
-    $items = $values;
-  }
-}
-
-/**
  * Generic field validation handler.
  *
  * Possible error codes:
@@ -86,13 +53,6 @@ function field_default_validate($entity_type, $entity, $field, $instance, $langc
   }
 }
 
-function field_default_submit($entity_type, $entity, $field, $instance, $langcode, &$items, $form, &$form_state) {
-  // Filter out empty values.
-  $items = _field_filter_items($field, $items);
-  // Reorder items to account for drag-n-drop reordering.
-  $items = _field_sort_items($field, $items);
-}
-
 /**
  * Default field 'insert' operation.
  *
diff --git a/core/modules/field/field.form.inc b/core/modules/field/field.form.inc
index 8e7dbaa..dae9ec9 100644
--- a/core/modules/field/field.form.inc
+++ b/core/modules/field/field.form.inc
@@ -6,290 +6,6 @@
  */
 
 /**
- * Creates a form element for a field and can populate it with a default value.
- *
- * If the form element is not associated with an entity (i.e., $entity is NULL)
- * field_get_default_value will be called to supply the default value for the
- * field. Also allows other modules to alter the form element by implementing
- * their own hooks.
- *
- * @param $entity_type
- *   The type of entity (for example 'node' or 'user') that the field belongs
- *   to.
- * @param $entity
- *   The entity object that the field belongs to. This may be NULL if creating a
- *   form element with a default value.
- * @param $field
- *   An array representing the field whose editing element is being created.
- * @param $instance
- *   An array representing the structure for $field in its current context.
- * @param $langcode
- *   The language associated with the field.
- * @param $items
- *   An array of the field values. When creating a new entity this may be NULL
- *   or an empty array to use default values.
- * @param $form
- *   An array representing the form that the editing element will be attached
- *   to.
- * @param $form_state
- *   An array containing the current state of the form.
- * @param $get_delta
- *   Used to get only a specific delta value of a multiple value field.
- *
- * @return
- *  The form element array created for this field.
- */
-function field_default_form($entity_type, $entity, $field, $instance, $langcode, $items, &$form, &$form_state, $get_delta = NULL) {
-  // This could be called with no entity, as when a UI module creates a
-  // dummy form to set default values.
-  if ($entity) {
-    $id = $entity->id();
-  }
-
-  $parents = $form['#parents'];
-
-  $addition = array();
-  $field_name = $field['field_name'];
-  $addition[$field_name] = array();
-
-  // Populate widgets with default values when creating a new entity.
-  if (empty($items) && empty($id)) {
-    $items = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
-  }
-
-  // Let modules alter the widget properties.
-  $context = array(
-    'entity_type' => $entity_type,
-    'entity' => $entity,
-    'field' => $field,
-    'instance' => $instance,
-    'default' => !$entity,
-  );
-  drupal_alter(array('field_widget_properties', 'field_widget_properties_' . $entity_type), $instance['widget'], $context);
-
-  // Collect widget elements.
-  $elements = array();
-
-  // Store field information in $form_state.
-  if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) {
-    $field_state = array(
-      'field' => $field,
-      'instance' => $instance,
-      'items_count' => count($items),
-      'array_parents' => array(),
-      'errors' => array(),
-    );
-    field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
-  }
-
-  // If field module handles multiple values for this form element, and we are
-  // displaying an individual element, process the multiple value form.
-  if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
-    // Store the entity in the form.
-    $form['#entity'] = $entity;
-    $elements = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state);
-  }
-  // If the widget is handling multiple values (e.g Options), or if we are
-  // displaying an individual element, just get a single form element and make
-  // it the $delta value.
-  else {
-    $delta = isset($get_delta) ? $get_delta : 0;
-    $function = $instance['widget']['module'] . '_field_widget_form';
-    if (function_exists($function)) {
-      $element = array(
-        '#entity_type' => $instance['entity_type'],
-        '#entity' => $entity,
-        '#bundle' => $instance['bundle'],
-        '#field_name' => $field_name,
-        '#language' => $langcode,
-        '#field_parents' => $parents,
-        '#columns' => array_keys($field['columns']),
-        '#title' => check_plain($instance['label']),
-        '#description' => field_filter_xss($instance['description']),
-        // Only the first widget should be required.
-        '#required' => $delta == 0 && $instance['required'],
-        '#delta' => $delta,
-      );
-      if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
-        // Allow modules to alter the field widget form element.
-        $context = array(
-          'form' => $form,
-          'field' => $field,
-          'instance' => $instance,
-          'langcode' => $langcode,
-          'items' => $items,
-          'delta' => $delta,
-          'default' => !$entity,
-        );
-        drupal_alter(array('field_widget_form', 'field_widget_' . $instance['widget']['type'] . '_form'), $element, $form_state, $context);
-
-        // If we're processing a specific delta value for a field where the
-        // field module handles multiples, set the delta in the result.
-        // For fields that handle their own processing, we can't make
-        // assumptions about how the field is structured, just merge in the
-        // returned element.
-        if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
-          $elements[$delta] = $element;
-        }
-        else {
-          $elements = $element;
-        }
-      }
-    }
-  }
-
-  // Also aid in theming of field widgets by rendering a classified container.
-  $addition[$field_name] = array(
-    '#type' => 'container',
-    '#attributes' => array(
-      'class' => array(
-        'field-type-' . drupal_html_class($field['type']),
-        'field-name-' . drupal_html_class($field_name),
-        'field-widget-' . drupal_html_class($instance['widget']['type']),
-      ),
-    ),
-    '#weight' => $instance['widget']['weight'],
-  );
-
-  // Populate the 'array_parents' information in $form_state['field'] after
-  // the form is built, so that we catch changes in the form structure performed
-  // in alter() hooks.
-  $elements['#after_build'][] = 'field_form_element_after_build';
-  $elements['#field_name'] = $field_name;
-  $elements['#language'] = $langcode;
-  $elements['#field_parents'] = $parents;
-
-  $addition[$field_name] += array(
-    '#tree' => TRUE,
-    // The '#language' key can be used to access the field's form element
-    // when $langcode is unknown.
-    '#language' => $langcode,
-    $langcode => $elements,
-    '#access' => field_access('edit', $field, $entity_type, $entity),
-  );
-
-  return $addition;
-}
-
-/**
- * Special handling to create form elements for multiple values.
- *
- * Handles generic features for multiple fields:
- * - number of widgets
- * - AHAH-'add more' button
- * - drag-n-drop value reordering
- */
-function field_multiple_value_form($field, $instance, $langcode, $items, &$form, &$form_state) {
-  $field_name = $field['field_name'];
-  $parents = $form['#parents'];
-
-  // Determine the number of widgets to display.
-  switch ($field['cardinality']) {
-    case FIELD_CARDINALITY_UNLIMITED:
-      $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
-      $max = $field_state['items_count'];
-      break;
-
-    default:
-      $max = $field['cardinality'] - 1;
-      break;
-  }
-
-  $title = check_plain($instance['label']);
-  $description = field_filter_xss($instance['description']);
-
-  $id_prefix = implode('-', array_merge($parents, array($field_name)));
-  $wrapper_id = drupal_html_id($id_prefix . '-add-more-wrapper');
-
-  $field_elements = array();
-
-  $function = $instance['widget']['module'] . '_field_widget_form';
-  if (function_exists($function)) {
-    for ($delta = 0; $delta <= $max; $delta++) {
-      $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
-      $element = array(
-        '#entity_type' => $instance['entity_type'],
-        '#entity' => $form['#entity'],
-        '#bundle' => $instance['bundle'],
-        '#field_name' => $field_name,
-        '#language' => $langcode,
-        '#field_parents' => $parents,
-        '#columns' => array_keys($field['columns']),
-        // For multiple fields, title and description are handled by the wrapping table.
-        '#title' => $multiple ? '' : $title,
-        '#description' => $multiple ? '' : $description,
-        // Only the first widget should be required.
-        '#required' => $delta == 0 && $instance['required'],
-        '#delta' => $delta,
-        '#weight' => $delta,
-      );
-      if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
-        // Input field for the delta (drag-n-drop reordering).
-        if ($multiple) {
-          // We name the element '_weight' to avoid clashing with elements
-          // defined by widget.
-          $element['_weight'] = array(
-            '#type' => 'weight',
-            '#title' => t('Weight for row @number', array('@number' => $delta + 1)),
-            '#title_display' => 'invisible',
-             // Note: this 'delta' is the FAPI 'weight' element's property.
-            '#delta' => $max,
-            '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
-            '#weight' => 100,
-          );
-        }
-
-        // Allow modules to alter the field widget form element.
-        $context = array(
-          'form' => $form,
-          'field' => $field,
-          'instance' => $instance,
-          'langcode' => $langcode,
-          'items' => $items,
-          'delta' => $delta,
-          'default' => FALSE,
-        );
-        drupal_alter(array('field_widget_form', 'field_widget_' . $instance['widget']['type'] . '_form'), $element, $form_state, $context);
-
-        $field_elements[$delta] = $element;
-      }
-    }
-
-    if ($field_elements) {
-      $field_elements += array(
-        '#theme' => 'field_multiple_value_form',
-        '#field_name' => $field['field_name'],
-        '#cardinality' => $field['cardinality'],
-        '#title' => $title,
-        '#required' => $instance['required'],
-        '#description' => $description,
-        '#prefix' => '<div id="' . $wrapper_id . '">',
-        '#suffix' => '</div>',
-        '#max_delta' => $max,
-      );
-      // Add 'add more' button, if not working with a programmed form.
-      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
-        $field_elements['add_more'] = array(
-          '#type' => 'submit',
-          '#name' => strtr($id_prefix, '-', '_') . '_add_more',
-          '#value' => t('Add another item'),
-          '#attributes' => array('class' => array('field-add-more-submit')),
-          '#limit_validation_errors' => array(array_merge($parents, array($field_name, $langcode))),
-          '#submit' => array('field_add_more_submit'),
-          '#ajax' => array(
-            'callback' => 'field_add_more_js',
-            'wrapper' => $wrapper_id,
-            'effect' => 'fade',
-          ),
-        );
-      }
-    }
-  }
-
-  return $field_elements;
-}
-
-/**
  * Returns HTML for an individual form element.
  *
  * Combine multiple values into a table with drag-n-drop reordering.
@@ -387,43 +103,6 @@ function field_form_element_after_build($element, &$form_state) {
 }
 
 /**
- * Transfer field-level validation errors to widgets.
- */
-function field_default_form_errors($entity_type, $entity, $field, $instance, $langcode, $items, $form, &$form_state) {
-  $field_state = field_form_get_state($form['#parents'], $field['field_name'], $langcode, $form_state);
-
-  if (!empty($field_state['errors'])) {
-    // Locate the correct element in the form.
-    $element = drupal_array_get_nested_value($form_state['complete_form'], $field_state['array_parents']);
-    // Only set errors if the element is accessible.
-    if (!isset($element['#access']) || $element['#access']) {
-      $function = $instance['widget']['module'] . '_field_widget_error';
-      $function_exists = function_exists($function);
-
-      $multiple_widget = field_behaviors_widget('multiple values', $instance) != FIELD_BEHAVIOR_DEFAULT;
-      foreach ($field_state['errors'] as $delta => $delta_errors) {
-        // For multiple single-value widgets, pass errors by delta.
-        // For a multiple-value widget, pass all errors to the main widget.
-        $error_element = $multiple_widget ? $element : $element[$delta];
-        foreach ($delta_errors as $error) {
-          if ($function_exists) {
-            $function($error_element, $error, $form, $form_state);
-          }
-          else {
-            // Make sure that errors are reported (even incorrectly flagged) if
-            // the widget module fails to implement hook_field_widget_error().
-            form_error($error_element, $error['message']);
-          }
-        }
-      }
-      // Reinitialize the errors list for the next submit.
-      $field_state['errors'] = array();
-      field_form_set_state($form['#parents'], $field['field_name'], $langcode, $form_state, $field_state);
-    }
-  }
-}
-
-/**
  * Submit handler for the "Add another item" button of a field form.
  *
  * This handler is run regardless of whether JS is enabled or not. It makes
diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc
index 65c6bcc..41dd9a6 100644
--- a/core/modules/field/field.info.inc
+++ b/core/modules/field/field.info.inc
@@ -5,6 +5,8 @@
  * Field Info API, providing information about available fields and field types.
  */
 
+use Drupal\field\FieldInstance;
+
 /**
  * @defgroup field_info Field Info API
  * @{
@@ -88,7 +90,6 @@ function _field_info_collate_types() {
     else {
       $info = array(
         'field types' => array(),
-        'widget types' => array(),
         'formatter types' => array(),
         'storage types' => array(),
       );
@@ -108,21 +109,6 @@ function _field_info_collate_types() {
       }
       drupal_alter('field_info', $info['field types']);
 
-      // Populate widget types.
-      foreach (module_implements('field_widget_info') as $module) {
-        $widget_types = (array) module_invoke($module, 'field_widget_info');
-        foreach ($widget_types as $name => $widget_info) {
-          // Provide defaults.
-          $widget_info += array(
-            'settings' => array(),
-          );
-          $info['widget types'][$name] = $widget_info;
-          $info['widget types'][$name]['module'] = $module;
-        }
-      }
-      drupal_alter('field_widget_info', $info['widget types']);
-      uasort($info['widget types'], 'drupal_sort_weight');
-
       // Populate formatter types.
       foreach (module_implements('field_formatter_info') as $module) {
         $formatter_types = (array) module_invoke($module, 'field_formatter_info');
@@ -227,7 +213,7 @@ function _field_info_collate_fields() {
       foreach ($definitions['instances'] as $instance) {
         $field = $info['fields'][$instance['field_id']];
         $instance = _field_info_prepare_instance($instance, $field);
-        $info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
+        $info['instances'][$instance['entity_type']][$instance['bundle']][$instance['field_name']] = new FieldInstance($instance);
         // Enrich field definitions with the list of bundles where they have
         // instances. NOTE: Deleted fields in $info['field_ids'] are not
         // enriched because all of their instances are deleted, too, and
@@ -310,8 +296,6 @@ function _field_info_prepare_instance($instance, $field) {
     $instance['default_value'] = NULL;
   }
 
-  $instance['widget'] = _field_info_prepare_instance_widget($field, $instance['widget']);
-
   foreach ($instance['display'] as $view_mode => $display) {
     $instance['display'][$view_mode] = _field_info_prepare_instance_display($field, $display);
   }
@@ -372,37 +356,6 @@ function _field_info_prepare_instance_display($field, $display) {
 }
 
 /**
- * Prepares widget specifications for the current run-time context.
- *
- * @param $field
- *   The field structure for the instance.
- * @param $widget
- *   Widget specifications as found in $instance['widget'].
- */
-function _field_info_prepare_instance_widget($field, $widget) {
-  $field_type = field_info_field_types($field['type']);
-
-  // Fill in default values.
-  $widget += array(
-    'type' => $field_type['default_widget'],
-    'settings' => array(),
-    'weight' => 0,
-  );
-
-  $widget_type = field_info_widget_types($widget['type']);
-  // Fallback to default formatter if formatter type is not available.
-  if (!$widget_type) {
-    $widget['type'] = $field_type['default_widget'];
-    $widget_type = field_info_widget_types($widget['type']);
-  }
-  $widget['module'] = $widget_type['module'];
-  // Fill in default settings for the widget.
-  $widget['settings'] += field_info_widget_settings($widget['type']);
-
-  return $widget;
-}
-
-/**
  * Prepares 'extra fields' for the current run-time context.
  *
  * @param $extra_fields
@@ -496,27 +449,22 @@ function field_info_field_types($field_type = NULL) {
 }
 
 /**
- * Returns information about field widgets from hook_field_widget_info().
+ * Returns information about field widgets from AnnotatedClassDiscovery.
  *
  * @param $widget_type
  *   (optional) A widget type name. If omitted, all widget types will be
  *   returned.
  *
  * @return
- *   Either a single widget type description, as provided by
- *   hook_field_widget_info(), or an array of all existing widget types, keyed
- *   by widget type name.
+ *   Either a single widget type description, as provided by class annotations
+ *   or an array of all existing widget types, keyed by widget type name.
  */
 function field_info_widget_types($widget_type = NULL) {
-  $info = _field_info_collate_types();
-  $widget_types = $info['widget types'];
   if ($widget_type) {
-    if (isset($widget_types[$widget_type])) {
-      return $widget_types[$widget_type];
-    }
+    return field_get_plugin_manager('widget')->getDefinition($widget_type);
   }
   else {
-    return $widget_types;
+    return field_get_plugin_manager('widget')->getDefinitions();
   }
 }
 
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 72b6d91..ce652cf 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -490,6 +490,35 @@ function field_associate_fields($module) {
 }
 
 /**
+ * Returns the PluginManager object for a given field plugin type.
+ *
+ * @param string $plugin_type
+ *   The plugin type. One of:
+ *   - field_type
+ *   - widget
+ *   - formatter
+ *   - storage
+ *
+ * @return Drupal\Component\Plugin\PluginManagerInterface
+ *   The PluginManager object.
+ */
+function field_get_plugin_manager($plugin_type) {
+  $plugin_types = &drupal_static(__FUNCTION__, array());
+
+  $classes = array(
+    'widget' => 'Drupal\field\Plugin\Type\Widget\WidgetPluginManager',
+  );
+
+  if (isset($classes[$plugin_type])) {
+    if (!isset($plugin_types[$plugin_type])) {
+      $plugin_types[$plugin_type] = new $classes[$plugin_type]();
+    }
+
+    return $plugin_types[$plugin_type];
+  }
+}
+
+/**
  * Helper function to get the default value for a field on an entity.
  *
  * @param $entity_type
diff --git a/core/modules/field/lib/Drupal/field/FieldInstance.php b/core/modules/field/lib/Drupal/field/FieldInstance.php
new file mode 100644
index 0000000..f75eb83
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/FieldInstance.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\FieldInstance.
+ */
+
+namespace Drupal\field;
+
+/**
+ * Class for field instance objects.
+ */
+class FieldInstance implements \ArrayAccess {
+
+  public $definition;
+
+  /**
+   * @var Drupal\field\Plugin\Type\Widget\WidgetInterface
+   */
+  protected $widget;
+
+  /**
+   * Constructs a FieldInstance object.
+   *
+   * @param array $definition
+   *   The instance definition array, as read from configuration storage.
+   */
+  public function __construct(array $definition) {
+    $this->definition = $definition;
+  }
+
+  /**
+   * Returns the Widget plugin for the instance.
+   *
+   * @return Drupal\field\Plugin\Type\Widget\WidgetInterface
+   *   The Widget plugin to be used for the instance.
+   */
+  public function getWidget() {
+    if (empty($this->widget)) {
+      $widget_properties = $this->definition['widget'];
+
+      // Let modules alter the widget properties.
+      $context = array(
+        'entity_type' => $this->definition['entity_type'],
+        'bundle' => $this->definition['bundle'],
+        'field' => field_info_field($this->definition['field_name']),
+        'instance' => $this,
+      );
+      drupal_alter(array('field_widget_properties', 'field_widget_properties_' . $this->definition['entity_type']), $widget_properties, $context);
+
+      $options = array(
+        'instance' => $this,
+        'type' => $widget_properties['type'],
+        'settings' => $widget_properties['settings'],
+        'weight' => $widget_properties['weight'],
+      );
+      $this->widget = field_get_plugin_manager('widget')->getInstance($options);
+    }
+
+    return $this->widget;
+  }
+
+  /**
+   * Implements ArrayAccess::offsetExists().
+   */
+  public function offsetExists($offset) {
+    return isset($this->definition[$offset]) || array_key_exists($offset, $this->definition);
+  }
+
+  /**
+   * Implements ArrayAccess::offsetGet().
+   */
+  public function &offsetGet($offset) {
+    return $this->definition[$offset];
+  }
+
+  /**
+   * Implements ArrayAccess::offsetSet().
+   */
+  public function offsetSet($offset, $value) {
+    if (!isset($offset)) {
+      // Do nothing; $array[] syntax is not supported by this temporary wrapper.
+      return;
+    }
+    $this->definition[$offset] = $value;
+
+    // If the widget properties changed, the widget plugin needs to be
+    // re-instanciated.
+    if ($offset == 'widget') {
+      unset($this->widget);
+    }
+  }
+
+  /**
+   * Implements ArrayAccess::offsetUnset().
+   */
+  public function offsetUnset($offset) {
+    unset($this->definition[$offset]);
+
+    // If the widget properties changed, the widget plugin needs to be
+    // re-instanciated.
+    if ($offset == 'widget') {
+      unset($this->widget);
+    }
+  }
+
+  /**
+   * Returns the instance definition as a regular array.
+   *
+   * This is used as a temporary BC layer.
+   * @todo Remove once the external APIs have been converted to use
+   *   FieldInstance objects.
+   *
+   * @return array
+   *   The instance definition as a regular array.
+   */
+  public function getArray() {
+    return $this->definition;
+  }
+
+  /**
+   * Handles serialization of Drupal\field\FieldInstance objects.
+   */
+  public function __sleep() {
+    return array('definition');
+  }
+  
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsBase.php b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsBase.php
new file mode 100644
index 0000000..be22874
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsBase.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\PluginSettingsBase.
+ */
+
+namespace Drupal\field\Plugin;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\field\Plugin\PluginSettingsInterface;
+
+/**
+ * Base class for the Field API plugins.
+ *
+ * This class handles lazy replacement of default settings values.
+ */
+abstract class PluginSettingsBase extends PluginBase implements PluginSettingsInterface {
+
+  /**
+   * The plugin settings.
+   *
+   * @var array
+   */
+  protected $settings = array();
+
+  /**
+   * Whether default settings have been merged into the current $settings.
+   *
+   * @var bool
+   */
+  protected $defaultSettingsMerged = FALSE;
+
+  /**
+   * Implements Drupal\field\Plugin\PluginSettingsInterface::getSettings().
+   */
+  public function getSettings() {
+    // Merge defaults before returning the array.
+    if (!$this->defaultSettingsMerged) {
+      $this->mergeDefaults();
+    }
+    return $this->settings;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\PluginSettingsInterface::getSetting().
+   */
+  public function getSetting($key) {
+    // Merge defaults if we have no value for the key.
+    if (!$this->defaultSettingsMerged && !array_key_exists($key, $this->settings)) {
+      $this->mergeDefaults();
+    }
+    return isset($this->settings[$key]) ? $this->settings[$key] : NULL;
+  }
+
+  /**
+   * Merges default settings values into $settings.
+   */
+  protected function mergeDefaults() {
+    $this->settings += $this->getDefaultSettings();
+    $this->defaultSettingsMerged = TRUE;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\PluginSettingsInterface::getDefaultSettings().
+   */
+  public function getDefaultSettings() {
+    $definition = $this->getDefinition();
+    return $definition['settings'];
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\PluginSettingsInterface::setSettings().
+   */
+  public function setSettings(array $settings) {
+    $this->settings = $settings;
+    $this->defaultSettingsMerged = FALSE;
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\PluginSettingsInterface::setSetting().
+   */
+  public function setSetting($key, $value) {
+    $this->settings[$key] = $value;
+    return $this;
+  }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsInterface.php b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsInterface.php
new file mode 100644
index 0000000..955e88d
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/PluginSettingsInterface.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\PluginSettingsInterface.
+ */
+
+namespace Drupal\field\Plugin;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
+/**
+ * Interface definition for plugin with settings.
+ */
+interface PluginSettingsInterface extends PluginInspectionInterface {
+
+  /**
+   * Returns the array of settings, including defaults for missing settings.
+   *
+   * @return array
+   *   The array of settings.
+   */
+  public function getSettings();
+
+  /**
+   * Returns the value of a setting, or its default value if absent.
+   *
+   * @param string $key
+   *   The setting name.
+   *
+   * @return mixed
+   *   The setting value.
+   */
+  public function getSetting($key);
+
+  /**
+   * Returns the default settings for the plugin.
+   *
+   * @return array
+   *   The array of default setting values, keyed by setting names.
+   */
+  public function getDefaultSettings();
+
+  /**
+   * Sets the settings for the plugin.
+   *
+   * @param array $settings
+   *   The array of settings, keyed by setting names. Missing settings will be
+   *   assigned their default values.
+   *
+   * @return Drupal\field\Plugin\PluginSettingsInterface
+   *   The plugin itself.
+   */
+  public function setSettings(array $settings);
+
+  /**
+   * Sets the value of a setting for the plugin.
+   *
+   * @param string $key
+   *   The setting name.
+   * @param mixed $value
+   *   The setting value.
+   *
+   * @return Drupal\field\Plugin\PluginSettingsInterface
+   *   The plugin itself.
+   */
+  public function setSetting($key, $value);
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/LegacyDiscoveryDecorator.php b/core/modules/field/lib/Drupal/field/Plugin/Type/LegacyDiscoveryDecorator.php
new file mode 100644
index 0000000..d23d932
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/LegacyDiscoveryDecorator.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\Type\LegacyDiscoveryDecorator.
+ */
+
+namespace Drupal\field\Plugin\Type;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Core\Plugin\Discovery\HookDiscovery;
+
+/**
+ * Custom decorator to add legacy widgets.
+ *
+ * Legacy widgets are discovered through the old hook_field_widget_info() hook,
+ * and handled by the Drupal\field\Plugin\field\widget\LegacyWidget class.
+ *
+ * @todo Remove once all core widgets have been converted.
+ */
+class LegacyDiscoveryDecorator implements DiscoveryInterface {
+
+  /**
+   * The decorated discovery object.
+   *
+   * @var Drupal\Component\Plugin\Discovery\DiscoveryInterface
+   */
+  protected $decorated;
+
+  /**
+   * Creates a Drupal\field\Plugin\Discovery\LegacyDiscoveryDecorator object.
+   *
+   * @param Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
+   *   The parent object implementing DiscoveryInterface that is being
+   *   decorated.
+   */
+  public function __construct(DiscoveryInterface $decorated) {
+    $this->decorated = $decorated;
+  }
+
+  /**
+   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
+   */
+  public function getDefinition($plugin_id) {
+    $definitions = $this->getDefinitions();
+    return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
+  }
+
+  /**
+   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
+   */
+  public function getDefinitions() {
+    $definitions = $this->decorated->getDefinitions();
+
+    $legacy_discovery = new HookDiscovery('field_widget_info');
+    if ($legacy_definitions = $legacy_discovery->getDefinitions()) {
+      foreach ($legacy_definitions as $plugin_id => &$definition) {
+        $definition['class'] = '\Drupal\field\Plugin\field\widget\LegacyWidget';
+
+        // Transform properties for which the format has changed.
+        if (isset($definition['field types'])) {
+          $definition['field_types'] = $definition['field types'];
+          unset($definition['field types']);
+        }
+        if (isset($definition['behaviors']['multiple values'])) {
+          $definition['multiple_values'] = $definition['behaviors']['multiple values'];
+          unset($definition['behaviors']['multiple values']);
+        }
+
+        $definitions[$plugin_id] = $definition;
+      }
+    }
+    return $definitions;
+  }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
new file mode 100644
index 0000000..6b653ff
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
@@ -0,0 +1,447 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\Type\Widget\WidgetBase.
+ */
+
+namespace Drupal\field\Plugin\Type\Widget;
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\Plugin\PluginSettingsBase;
+use Drupal\field\FieldInstance;
+
+/**
+ * Base class for 'Field widget' plugin implementations.
+ */
+abstract class WidgetBase extends PluginSettingsBase implements WidgetInterface {
+
+  /**
+   * The field definition.
+   *
+   * @var array
+   */
+  protected $field;
+
+  /**
+   * The field instance definition.
+   *
+   * @var Drupal\field\FieldInstance
+   */
+  protected $instance;
+
+  /**
+   * The widget settings.
+   *
+   * @var array
+   */
+  protected $settings;
+
+  /**
+   * The widget weight.
+   *
+   * @var int
+   */
+  protected $weight;
+
+  /**
+   * Constructs a WidgetBase object.
+   *
+   * @param array $plugin_id
+   *   The plugin_id for the widget..
+   * @param Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
+   *   The Discovery class that holds access to the widget implementation
+   *   definition.
+   * @param Drupal\field\FieldInstance $instance
+   *   The field instance to which the widget is associated.
+   * @param array $settings
+   *   The widget's settings.
+   * @param int $weight
+   *   The widget's weight.
+   */
+  public function __construct($plugin_id, DiscoveryInterface $discovery, FieldInstance $instance, array $settings, $weight) {
+    parent::__construct(array(), $plugin_id, $discovery);
+
+    $this->instance = $instance;
+    $this->field = field_info_field($instance['field_name']);
+    $this->settings = $settings;
+    $this->weight = $weight;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::form().
+   */
+  public function form(EntityInterface $entity, $langcode, array $items, array &$form, array &$form_state, $get_delta = NULL) {
+    $entity_type = $entity->entityType();
+    $field = $this->field;
+    $instance = $this->instance;
+    $field_name = $field['field_name'];
+
+    $parents = $form['#parents'];
+
+    $addition = array(
+      $field_name => array(),
+    );
+
+    // Populate widgets with default values when creating a new entity.
+    if (empty($items) && ($entity->isNew())) {
+      $items = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
+    }
+
+    // Store field information in $form_state.
+    if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) {
+      $field_state = array(
+          'field' => $field,
+          'instance' => $instance,
+          'items_count' => count($items),
+          'array_parents' => array(),
+          'errors' => array(),
+      );
+      field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
+    }
+
+    // Collect widget elements.
+    $elements = array();
+
+    // If the widget is handling multiple values (e.g Options), or if we are
+    // displaying an individual element, just get a single form element and make
+    // it the $delta value.
+    $definition = $this->getDefinition();
+    if (isset($get_delta) || $definition['multiple_values']) {
+      $delta = isset($get_delta) ? $get_delta : 0;
+      $element = array(
+        '#title' => check_plain($instance['label']),
+        '#description' => field_filter_xss($instance['description']),
+      );
+      $element = $this->formSingleElement($entity, $items, $delta, $langcode, $element, $form, $form_state);
+
+      if ($element) {
+        if (isset($get_delta)) {
+          // If we are processing a specific delta value for a field where the
+          // field module handles multiples, set the delta in the result.
+          $elements[$delta] = $element;
+        }
+        else {
+          // For fields that handle their own processing, we cannot make
+          // assumptions about how the field is structured, just merge in the
+          // returned element.
+          $elements = $element;
+        }
+      }
+    }
+    // If the widget does not handle multiple values itself, (and we are not
+    // displaying an individual element), process the multiple value form.
+    else {
+      $elements = $this->formMultipleElements($entity, $items, $langcode, $form, $form_state);
+    }
+
+    // Also aid in theming of field widgets by rendering a classified
+    // container.
+    $addition[$field_name] = array(
+      '#type' => 'container',
+      '#attributes' => array(
+        'class' => array(
+          'field-type-' . drupal_html_class($field['type']),
+          'field-name-' . drupal_html_class($field_name),
+          'field-widget-' . drupal_html_class($this->getPluginId()),
+        ),
+      ),
+      '#weight' => $this->weight,
+    );
+
+    // Populate the 'array_parents' information in $form_state['field'] after
+    // the form is built, so that we catch changes in the form structure performed
+    // in alter() hooks.
+    $elements['#after_build'][] = 'field_form_element_after_build';
+    $elements['#field_name'] = $field_name;
+    $elements['#language'] = $langcode;
+    $elements['#field_parents'] = $parents;
+
+    $addition[$field_name] += array(
+      '#tree' => TRUE,
+      // The '#language' key can be used to access the field's form element
+      // when $langcode is unknown.
+      '#language' => $langcode,
+      $langcode => $elements,
+      '#access' => field_access('edit', $field, $entity_type, $entity),
+    );
+
+    return $addition;
+  }
+
+  /**
+   * Special handling to create form elements for multiple values.
+   *
+   * Handles generic features for multiple fields:
+   * - number of widgets
+   * - AHAH-'add more' button
+   * - table display and drag-n-drop value reordering
+   */
+  protected function formMultipleElements(EntityInterface $entity, array $items, $langcode, array &$form, array &$form_state) {
+    $field = $this->field;
+    $instance = $this->instance;
+    $field_name = $field['field_name'];
+
+    $parents = $form['#parents'];
+
+    // Determine the number of widgets to display.
+    switch ($field['cardinality']) {
+      case FIELD_CARDINALITY_UNLIMITED:
+        $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);
+        $max = $field_state['items_count'];
+        $is_multiple = TRUE;
+        break;
+
+      default:
+        $max = $field['cardinality'] - 1;
+        $is_multiple = ($field['cardinality'] > 1);
+        break;
+    }
+
+    $id_prefix = implode('-', array_merge($parents, array($field_name)));
+    $wrapper_id = drupal_html_id($id_prefix . '-add-more-wrapper');
+
+    $title = check_plain($instance['label']);
+    $description = field_filter_xss($instance['description']);
+
+    $elements = array();
+
+    for ($delta = 0; $delta <= $max; $delta++) {
+      // For multiple fields, title and description are handled by the wrapping
+      // table.
+      $element = array(
+        '#title' => $is_multiple ? '' : $title,
+        '#description' => $is_multiple ? '' : $description,
+      );
+      $element = $this->formSingleElement($entity, $items, $delta, $langcode, $element, $form, $form_state);
+
+      if ($element) {
+        // Input field for the delta (drag-n-drop reordering).
+        if ($is_multiple) {
+          // We name the element '_weight' to avoid clashing with elements
+          // defined by widget.
+          $element['_weight'] = array(
+            '#type' => 'weight',
+            '#title' => t('Weight for row @number', array('@number' => $delta + 1)),
+            '#title_display' => 'invisible',
+            // Note: this 'delta' is the FAPI #type 'weight' element's property.
+            '#delta' => $max,
+            '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
+            '#weight' => 100,
+          );
+        }
+
+        $elements[$delta] = $element;
+      }
+    }
+
+    if ($elements) {
+      $elements += array(
+        '#theme' => 'field_multiple_value_form',
+        '#field_name' => $field['field_name'],
+        '#cardinality' => $field['cardinality'],
+        '#required' => $instance['required'],
+        '#title' => $title,
+        '#description' => $description,
+        '#prefix' => '<div id="' . $wrapper_id . '">',
+        '#suffix' => '</div>',
+        '#max_delta' => $max,
+      );
+
+      // Add 'add more' button, if not working with a programmed form.
+      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
+        $elements['add_more'] = array(
+          '#type' => 'submit',
+          '#name' => strtr($id_prefix, '-', '_') . '_add_more',
+          '#value' => t('Add another item'),
+          '#attributes' => array('class' => array('field-add-more-submit')),
+          '#limit_validation_errors' => array(array_merge($parents, array($field_name, $langcode))),
+          '#submit' => array('field_add_more_submit'),
+          '#ajax' => array(
+              'callback' => 'field_add_more_js',
+              'wrapper' => $wrapper_id,
+              'effect' => 'fade',
+          ),
+        );
+      }
+    }
+
+    return $elements;
+  }
+
+  /**
+   * Generates the form element for a single copy of the widget.
+   */
+  protected function formSingleElement(EntityInterface $entity, array $items, $delta, $langcode, array $element, array &$form, array &$form_state) {
+    $instance = $this->instance;
+    $field = $this->field;
+
+    $element += array(
+      '#entity_type' => $entity->entityType(),
+      '#bundle' => $entity->bundle(),
+      '#entity' => $entity,
+      '#field_name' => $field['field_name'],
+      '#language' => $langcode,
+      '#field_parents' => $form['#parents'],
+      '#columns' => array_keys($field['columns']),
+      // Only the first widget should be required.
+      '#required' => $delta == 0 && $instance['required'],
+      '#delta' => $delta,
+      '#weight' => $delta,
+    );
+
+    $element = $this->formElement($items, $delta, $element, $langcode, $form, $form_state);
+
+    if ($element) {
+      // Allow modules to alter the field widget form element.
+      $context = array(
+        'form' => $form,
+        'field' => $field,
+        'instance' => $instance,
+        'langcode' => $langcode,
+        'items' => $items,
+        'delta' => $delta,
+        'default' => !empty($entity->field_ui_default_value),
+      );
+      drupal_alter(array('field_widget_form', 'field_widget_' . $this->getPluginId() . '_form'), $element, $form_state, $context);
+    }
+
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::submit().
+   */
+  public function submit(EntityInterface $entity, $langcode, array &$items, array $form, array &$form_state) {
+    $field_name = $this->field['field_name'];
+
+    // Extract the values from $form_state['values'].
+    $path = array_merge($form['#parents'], array($field_name, $langcode));
+    $key_exists = NULL;
+    $values = drupal_array_get_nested_value($form_state['values'], $path, $key_exists);
+
+    if ($key_exists) {
+      // Remove the 'value' of the 'add more' button.
+      unset($values['add_more']);
+
+      // Let the widget turn the submitted values into actual field values.
+      // Make sure the '_weight' entries are persisted in the process.
+      $weights = array();
+      if (isset($values[0]['_weight'])) {
+        foreach ($values as $delta => $value) {
+          $weights[$delta] = $value['_weight'];
+        }
+      }
+      $items = $this->massageFormValues($values, $form, $form_state);
+
+      foreach ($items as $delta => &$item) {
+        // Put back the weight.
+        if (isset($weights[$delta])) {
+          $item['_weight'] = $weights[$delta];
+        }
+        // The tasks below are going to reshuffle deltas. Keep track of the
+        // original deltas for correct reporting of errors in flagErrors().
+        $item['_original_delta'] = $delta;
+      }
+
+      // Account for drag-n-drop reordering.
+      $this->sortItems($items);
+
+      // Remove empty values.
+      $items = _field_filter_items($this->field, $items);
+
+      // Put delta mapping in $form_state, so that flagErrors() can use it.
+      $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+      foreach ($items as $delta => &$item) {
+        $field_state['original_deltas'][$delta] = $item['_original_delta'];
+        unset($item['_original_delta']);
+      }
+      field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
+    }
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::flagErrors().
+   */
+  public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state) {
+    $field_name = $this->field['field_name'];
+
+    $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+
+    if (!empty($field_state['errors'])) {
+      // Locate the correct element in the the form.
+      $element = drupal_array_get_nested_value($form_state['complete_form'], $field_state['array_parents']);
+
+      // Only set errors if the element is accessible.
+      if (!isset($element['#access']) || $element['#access']) {
+        $definition = $this->getDefinition();
+        $is_multiple = $definition['multiple_values'];
+
+        foreach ($field_state['errors'] as $delta => $delta_errors) {
+          // For a multiple-value widget, pass all errors to the main widget.
+          // For single-value widgets, pass errors by delta.
+          if ($is_multiple) {
+            $delta_element = $element;
+          }
+          else {
+            $original_delta = $field_state['original_deltas'][$delta];
+            $delta_element = $element[$original_delta];
+          }
+          foreach ($delta_errors as $error) {
+            $error_element = $this->errorElement($delta_element, $error, $form, $form_state);
+            form_error($error_element, $error['message']);
+          }
+        }
+        // Reinitialize the errors list for the next submit.
+        $field_state['errors'] = array();
+        field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
+      }
+    }
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    return array();
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+   */
+  public function errorElement(array $element, array $error, array $form, array &$form_state) {
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::massageFormValues()
+   */
+  public function massageFormValues(array $values, array $form, array &$form_state) {
+    return $values;
+  }
+
+  /**
+   * Sorts submitted field values according to drag-n-drop reordering.
+   *
+   * @param array $items
+   *   The field values.
+   */
+  protected function sortItems(array &$items) {
+    $is_multiple = ($this->field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) || ($this->field['cardinality'] > 1);
+    if ($is_multiple && isset($items[0]['_weight'])) {
+      usort($items, function ($a, $b) {
+        $a_weight = (is_array($a) ? $a['_weight'] : 0);
+        $b_weight = (is_array($b) ? $b['_weight'] : 0);
+        return $a_weight - $b_weight;
+      });
+      // Remove the '_weight' entries.
+      foreach ($items as $delta => &$item) {
+        if (is_array($item)) {
+          unset($item['_weight']);
+        }
+      }
+    }
+  }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBaseInterface.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBaseInterface.php
new file mode 100644
index 0000000..8fdd785
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBaseInterface.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\Type\Widget\WidgetBaseInterface.
+ */
+
+namespace Drupal\field\Plugin\Type\Widget;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\Plugin\PluginSettingsInterface;
+
+/**
+ * Base interface definition for "Field widget" plugins.
+ *
+ * This interface details base wrapping methods that most widget implementations
+ * will want to directly inherit from Drupal\field\Plugin\Type\Widget\WidgetBase.
+ * See Drupal\field\Plugin\Type\Widget\WidgetInterface for methods that will more
+ * likely be overriden.
+ */
+interface WidgetBaseInterface extends PluginSettingsInterface {
+
+  /**
+   * Creates a form element for a field.
+   *
+   * If the entity associated with the form is new (i.e., $entity->isNew() is
+   * TRUE), the 'default value', if any, is pre-populated. Also allows other
+   * modules to alter the form element by implementing their own hooks.
+   *
+   * @param Drupal\Core\Entity\EntityInterface $entity
+   *   The entity for which the widget is being built.
+   * @param string $langcode
+   *   The language associated with the field.
+   * @param array $items
+   *   An array of the field values. When creating a new entity this may be NULL
+   *   or an empty array to use default values.
+   * @param array $form
+   *   An array representing the form that the editing element will be attached
+   *   to.
+   * @param array $form_state
+   *   An array containing the current state of the form.
+   * @param int $get_delta
+   *   Used to get only a specific delta value of a multiple value field.
+   *
+   * @return array
+   *   The form element array created for this field.
+   */
+  public function form(EntityInterface $entity, $langcode, array $items, array &$form, array &$form_state, $get_delta = NULL);
+
+  /**
+   * Extracts field values from submitted form values.
+   *
+   * @param Drupal\Core\Entity\EntityInterface $entity
+   *   The entity for which the widget is being submitted.
+   * @param string $langcode
+   *   The language associated to $items.
+   * @param array $items
+   *   The field values. This parameter is altered by reference to receive the
+   *   incoming form values.
+   * @param array $form
+   *   The form structure where field elements are attached to. This might be a
+   *   full form structure, or a sub-element of a larger form.
+   * @param array $form_state
+   *   The form state.
+   */
+  public function submit(EntityInterface $entity, $langcode, array &$items, array $form, array &$form_state);
+
+  /**
+   * Reports field-level validation errors against actual form elements.
+   *
+   * @param Drupal\Core\Entity\EntityInterface $entity
+   *   The entity for which the widget is being submitted.
+   * @param string $langcode
+   *   The language associated to $items.
+   * @param array $items
+   *   The field values.
+   * @param array $form
+   *   The form structure where field elements are attached to. This might be a
+   *   full form structure, or a sub-element of a larger form.
+   * @param array $form_state
+   *   The form state.
+   */
+  public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state);
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetFactory.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetFactory.php
new file mode 100644
index 0000000..851cbea
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetFactory.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\WidgetFactory.
+ */
+
+namespace Drupal\field\Plugin\Type\Widget;
+
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+
+/**
+ * Factory class for the Widget plugin type.
+ */
+class WidgetFactory extends DefaultFactory {
+
+  /**
+   * Overrides Drupal\Component\Plugin\Factory\DefaultFactory::createInstance().
+   */
+  public function createInstance($plugin_id, array $configuration) {
+    $plugin_class = $this->getPluginClass($plugin_id);
+    return new $plugin_class($plugin_id, $this->discovery, $configuration['instance'], $configuration['settings'], $configuration['weight']);
+  }
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php
new file mode 100644
index 0000000..c0ffc95
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetInterface.php
@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\Type\Widget\WidgetInterface.
+ */
+
+namespace Drupal\field\Plugin\Type\Widget;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\FieldInstance;
+
+/**
+ * Interface definition for field widget plugins.
+ *
+ * This interface details the methods that most plugin implementations will want
+ * to override. See Drupal\field\Plugin\Type\Widget\WidgetBaseInterface for base
+ * wrapping methods that should most likely be inherited directly from
+ * Drupal\field\Plugin\Type\Widget\WidgetBase..
+ */
+interface WidgetInterface extends WidgetBaseInterface {
+
+  /**
+   * Returns a form to configure settings for the widget.
+   *
+   * Invoked from field_ui_field_edit_form() to allow administrators to
+   * configure the widget. The field_ui module takes care of handling submitted
+   * form values.
+   *
+   * @param array $form
+   *   The form where the settings form is being included in.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   The form definition for the widget settings.
+   */
+  public function settingsForm(array $form, array &$form_state);
+
+  /**
+   * Returns the form for a single field widget.
+   *
+   * Field widget form elements should be based on the passed-in $element, which
+   * contains the base form element properties derived from the field
+   * configuration.
+   *
+   * The BaseWidget methods will set the weight, field name and delta values for
+   * each form element. If there are multiple values for this field, the
+   * formElement() method will be called as many times as needed.
+   *
+   * Note that, depending on the context in which the widget is being included
+   * (regular entity form, field configuration form, advanced search form...),
+   * the values for $field and $instance might be different from the "official"
+   * definitions returned by field_info_field() and field_info_instance().
+   * Examples: mono-value widget even if the field is multi-valued, non-required
+   * widget even if the field is 'required'...
+   *
+   * Therefore, the FAPI element callbacks (such as #process, #element_validate,
+   * #value_callback...) used by the widget cannot use the field_info_field()
+   * or field_info_instance() functions to retrieve the $field or $instance
+   * definitions they should operate on. The field_widget_field() and
+   * field_widget_instance() functions should be used instead to fetch the
+   * current working definitions from $form_state, where Field API stores them.
+   *
+   * Alternatively, hook_field_widget_form() can extract the needed specific
+   * properties from $field and $instance and set them as ad-hoc
+   * $element['#custom'] properties, for later use by its element callbacks.
+   *
+   * Other modules may alter the form element provided by this function using
+   * hook_field_widget_form_alter().
+   *
+   * @param array $items
+   *   Array of default values for this field.
+   * @param int $delta
+   *   The order of this item in the array of subelements (0, 1, 2, etc).
+   * @param array $element
+   *   A form element array containing basic properties for the widget:
+   *   - #entity_type: The name of the entity the field is attached to.
+   *   - #bundle: The name of the field bundle the field is contained in.
+   *   - #entity: The entity the field is attached to.
+   *   - #field_name: The name of the field.
+   *   - #language: The language the field is being edited in.
+   *   - #field_parents: The 'parents' space for the field in the form. Most
+   *       widgets can simply overlook this property. This identifies the
+   *       location where the field values are placed within
+   *       $form_state['values'], and is used to access processing information
+   *       for the field through the field_form_get_state() and
+   *       field_form_set_state() functions.
+   *   - #columns: A list of field storage columns of the field.
+   *   - #title: The sanitized element label for the field instance, ready for
+   *     output.
+   *   - #description: The sanitized element description for the field instance,
+   *     ready for output.
+   *   - #required: A Boolean indicating whether the element value is required;
+   *     for required multiple value fields, only the first widget's values are
+   *     required.
+   *   - #delta: The order of this item in the array of subelements; see $delta
+   *     above.
+   * @param string $langcode
+   *   The language associated with $items.
+   * @param string $form
+   *   The form structure where widgets are being attached to. This might be a
+   *   full form structure, or a sub-element of a larger form.
+   * @param string $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   The form elements for a single widget for this field.
+   *
+   * @see field_widget_field()
+   * @see field_widget_instance()
+   * @see hook_field_widget_form_alter()
+   * @see hook_field_widget_WIDGET_TYPE_form_alter()
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state);
+
+  /**
+   * Assigns a field-level validation error to the right widget sub-element.
+   *
+   * Depending on the widget's internal structure, a field-level validation
+   * error needs to be flagged on the right sub-element.
+   *
+   * @param array $element
+   *   An array containing the form element for the widget, as generated by
+   *   formElement().
+   * @param array $error
+   *   An associative array with the following key-value pairs, as returned by
+   *   hook_field_validate():
+   *   - error: the error code. Complex widgets might need to report different
+   *     errors to different form elements inside the widget.
+   *   - message: the human readable message to be displayed.
+   * @param array $form
+   *   The form structure where field elements are attached to. This might be a
+   *   full form structure, or a sub-element of a larger form.
+   * @param array $form_state
+   *   An associative array containing the current state of the form.
+   *
+   * @return array
+   *   The element on which the error should be flagged.
+   */
+  public function errorElement(array $element, array $error, array $form, array &$form_state);
+
+  /**
+   * Massages the form values into the format expected for field values.
+   *
+   * @param array $values
+   *   The submitted form values produced by the widget.
+   *   - If the widget does not manage multiple values itself, the array holds
+   *     the values generated by the multiple copies of the $element generated
+   *     by the formElement() method, keyed by delta.
+   *   - If the widget manages multiple values, the array holds the values
+   *     of the form element generated by the formElement() method.
+   * @param array $form
+   *   The form structure where field elements are attached to. This might be a
+   *   full form structure, or a sub-element of a larger form.
+   * @param array $form_state
+   *   The form state.
+   *
+   * @return array
+   *   An array of field values, keyed by delta.
+   */
+  public function massageFormValues(array $values, array $form, array &$form_state);
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
new file mode 100644
index 0000000..bb55d53
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\Type\Widget\WidgetPluginManager.
+ */
+
+namespace Drupal\field\Plugin\Type\Widget;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\field\Plugin\Type\LegacyDiscoveryDecorator;
+
+/**
+ * Plugin type manager for field widgets.
+ */
+class WidgetPluginManager extends PluginManagerBase {
+
+  protected $defaults = array(
+    'settings' => array(),
+    'multiple_values' => FALSE,
+  );
+
+  protected $cache_bin = 'field';
+  protected $cache_key = 'field_widget_types';
+  protected $hook = 'field_widget_info';
+
+  /**
+   * Constructs a WidgetPluginManager object.
+   */
+  public function __construct() {
+    $this->baseDiscovery = new LegacyDiscoveryDecorator(new AnnotatedClassDiscovery('field', 'widget'));
+    $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_key, $this->cache_bin);
+
+    $this->factory = new WidgetFactory($this);
+  }
+
+  /**
+   * Clear cached definitions.
+   *
+   * @todo Remove when http://drupal.org/node/1764232 is fixed.
+   */
+  public function clearDefinitions() {
+    // Clear 'static' data by creating a new object.
+    $this->discovery = new CacheDecorator($this->baseDiscovery, $this->cache_key, $this->cache_bin);
+    cache($this->cache_bin)->delete($this->cache_key);
+  }
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::getDefinition().
+   *
+   * @todo Remove when http://drupal.org/node/1778942 is fixed.
+   */
+  public function getDefinition($plugin_id) {
+    $definition = $this->discovery->getDefinition($plugin_id);
+    if (!empty($definition)) {
+      $this->processDefinition($definition, $plugin_id);
+      return $definition;;
+    }
+  }
+
+  /**
+   * Overrides Drupal\Component\Plugin\PluginManagerBase::getInstance().
+   */
+  public function getInstance(array $options) {
+    $instance = $options['instance'];
+    $type = $options['type'];
+
+    $definition = $this->getDefinition($type);
+    $field = field_info_field($instance['field_name']);
+
+    // Switch back to default widget if either:
+    // - $type_info doesn't exist (the widget type is unknown),
+    // - the field type is not allowed for the widget.
+    if (!isset($definition['class']) || !in_array($field['type'], $definition['field_types'])) {
+      // Grab the default widget for the field type.
+      $field_type_definition = field_info_field_types($field['type']);
+      $type = $field_type_definition['default_widget'];
+    }
+
+    $configuration = array(
+      'instance' => $instance,
+      'settings' => $options['settings'],
+      'weight' => $options['weight'],
+    );
+    return $this->createInstance($type, $configuration);
+  }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php b/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php
new file mode 100644
index 0000000..166bd87
--- /dev/null
+++ b/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field\Plugin\field\widget\LegacyWidget.
+ */
+
+namespace Drupal\field\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation for legacy widgets.
+ *
+ * This special implementation acts as a temporary BC layer for widgets that
+ * have not been converted to Plugins, and bridges new methods to the old-style
+ * hook_field_widget_*() callbacks.
+ *
+ * This class is not discovered by the annotations reader, but referenced by
+ * the Drupal\field\Plugin\Discovery\LegacyDiscoveryDecorator.
+ *
+ * @todo Remove once all core widgets have been converted.
+ */
+class LegacyWidget extends WidgetBase {
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $definition = $this->getDefinition();
+    $function = $definition['module'] . '_field_widget_settings_form';
+    if (function_exists($function)) {
+      return $function($this->field, $this->instance);
+    }
+    return array();
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $definition = $this->getDefinition();
+    $function = $definition['module'] . '_field_widget_form';
+
+    if (function_exists($function)) {
+      // hook_field_widget_form() implementations read widget properties directly
+      // from $instance. Put the actual properties we use here, which might have
+      // been altered by hook_field_widget_property().
+      $instance = clone $this->instance;
+      $instance['widget']['type'] = $this->getPluginId();
+      $instance['widget']['settings'] = $this->getSettings();
+
+      return $function($form, $form_state, $this->field, $instance, $langcode, $items, $delta, $element);
+    }
+    return array();
+  }
+
+  /**
+   * Overrides Drupal\field\Plugin\Type\Widget\WidgetBase::flagErrors().
+   *
+   * In D7, hook_field_widget_error() was supposed to call form_error() itself,
+   * whereas the new errorElement() method simply returns the element to flag.
+   * So we override the flagError() method to be more similar to the previous
+   * code in field_default_form_errors().
+   */
+  public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state) {
+    $field_name = $this->field['field_name'];
+
+    $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
+
+    if (!empty($field_state['errors'])) {
+      // Locate the correct element in the form.
+      $element = drupal_array_get_nested_value($form_state['complete_form'], $field_state['array_parents']);
+      // Only set errors if the element is accessible.
+      if (!isset($element['#access']) || $element['#access']) {
+        $definition = $this->getDefinition();
+        $is_multiple = $definition['multiple_values'];
+        $function = $definition['module'] . '_field_widget_error';
+        $function_exists = function_exists($function);
+
+        foreach ($field_state['errors'] as $delta => $delta_errors) {
+          // For multiple single-value widgets, pass errors by delta.
+          // For a multiple-value widget, pass all errors to the main widget.
+          $error_element = $is_multiple ? $element : $element[$delta];
+          foreach ($delta_errors as $error) {
+            if ($function_exists) {
+              $function($error_element, $error, $form, $form_state);
+            }
+            else {
+              // Make sure that errors are reported (even incorrectly flagged) if
+              // the widget module fails to implement hook_field_widget_error().
+              form_error($error_element, $error['message']);
+            }
+          }
+        }
+        // Reinitialize the errors list for the next submit.
+        $field_state['errors'] = array();
+        field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
+      }
+    }
+  }
+
+}
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 7155af9..28112d2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -52,15 +52,6 @@ class FieldInfoTest extends FieldTestBase {
       $this->assertEqual($info[$f_key]['module'], 'field_test',  t("Formatter type field_test module appears"));
     }
 
-    $widget_info = field_test_field_widget_info();
-    $info = field_info_widget_types();
-    foreach ($widget_info as $w_key => $widget) {
-      foreach ($widget as $key => $val) {
-        $this->assertEqual($info[$w_key][$key], $val, t("Widget type $w_key key $key is $val"));
-      }
-      $this->assertEqual($info[$w_key]['module'], 'field_test',  t("Widget type field_test module appears"));
-    }
-
     $storage_info = field_test_field_storage_info();
     $info = field_info_storage_types();
     foreach ($storage_info as $s_key => $storage) {
@@ -208,9 +199,10 @@ class FieldInfoTest extends FieldTestBase {
     $this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
 
     // Check that the default widget is used and expected settings are in place.
-    $this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
-    $widget_type = field_info_widget_types($instance['widget']['type']);
-    $this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
+    $widget = $instance->getWidget();
+    $this->assertIdentical($widget->getPluginId(), $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
+    $widget_type = $widget->getDefinition();
+    $this->assertIdentical($widget->getSettings(), $widget_type['settings'] , t('All expected widget settings are present.'));
 
     // Check that display settings are set for the 'default' mode.
     $display = $instance['display']['default'];
@@ -256,9 +248,9 @@ class FieldInfoTest extends FieldTestBase {
       $this->assertIdentical(field_info_instance_settings($type), $data['instance_settings'], "field_info_field_settings returns {$type}'s field instance settings");
     }
 
-    $info = field_test_field_widget_info();
-    foreach ($info as $type => $data) {
-      $this->assertIdentical(field_info_widget_settings($type), $data['settings'], "field_info_widget_settings returns {$type}'s widget settings");
+    foreach (array('test_field_widget', 'test_field_widget_multiple') as $type) {
+      $info = field_info_widget_types($type);
+      $this->assertIdentical(field_info_widget_settings($type), $info['settings'], "field_info_widget_settings returns {$type}'s widget settings");
     }
 
     $info = field_test_field_formatter_info();
diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
index dea9dc5..2e6121e 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php
@@ -67,9 +67,8 @@ class FormTest extends FieldTestBase {
     $this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
     // TODO : check that the widget is populated with default value ?
 
-    // Check that neither hook_field_widget_properties_alter nor
-    // hook_field_widget_form_alter() believe this is the default value form.
-    $this->assertNoText('From hook_field_widget_properties_alter(): Default form is true.', 'Not default value form in hook_field_widget_properties_alter().');
+    // Check that hook_field_widget_form_alter() does not believe this is the
+    // default value form.
     $this->assertNoText('From hook_field_widget_form_alter(): Default form is true.', 'Not default value form in hook_field_widget_form_alter().');
 
     // Submit with invalid value (field-level validation).
diff --git a/core/modules/field/modules/number/lib/Drupal/number/Plugin/field/widget/NumberWidget.php b/core/modules/field/modules/number/lib/Drupal/number/Plugin/field/widget/NumberWidget.php
new file mode 100644
index 0000000..ecaf204
--- /dev/null
+++ b/core/modules/field/modules/number/lib/Drupal/number/Plugin/field/widget/NumberWidget.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\number\Plugin\field\widget\NumberWidget.
+ */
+
+namespace Drupal\number\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation of the 'number' widget.
+ *
+ * @Plugin(
+ *   id = "number",
+ *   module = "number",
+ *   label = @Translation("Text field"),
+ *   field_types = {
+ *     "number_integer",
+ *     "number_decimal",
+ *     "number_float"
+ *   }
+ * )
+ */
+class NumberWidget extends WidgetBase {
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $field = $this->field;
+    $instance = $this->instance;
+
+    $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL;
+
+    $element += array(
+      '#type' => 'number',
+      '#default_value' => $value,
+    );
+
+    // Set the step for floating point and decimal numbers.
+    switch ($field['type']) {
+      case 'number_decimal':
+        $element['#step'] = pow(0.1, $field['settings']['scale']);
+        break;
+
+      case 'number_float':
+        $element['#step'] = 'any';
+        break;
+    }
+
+    // Set minimum and maximum.
+    if (is_numeric($instance['settings']['min'])) {
+      $element['#min'] = $instance['settings']['min'];
+    }
+    if (is_numeric($instance['settings']['max'])) {
+      $element['#max'] = $instance['settings']['max'];
+    }
+
+    // Add prefix and suffix.
+    if (!empty($instance['settings']['prefix'])) {
+      $prefixes = explode('|', $instance['settings']['prefix']);
+      $element['#field_prefix'] = field_filter_xss(array_pop($prefixes));
+    }
+    if (!empty($instance['settings']['suffix'])) {
+      $suffixes = explode('|', $instance['settings']['suffix']);
+      $element['#field_suffix'] = field_filter_xss(array_pop($suffixes));
+    }
+
+    return array('value' => $element);
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+   */
+  public function errorElement(array $element, array $error, array $form, array &$form_state) {
+    return $element['value'];
+  }
+
+}
diff --git a/core/modules/field/modules/number/number.module b/core/modules/field/modules/number/number.module
index 9985984..1a7024b 100644
--- a/core/modules/field/modules/number/number.module
+++ b/core/modules/field/modules/number/number.module
@@ -304,65 +304,3 @@ function number_field_formatter_view($entity_type, $entity, $field, $instance, $
 
   return $element;
 }
-
-/**
- * Implements hook_field_widget_info().
- */
-function number_field_widget_info() {
-  return array(
-    'number' => array(
-      'label' => t('Text field'),
-      'field types' => array('number_integer', 'number_decimal', 'number_float'),
-    ),
-  );
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function number_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
-  $value = isset($items[$delta]['value']) ? $items[$delta]['value'] : '';
-
-  $element += array(
-    '#type' => 'number',
-    '#default_value' => $value,
-  );
-
-  // Set the step for floating point and decimal numbers.
-  switch ($field['type']) {
-    case 'number_decimal':
-      $element['#step'] = pow(0.1, $field['settings']['scale']);
-      break;
-
-    case 'number_float':
-      $element['#step'] = 'any';
-      break;
-  }
-
-  // Set minimum and maximum.
-  if (is_numeric($instance['settings']['min'])) {
-    $element['#min'] = $instance['settings']['min'];
-  }
-  if (is_numeric($instance['settings']['max'])) {
-    $element['#max'] = $instance['settings']['max'];
-  }
-
-  // Add prefix and suffix.
-  if (!empty($instance['settings']['prefix'])) {
-    $prefixes = explode('|', $instance['settings']['prefix']);
-    $element['#field_prefix'] = field_filter_xss(array_pop($prefixes));
-  }
-  if (!empty($instance['settings']['suffix'])) {
-    $suffixes = explode('|', $instance['settings']['suffix']);
-    $element['#field_suffix'] = field_filter_xss(array_pop($suffixes));
-  }
-
-  return array('value' => $element);
-}
-
-/**
- * Implements hook_field_widget_error().
- */
-function number_field_widget_error($element, $error, $form, &$form_state) {
-  form_error($element['value'], $error['message']);
-}
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php
new file mode 100644
index 0000000..17f80a0
--- /dev/null
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWidget.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\text\Plugin\field\widget\TextareaWidget.
+ */
+
+namespace Drupal\text\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation of the 'text_textarea' widget.
+ *
+ * @Plugin(
+ *   id = "text_textarea",
+ *   module = "text",
+ *   label = @Translation("Text area (multiple rows)"),
+ *   field_types = {
+ *     "text_long"
+ *   },
+ *   settings = {
+ *     "rows" = "5"
+ *   }
+ * )
+ */
+class TextareaWidget extends WidgetBase {
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $element['rows'] = array(
+      '#type' => 'number',
+      '#title' => t('Rows'),
+      '#default_value' => $this->getSetting('rows'),
+      '#required' => TRUE,
+      '#min' => 1,
+    );
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $main_widget = $element + array(
+      '#type' => 'textarea',
+      '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+      '#rows' => $this->getSetting('rows'),
+      '#attributes' => array('class' => array('text-full')),
+    );
+
+    if ($this->instance['settings']['text_processing']) {
+      $element = $main_widget;
+      $element['#type'] = 'text_format';
+      $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
+      $element['#base_type'] = $main_widget['#type'];
+    }
+    else {
+      $element['value'] = $main_widget;
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php
new file mode 100644
index 0000000..373f1ed
--- /dev/null
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextareaWithSummaryWidget.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\text\Plugin\field\widget\TextareaWithSummaryWidget.
+ */
+
+namespace Drupal\text\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Plugin implementation of the 'text_textarea_with_summary' widget.
+ *
+ * @Plugin(
+ *   id = "text_textarea_with_summary",
+ *   module = "text",
+ *   label = @Translation("Text area with a summary"),
+ *   field_types = {
+ *     "text_with_summary"
+ *   },
+ *   settings = {
+ *     "rows" = "9",
+ *     "summary_rows" = "3"
+ *   }
+ * )
+ */
+class TextareaWithSummaryWidget extends TextareaWidget {
+
+  /**
+   * Overrides TextareaWidget::formElement().
+   */
+  function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $element = parent::formElement($items, $delta, $element, $langcode, $form, $form_state);
+
+    $display_summary = !empty($items[$delta]['summary']) || $this->instance['settings']['display_summary'];
+    $element['summary'] = array(
+      '#type' => $display_summary ? 'textarea' : 'value',
+      '#default_value' => isset($items[$delta]['summary']) ? $items[$delta]['summary'] : NULL,
+      '#title' => t('Summary'),
+      '#rows' => $this->getSetting('summary_rows'),
+      '#description' => t('Leave blank to use trimmed value of full text as the summary.'),
+      '#attached' => array(
+        'js' => array(drupal_get_path('module', 'text') . '/text.js'),
+      ),
+      '#attributes' => array('class' => array('text-summary')),
+      '#prefix' => '<div class="text-summary-wrapper">',
+      '#suffix' => '</div>',
+      '#weight' => -10,
+    );
+
+    return $element;
+  }
+
+  /**
+   * Overrides TextareaWidget::errorElement().
+   */
+  public function errorElement(array $element, array $error, array $form, array &$form_state) {
+    switch ($error['error']) {
+      case 'text_summary_max_length':
+        $error_element = $element['summary'];
+        break;
+
+      default:
+        $error_element = $element;
+        break;
+    }
+
+    return $error_element;
+  }
+
+}
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php
new file mode 100644
index 0000000..e6b87b1
--- /dev/null
+++ b/core/modules/field/modules/text/lib/Drupal/text/Plugin/field/widget/TextfieldWidget.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\text\Plugin\field\widget\TextfieldWidget.
+ */
+
+namespace Drupal\text\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation of the 'text_textfield' widget.
+ *
+ * @Plugin(
+ *   id = "text_textfield",
+ *   module = "text",
+ *   label = @Translation("Text field"),
+ *   field_types = {
+ *     "text"
+ *   },
+ *   settings = {
+ *     "size" = "60"
+ *   }
+ * )
+ */
+class TextfieldWidget extends WidgetBase {
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $element['size'] = array(
+      '#type' => 'number',
+      '#title' => t('Size of textfield'),
+      '#default_value' => $this->getSetting('size'),
+      '#required' => TRUE,
+      '#min' => 1,
+    );
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $main_widget = $element + array(
+      '#type' => 'textfield',
+      '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
+      '#size' => $this->getSetting('size'),
+      '#maxlength' => $this->field['settings']['max_length'],
+      '#attributes' => array('class' => array('text-full')),
+    );
+
+    if ($this->instance['settings']['text_processing']) {
+      $element = $main_widget;
+      $element['#type'] = 'text_format';
+      $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
+      $element['#base_type'] = $main_widget['#type'];
+    }
+    else {
+      $element['value'] = $main_widget;
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/modules/field/modules/text/text.module b/core/modules/field/modules/text/text.module
index dcf4d1e..bbd209e 100644
--- a/core/modules/field/modules/text/text.module
+++ b/core/modules/field/modules/text/text.module
@@ -444,140 +444,6 @@ function text_summary($text, $format = NULL, $size = NULL) {
 }
 
 /**
- * Implements hook_field_widget_info().
- */
-function text_field_widget_info() {
-  return array(
-    'text_textfield' => array(
-      'label' => t('Text field'),
-      'field types' => array('text'),
-      'settings' => array('size' => 60),
-    ),
-    'text_textarea' => array(
-      'label' => t('Text area (multiple rows)'),
-      'field types' => array('text_long'),
-      'settings' => array('rows' => 5),
-    ),
-    'text_textarea_with_summary' => array(
-      'label' => t('Text area with a summary'),
-      'field types' => array('text_with_summary'),
-      'settings' => array('rows' => 9, 'summary_rows' => 3),
-    ),
-  );
-}
-
-/**
- * Implements hook_field_widget_settings_form().
- */
-function text_field_widget_settings_form($field, $instance) {
-  $widget = $instance['widget'];
-  $settings = $widget['settings'];
-
-  if ($widget['type'] == 'text_textfield') {
-    $form['size'] = array(
-      '#type' => 'number',
-      '#title' => t('Size of textfield'),
-      '#default_value' => $settings['size'],
-      '#required' => TRUE,
-      '#min' => 1,
-    );
-  }
-  else {
-    $form['rows'] = array(
-      '#type' => 'number',
-      '#title' => t('Rows'),
-      '#default_value' => $settings['rows'],
-      '#required' => TRUE,
-      '#min' => 1,
-    );
-  }
-
-  return $form;
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function text_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
-  $summary_widget = array();
-  $main_widget = array();
-
-  switch ($instance['widget']['type']) {
-    case 'text_textfield':
-      $main_widget = $element + array(
-        '#type' => 'textfield',
-        '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
-        '#size' => $instance['widget']['settings']['size'],
-        '#maxlength' => $field['settings']['max_length'],
-        '#attributes' => array('class' => array('text-full')),
-      );
-      break;
-
-    case 'text_textarea_with_summary':
-      $display = !empty($items[$delta]['summary']) || !empty($instance['settings']['display_summary']);
-      $summary_widget = array(
-        '#type' => $display ? 'textarea' : 'value',
-        '#default_value' => isset($items[$delta]['summary']) ? $items[$delta]['summary'] : NULL,
-        '#title' => t('Summary'),
-        '#rows' => $instance['widget']['settings']['summary_rows'],
-        '#description' => t('Leave blank to use trimmed value of full text as the summary.'),
-        '#attached' => array(
-          'js' => array(drupal_get_path('module', 'text') . '/text.js'),
-        ),
-        '#attributes' => array('class' => array('text-summary')),
-        '#prefix' => '<div class="text-summary-wrapper">',
-        '#suffix' => '</div>',
-        '#weight' => -10,
-      );
-      // Fall through to the next case.
-
-    case 'text_textarea':
-      $main_widget = $element + array(
-        '#type' => 'textarea',
-        '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : NULL,
-        '#rows' => $instance['widget']['settings']['rows'],
-        '#attributes' => array('class' => array('text-full')),
-      );
-      break;
-  }
-
-  if ($main_widget) {
-    // Conditionally alter the form element's type if text processing is enabled.
-    if ($instance['settings']['text_processing']) {
-      $element = $main_widget;
-      $element['#type'] = 'text_format';
-      $element['#format'] = isset($items[$delta]['format']) ? $items[$delta]['format'] : NULL;
-      $element['#base_type'] = $main_widget['#type'];
-    }
-    else {
-      $element['value'] = $main_widget;
-    }
-  }
-  if ($summary_widget) {
-    $element['summary'] = $summary_widget;
-  }
-
-  return $element;
-}
-
-/**
- * Implements hook_field_widget_error().
- */
-function text_field_widget_error($element, $error, $form, &$form_state) {
-  switch ($error['error']) {
-    case 'text_summary_max_length':
-      $error_element = $element[$element['#columns'][1]];
-      break;
-
-    default:
-      $error_element = $element[$element['#columns'][0]];
-      break;
-  }
-
-  form_error($error_element, $error['message']);
-}
-
-/**
  * Implements hook_field_prepare_translation().
  */
 function text_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index 4819b3f..7acb4bb 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -337,9 +337,11 @@ function field_test_entity_nested_form($form, &$form_state, $entity_1, $entity_2
  */
 function field_test_entity_nested_form_validate($form, &$form_state) {
   $entity_1 = entity_create('test_entity', $form_state['values']);
+  field_attach_submit('test_entity', $entity_1, $form, $form_state);
   field_attach_form_validate('test_entity', $entity_1, $form, $form_state);
 
   $entity_2 = entity_create('test_entity', $form_state['values']['entity_2']);
+  field_attach_submit('test_entity', $entity_2, $form['entity_2'], $form_state);
   field_attach_form_validate('test_entity', $entity_2, $form['entity_2'], $form_state);
 }
 
diff --git a/core/modules/field/tests/modules/field_test/field_test.field.inc b/core/modules/field/tests/modules/field_test/field_test.field.inc
index 8933d8b..b2ed35c 100644
--- a/core/modules/field/tests/modules/field_test/field_test.field.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.field.inc
@@ -163,53 +163,6 @@ function field_test_field_instance_settings_form($field, $instance) {
 }
 
 /**
- * Implements hook_field_widget_info().
- */
-function field_test_field_widget_info() {
-  return array(
-    'test_field_widget' => array(
-      'label' => t('Test field'),
-      'field types' => array('test_field', 'hidden_test_field'),
-      'settings' => array('test_widget_setting' => 'dummy test string'),
-    ),
-    'test_field_widget_multiple' => array(
-      'label' => t('Test field 1'),
-      'field types' => array('test_field'),
-      'settings' => array('test_widget_setting_multiple' => 'dummy test string'),
-      'behaviors' => array(
-        'multiple values' => FIELD_BEHAVIOR_CUSTOM,
-      ),
-    ),
-  );
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function field_test_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
-  switch ($instance['widget']['type']) {
-    case 'test_field_widget':
-      $element += array(
-        '#type' => 'textfield',
-        '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
-      );
-      return array('value' => $element);
-
-    case 'test_field_widget_multiple':
-      $values = array();
-      foreach ($items as $delta => $value) {
-        $values[] = $value['value'];
-      }
-      $element += array(
-        '#type' => 'textfield',
-        '#default_value' => implode(', ', $values),
-        '#element_validate' => array('field_test_widget_multiple_validate'),
-      );
-      return $element;
-  }
-}
-
-/**
  * Form element validation handler for 'test_field_widget_multiple' widget.
  */
 function field_test_widget_multiple_validate($element, &$form_state) {
@@ -222,42 +175,6 @@ function field_test_widget_multiple_validate($element, &$form_state) {
 }
 
 /**
- * Implements hook_field_widget_error().
- */
-function field_test_field_widget_error($element, $error, $form, &$form_state) {
-  // @todo No easy way to differenciate widget types, we should receive it as a
-  // parameter.
-  if (isset($element['value'])) {
-    // Widget is test_field_widget.
-    $error_element = $element['value'];
-  }
-  else {
-    // Widget is test_field_widget_multiple.
-    $error_element = $element;
-  }
-
-  form_error($error_element, $error['message']);
-}
-
-/**
- * Implements hook_field_widget_settings_form().
- */
-function field_test_field_widget_settings_form($field, $instance) {
-  $widget = $instance['widget'];
-  $settings = $widget['settings'];
-
-  $form['test_widget_setting'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Field test field widget setting'),
-    '#default_value' => $settings['test_widget_setting'],
-    '#required' => FALSE,
-    '#description' => t('A dummy form element to simulate field widget setting.'),
-  );
-
-  return $form;
-}
-
-/**
  * Implements hook_field_formatter_info().
  */
 function field_test_field_formatter_info() {
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 6dfbb15..f4bb287 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -238,11 +238,6 @@ function field_test_field_widget_properties_alter(&$widget, $context) {
   if (in_array($context['entity_type'], array('node', 'comment')) && ($context['field']['field_name'] == 'alter_test_text')) {
     $widget['settings']['size'] = 42;
   }
-  // Set a message if this is for the form displayed to set default value for
-  // the field instance.
-  if ($context['default']) {
-    drupal_set_message('From hook_field_widget_properties_alter(): Default form is true.');
-  }
 }
 
 /**
@@ -261,11 +256,11 @@ function field_test_field_widget_properties_user_alter(&$widget, $context) {
 function field_test_field_widget_form_alter(&$element, &$form_state, $context) {
   switch ($context['field']['field_name']) {
     case 'alter_test_text':
-      drupal_set_message('Field size: ' . $context['instance']['widget']['settings']['size']);
+      drupal_set_message('Field size: ' . $context['instance']->getWidget()->getSetting('size'));
       break;
 
     case 'alter_test_options':
-      drupal_set_message('Widget type: ' . $context['instance']['widget']['type']);
+      drupal_set_message('Widget type: ' . $context['instance']->getWidget()->getPluginId());
       break;
   }
   // Set a message if this is for the form displayed to set default value for
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidget.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidget.php
new file mode 100644
index 0000000..55471ba
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidget.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_test\Plugin\field\widget\TestFieldWidget.
+ */
+
+namespace Drupal\field_test\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation of the 'test_field_widget' widget.
+ *
+ * @Plugin(
+ *   id = "test_field_widget",
+ *   module = "field_test",
+ *   label = @Translation("Test widget"),
+ *   field_types = {
+ *      "test_field",
+ *      "hidden_test_field"
+ *   },
+ *   settings = {
+ *     "test_widget_setting" = "dummy test string"
+ *   }
+ * )
+ */
+class TestFieldWidget extends WidgetBase {
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $element['test_widget_setting'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Field test field widget setting'),
+      '#description' => t('A dummy form element to simulate field widget setting.'),
+      '#default_value' => $this->getSetting('test_widget_setting'),
+      '#required' => FALSE,
+    );
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $element += array(
+      '#type' => 'textfield',
+      '#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
+    );
+    return array('value' => $element);
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+   */
+  public function errorElement(array $element, array $error, array $form, array &$form_state) {
+    return $element['value'];
+  }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
new file mode 100644
index 0000000..ead51bd
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_test\Plugin\field\widget\TestFieldWidgetMultiple.
+ */
+
+namespace Drupal\field_test\Plugin\field\widget;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation of the 'test_field_widget_multiple' widget.
+ *
+ * @Plugin(
+ *   id = "test_field_widget_multiple",
+ *   module = "field_test",
+ *   label = @Translation("Test widget - multiple"),
+ *   field_types = {
+ *      "test_field"
+ *   },
+ *   settings = {
+ *     "test_widget_setting_multiple" = "dummy test string"
+ *   },
+ *   multiple_values = TRUE
+ * )
+ */
+class TestFieldWidgetMultiple extends WidgetBase {
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::settingsForm().
+   */
+  public function settingsForm(array $form, array &$form_state) {
+    $element['test_field_widget_multiple'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Field test field widget setting'),
+      '#description' => t('A dummy form element to simulate field widget setting.'),
+      '#default_value' => $this->getSetting('test_widget_setting'),
+      '#required' => FALSE,
+    );
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
+   */
+  public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    $values = array();
+    foreach ($items as $delta => $value) {
+      $values[] = $value['value'];
+    }
+    $element += array(
+      '#type' => 'textfield',
+      '#default_value' => implode(', ', $values),
+      '#element_validate' => array('field_test_widget_multiple_validate'),
+    );
+    return $element;
+  }
+
+  /**
+   * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::errorElement().
+   */
+  public function errorElement(array $element, array $error, array $form, array &$form_state) {
+    return $element;
+  }
+
+}
diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc
index bb99234..54dc192 100644
--- a/core/modules/field_ui/field_ui.admin.inc
+++ b/core/modules/field_ui/field_ui.admin.inc
@@ -5,6 +5,8 @@
  * Administrative interface for custom field type creation.
  */
 
+use Drupal\field\FieldInstance;
+
 /**
  * Page callback: Lists all defined fields for quick reference.
  *
@@ -307,7 +309,6 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
   // Gather bundle information.
   $instances = field_info_instances($entity_type, $bundle);
   $field_types = field_info_field_types();
-  $widget_types = field_info_widget_types();
 
   $extra_fields = field_info_extra_fields($entity_type, $bundle, 'form');
 
@@ -345,6 +346,8 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
   foreach ($instances as $name => $instance) {
     $field = field_info_field($instance['field_name']);
     $admin_field_path = $admin_path . '/fields/' . $instance['field_name'];
+    $widget_definition = $instance->getWidget()->getDefinition();
+
     $table[$name] = array(
       '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')),
       '#row_type' => 'field',
@@ -387,7 +390,7 @@ function field_ui_field_overview_form($form, &$form_state, $entity_type, $bundle
       ),
       'widget_type' => array(
         '#type' => 'link',
-        '#title' => t($widget_types[$instance['widget']['type']]['label']),
+        '#title' => $widget_definition['label'],
         '#href' => $admin_field_path . '/widget-type',
         '#options' => array('attributes' => array('title' => t('Change widget type.'))),
       ),
@@ -1475,8 +1478,10 @@ function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) {
   if (!isset($options)) {
     $options = array();
     $field_types = field_info_field_types();
-    foreach (field_info_widget_types() as $name => $widget_type) {
-      foreach ($widget_type['field types'] as $widget_field_type) {
+    $widget_types = field_info_widget_types();
+    uasort($widget_types, 'drupal_sort_weight');
+    foreach ($widget_types as $name => $widget_type) {
+      foreach ($widget_type['field_types'] as $widget_field_type) {
         // Check that the field type exists.
         if (isset($field_types[$widget_field_type])) {
           $options[$widget_field_type][$name] = $widget_type['label'];
@@ -1657,7 +1662,7 @@ function field_ui_field_settings_form_submit($form, &$form_state) {
  * @see field_ui_widget_type_form_submit()
  * @ingroup forms
  */
-function field_ui_widget_type_form($form, &$form_state, $instance) {
+function field_ui_widget_type_form($form, &$form_state, FieldInstance $instance) {
   drupal_set_title($instance['label']);
 
   $bundle = $instance['bundle'];
@@ -1666,7 +1671,7 @@ function field_ui_widget_type_form($form, &$form_state, $instance) {
 
   $field = field_info_field($field_name);
   $field_type = field_info_field_types($field['type']);
-  $widget_type = field_info_widget_types($instance['widget']['type']);
+  $widget_definition = $instance->getWidget()->getDefinition();
   $bundles = field_info_bundles();
   $bundle_label = $bundles[$entity_type][$bundle]['label'];
 
@@ -1685,7 +1690,7 @@ function field_ui_widget_type_form($form, &$form_state, $instance) {
     '#title' => t('Widget type'),
     '#required' => TRUE,
     '#options' => field_ui_widget_type_options($field['type']),
-    '#default_value' => $instance['widget']['type'],
+    '#default_value' => $instance->getWidget()->getPluginId(),
     '#description' => t('The type of form element you would like to present to the user when creating this field in the %type type.', array('%type' => $bundle_label)),
   );
 
@@ -1817,6 +1822,10 @@ function field_ui_field_edit_form($form, &$form_state, $instance) {
 
   $form['#field'] = $field;
   $form['#instance'] = $instance;
+  // Create an arbitrary entity object (used by the 'default value' widget).
+  $ids = (object) array('entity_type' => $instance['entity_type'], 'bundle' => $instance['bundle'], 'entity_id' => NULL);
+  $form['#entity'] = _field_create_entity_from_ids($ids);
+  $form['#entity']->field_ui_default_value = TRUE;
 
   if (!empty($field['locked'])) {
     $form['locked'] = array(
@@ -1906,12 +1915,9 @@ function field_ui_field_edit_form($form, &$form_state, $instance) {
     $form['instance']['settings'] = $additions;
   }
 
-  // Add additional widget settings from the widget module.
-  $additions = module_invoke($widget_type['module'], 'field_widget_settings_form', $field, $instance);
-  if (is_array($additions)) {
-    $form['instance']['widget']['settings'] = $additions;
-    $form['instance']['widget']['active']['#value'] = 1;
-  }
+  // Add widget settings for the widget type.
+  $additions = $instance->getWidget()->settingsForm($form, $form_state);
+  $form['instance']['widget']['settings'] = $additions ? $additions : array('#type' => 'value', '#value' => array());
 
   // Add handling for default value if not provided by any other module.
   if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && empty($instance['default_value_function'])) {
@@ -2018,6 +2024,7 @@ function field_ui_field_edit_instance_pre_render($element) {
  */
 function field_ui_default_value_widget($field, $instance, &$form, &$form_state) {
   $field_name = $field['field_name'];
+  $entity = $form['#entity'];
 
   $element = array(
     '#type' => 'fieldset',
@@ -2031,13 +2038,15 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
     '#parents' => array(),
   );
 
-  // Insert the widget.
-  $items = $instance['default_value'];
+  // Adjust the instance definition used for the form element. We want a
+  // non-required input and no description.
   $instance['required'] = FALSE;
   $instance['description'] = '';
 
-  // @todo Allow multiple values (requires more work on 'add more' JS handler).
-  $element += field_default_form($instance['entity_type'], NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state, 0);
+  // Insert the widget. Since we do not use the "official" instance definition,
+  // the whole flow cannot use field_invoke_method().
+  $items = (array) $instance['default_value'];
+  $element += $instance->getWidget()->form($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
 
   return $element;
 }
@@ -2050,31 +2059,36 @@ function field_ui_default_value_widget($field, $instance, &$form, &$form_state)
 function field_ui_field_edit_form_validate($form, &$form_state) {
   // Take the incoming values as the $instance definition, so that the 'default
   // value' gets validated using the instance settings being submitted.
-  $instance = $form_state['values']['instance'];
+  $instance = $form['#instance'];
   $field_name = $instance['field_name'];
+  $entity = $form['#entity'];
 
   if (isset($form['instance']['default_value_widget'])) {
     $element = $form['instance']['default_value_widget'];
 
-    $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state);
-    $field = $field_state['field'];
-
     // Extract the 'default value'.
     $items = array();
-    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
+    $instance->getWidget()->submit($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
 
-    // Validate the value and report errors.
+    // Grab the field definition from $form_state.
+    $field_state = field_form_get_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state);
+    $field = $field_state['field'];
+
+    // Validate the value.
     $errors = array();
     $function = $field['module'] . '_field_validate';
     if (function_exists($function)) {
       $function(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $errors);
     }
+
+    // Report errors.
     if (isset($errors[$field_name][LANGUAGE_NOT_SPECIFIED])) {
       // Store reported errors in $form_state.
       $field_state['errors'] = $errors[$field_name][LANGUAGE_NOT_SPECIFIED];
       field_form_set_state($element['#parents'], $field_name, LANGUAGE_NOT_SPECIFIED, $form_state, $field_state);
+
       // Assign reported errors to the correct form element.
-      field_default_form_errors(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
+      $instance->getWidget()->flagErrors($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
     }
   }
 }
@@ -2085,12 +2099,12 @@ function field_ui_field_edit_form_validate($form, &$form_state) {
  * @see field_ui_field_edit_form_validate().
  */
 function field_ui_field_edit_form_submit($form, &$form_state) {
-  $instance = $form_state['values']['instance'];
-  $field = $form_state['values']['field'];
+  $instance = $form['#instance'];
+  $field = $form['#field'];
+  $entity = $form['#entity'];
 
-  // Update any field settings that have changed.
-  $field_source = field_info_field($instance['field_name']);
-  $field = array_merge($field_source, $field);
+  // Merge incoming values into the field.
+  $field = array_merge($field, $form_state['values']['field']);
   try {
     field_update_field($field);
   }
@@ -2105,15 +2119,15 @@ function field_ui_field_edit_form_submit($form, &$form_state) {
 
     // Extract field values.
     $items = array();
-    field_default_extract_form_values(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
-    field_default_submit(NULL, NULL, $field, $instance, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
+    $instance->getWidget()->submit($entity, LANGUAGE_NOT_SPECIFIED, $items, $element, $form_state);
 
     $instance['default_value'] = $items ? $items : NULL;
   }
 
-  // Retrieve the stored instance settings to merge with the incoming values.
-  $instance_source = field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle']);
-  $instance = array_merge($instance_source, $instance);
+  // Merge incoming values into the instance.
+  foreach ($form_state['values']['instance'] as $key => $value) {
+    $instance[$key] = $value;
+  }
   field_update_instance($instance);
 
   drupal_set_message(t('Saved %label configuration.', array('%label' => $instance['label'])));
diff --git a/core/modules/field_ui/field_ui.api.php b/core/modules/field_ui/field_ui.api.php
index 80cbe78..e33fbdd 100644
--- a/core/modules/field_ui/field_ui.api.php
+++ b/core/modules/field_ui/field_ui.api.php
@@ -91,47 +91,6 @@ function hook_field_instance_settings_form($field, $instance) {
 }
 
 /**
- * Add settings to a widget settings form.
- *
- * Invoked from field_ui_field_edit_form() to allow the module defining the
- * widget to add settings for a widget instance.
- *
- * @param $field
- *   The field structure being configured.
- * @param $instance
- *   The instance structure being configured.
- *
- * @return
- *   The form definition for the widget settings.
- */
-function hook_field_widget_settings_form($field, $instance) {
-  $widget = $instance['widget'];
-  $settings = $widget['settings'];
-
-  if ($widget['type'] == 'text_textfield') {
-    $form['size'] = array(
-      '#type' => 'number',
-      '#title' => t('Size of textfield'),
-      '#default_value' => $settings['size'],
-      '#min' => 1,
-      '#required' => TRUE,
-    );
-  }
-  else {
-    $form['rows'] = array(
-      '#type' => 'number',
-      '#title' => t('Rows'),
-      '#default_value' => $settings['rows'],
-      '#min' => 1,
-      '#required' => TRUE,
-    );
-  }
-
-  return $form;
-}
-
-
-/**
  * Specify the form elements for a formatter's settings.
  *
  * @param $field
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php
index 687aa2e..7ee9501 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/AlterTest.php
@@ -69,10 +69,8 @@ class AlterTest extends WebTestBase {
     // size when the form is displayed.
     $this->drupalGet('admin/structure/types/manage/article/fields/alter_test_text');
     $this->assertText('Field size: 42', 'Altered field size is found in hook_field_widget_form_alter().');
-    // Test that hook_field_widget_properties_alter() and
-    // hook_field_widget_form_alter() registers this is the default value form
-    // and sets a message.
-    $this->assertText('From hook_field_widget_properties_alter(): Default form is true.', 'Default value form detected in hook_field_widget_properties_alter().');
+    // Test that hook_field_widget_form_alter() registers this is the default
+    // value form and sets a message.
     $this->assertText('From hook_field_widget_form_alter(): Default form is true.', 'Default value form detected in hook_field_widget_form_alter().');
 
     // Create the alter_test_options field.
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 61b018a..8e5a18c 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -255,9 +255,8 @@ function forum_uri($forum) {
  */
 function _forum_node_check_node_type(Node $node) {
   // Fetch information about the forum field.
-  $field = field_info_instance('node', 'taxonomy_forums', $node->type);
-
-  return is_array($field);
+  $instance = field_info_instance('node', 'taxonomy_forums', $node->type);
+  return !empty($instance);
 }
 
 /**
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
index a72e052..f7670fe 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageFieldDefaultImagesTest.php
@@ -56,12 +56,17 @@ class ImageFieldDefaultImagesTest extends ImageFieldTestBase {
     $instance = field_info_instance('node', $field_name, 'article');
 
     // Add another instance with another default image to the page content type.
-    $instance2 = array_merge($instance, array(
+    $instance2 = array(
+      'field_name' => $field['field_name'],
+      'entity_type' => 'node',
       'bundle' => 'page',
+      'label' => $instance['label'],
+      'required' => $instance['required'],
       'settings' => array(
         'default_image' => $default_images['instance2']->fid,
       ),
-    ));
+      'widget' => $instance['widget'],
+    );
     field_create_instance($instance2);
     $instance2 = field_info_instance('node', $field_name, 'page');
 
