diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc
index 3b5c277..2246fec 100644
--- a/core/includes/ajax.inc
+++ b/core/includes/ajax.inc
@@ -165,7 +165,7 @@
  *
  * @deprecated Use \Drupal\Core\Render\Element\FormElement::processAjaxForm().
  */
-function ajax_process_form($element, FormStateInterface $form_state, &$complete_form) {
+function ajax_process_form(&$element, FormStateInterface $form_state, &$complete_form) {
   return Element\FormElement::processAjaxForm($element, $form_state, $complete_form);
 }
 
diff --git a/core/includes/form.inc b/core/includes/form.inc
index fb9add4..3fa28bb 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -268,49 +268,10 @@ function form_state_values_clean(FormStateInterface $form_state) {
 /**
  * Determines the value for an image button form element.
  *
- * @param $form
- *   The form element whose value is being populated.
- * @param $input
- *   The incoming input to populate the form element. If this is FALSE,
- *   the element's default value should be returned.
- * @param $form_state
- *   The current state of the form.
- *
- * @return
- *   The data that will appear in the $form_state->getValues() collection
- *   for this element. Return nothing to use the default.
+ * @deprecated Use \Drupal\Core\Render\Element\ImageButton::valueCallback().
  */
 function form_type_image_button_value($form, $input, FormStateInterface $form_state) {
-  if ($input !== FALSE) {
-    if (!empty($input)) {
-      // If we're dealing with Mozilla or Opera, we're lucky. It will
-      // return a proper value, and we can get on with things.
-      return $form['#return_value'];
-    }
-    else {
-      // Unfortunately, in IE we never get back a proper value for THIS
-      // form element. Instead, we get back two split values: one for the
-      // X and one for the Y coordinates on which the user clicked the
-      // button. We'll find this element in the #post data, and search
-      // in the same spot for its name, with '_x'.
-      $input = $form_state['input'];
-      foreach (explode('[', $form['#name']) as $element_name) {
-        // chop off the ] that may exist.
-        if (substr($element_name, -1) == ']') {
-          $element_name = substr($element_name, 0, -1);
-        }
-
-        if (!isset($input[$element_name])) {
-          if (isset($input[$element_name . '_x'])) {
-            return $form['#return_value'];
-          }
-          return NULL;
-        }
-        $input = $input[$element_name];
-      }
-      return $form['#return_value'];
-    }
-  }
+  return Element\ImageButton::valueCallback($form, $input, $form_state);
 }
 
 /**
@@ -568,7 +529,7 @@ function form_type_select_value($element, $input = FALSE) {
  *
  * @deprecated Use \Drupal\Core\Render\Element\Textfield::valueCallback().
  */
-function form_type_textfield_value(&$element, $input, &$form_state) {
+function form_type_textfield_value(&$element, $input, FormStateInterface $form_state) {
   return Element\Textfield::valueCallback($element, $input, $form_state);
 }
 
@@ -1151,15 +1112,11 @@ function form_pre_render_conditional_form_element($element) {
 
 /**
  * Processes a form button element.
+ *
+ * @deprecated Use \Drupal\Core\Render\Element\Button::processButton().
  */
-function form_process_button($element, FormStateInterface $form_state) {
-  // If this is a button intentionally allowing incomplete form submission
-  // (e.g., a "Previous" or "Add another item" button), then also skip
-  // client-side validation.
-  if (isset($element['#limit_validation_errors']) && $element['#limit_validation_errors'] !== FALSE) {
-    $element['#attributes']['formnovalidate'] = 'formnovalidate';
-  }
-  return $element;
+function form_process_button(&$element, FormStateInterface $form_state, &$complete_form) {
+  return Element\Button::processButton($element, $form_state, $complete_form);
 }
 
 /**
@@ -1296,7 +1253,7 @@ function form_pre_render_actions_dropbutton(array $element) {
  *
  * @deprecated Use \Drupal\Core\Render\Element\FormElement::processPattern().
  */
-function form_process_pattern($element, FormStateInterface $form_state, &$complete_form) {
+function form_process_pattern(&$element, FormStateInterface $form_state, &$complete_form) {
   return Element\FormElement::processPattern($element, $form_state, $complete_form);
 }
 
@@ -1682,181 +1639,28 @@ function form_validate_table($element, FormStateInterface $form_state) {
 /**
  * Processes a machine-readable name form element.
  *
- * @param $element
- *   The form element to process. Properties used:
- *   - #machine_name: An associative array containing:
- *     - exists: A callable to invoke for checking whether a submitted machine
- *       name value already exists. The submitted value is passed as an
- *       argument. In most cases, an existing API or menu argument loader
- *       function can be re-used. The callback is only invoked, if the submitted
- *       value differs from the element's #default_value.
- *     - source: (optional) The #array_parents of the form element containing
- *       the human-readable name (i.e., as contained in the $form structure) to
- *       use as source for the machine name. Defaults to array('label').
- *     - label: (optional) A text to display as label for the machine name value
- *       after the human-readable name form element. Defaults to "Machine name".
- *     - replace_pattern: (optional) A regular expression (without delimiters)
- *       matching disallowed characters in the machine name. Defaults to
- *       '[^a-z0-9_]+'.
- *     - replace: (optional) A character to replace disallowed characters in the
- *       machine name via JavaScript. Defaults to '_' (underscore). When using a
- *       different character, 'replace_pattern' needs to be set accordingly.
- *     - error: (optional) A custom form error message string to show, if the
- *       machine name contains disallowed characters.
- *     - standalone: (optional) Whether the live preview should stay in its own
- *       form element rather than in the suffix of the source element. Defaults
- *       to FALSE.
- *   - #maxlength: (optional) Should be set to the maximum allowed length of the
- *     machine name. Defaults to 64.
- *   - #disabled: (optional) Should be set to TRUE in case an existing machine
- *     name must not be changed after initial creation.
+ * @deprecated Use \Drupal\Core\Render\Element\MachineName::processMachineName().
  */
-function form_process_machine_name($element, FormStateInterface $form_state) {
-  // We need to pass the langcode to the client.
-  $language = \Drupal::languageManager()->getCurrentLanguage();
-
-  // Apply default form element properties.
-  $element += array(
-    '#title' => t('Machine-readable name'),
-    '#description' => t('A unique machine-readable name. Can only contain lowercase letters, numbers, and underscores.'),
-    '#machine_name' => array(),
-    '#field_prefix' => '',
-    '#field_suffix' => '',
-    '#suffix' => '',
-  );
-  // A form element that only wants to set one #machine_name property (usually
-  // 'source' only) would leave all other properties undefined, if the defaults
-  // were defined in hook_element_info(). Therefore, we apply the defaults here.
-  $element['#machine_name'] += array(
-    'source' => array('label'),
-    'target' => '#' . $element['#id'],
-    'label' => t('Machine name'),
-    'replace_pattern' => '[^a-z0-9_]+',
-    'replace' => '_',
-    'standalone' => FALSE,
-    'field_prefix' => $element['#field_prefix'],
-    'field_suffix' => $element['#field_suffix'],
-  );
-
-  // By default, machine names are restricted to Latin alphanumeric characters.
-  // So, default to LTR directionality.
-  if (!isset($element['#attributes'])) {
-    $element['#attributes'] = array();
-  }
-  $element['#attributes'] += array('dir' => 'ltr');
-
-  // The source element defaults to array('name'), but may have been overidden.
-  if (empty($element['#machine_name']['source'])) {
-    return $element;
-  }
-
-  // Retrieve the form element containing the human-readable name from the
-  // complete form in $form_state. By reference, because we may need to append
-  // a #field_suffix that will hold the live preview.
-  $key_exists = NULL;
-  $source = NestedArray::getValue($form_state->getCompleteForm(), $element['#machine_name']['source'], $key_exists);
-  if (!$key_exists) {
-    return $element;
-  }
-
-  $suffix_id = $source['#id'] . '-machine-name-suffix';
-  $element['#machine_name']['suffix'] = '#' . $suffix_id;
-
-  if ($element['#machine_name']['standalone']) {
-    $element['#suffix'] = SafeMarkup::set($element['#suffix'] . ' <small id="' . $suffix_id . '">&nbsp;</small>');
-  }
-  else {
-    // Append a field suffix to the source form element, which will contain
-    // the live preview of the machine name.
-    $source += array('#field_suffix' => '');
-    $source['#field_suffix'] = SafeMarkup::set($source['#field_suffix'] . ' <small id="' . $suffix_id . '">&nbsp;</small>');
-
-    $parents = array_merge($element['#machine_name']['source'], array('#field_suffix'));
-    NestedArray::setValue($form_state->getCompleteForm(), $parents, $source['#field_suffix']);
-  }
-
-  $js_settings = array(
-    'type' => 'setting',
-    'data' => array(
-      'machineName' => array(
-        '#' . $source['#id'] => $element['#machine_name'],
-      ),
-      'langcode' => $language->id,
-    ),
-  );
-  $element['#attached']['library'][] = 'core/drupal.machine-name';
-  $element['#attached']['js'][] = $js_settings;
-
-  return $element;
+function form_process_machine_name(&$element, FormStateInterface $form_state, &$complete_form) {
+  return Element\MachineName::processMachineName($element, $form_state, $complete_form);
 }
 
 /**
  * Form element validation handler for machine_name elements.
  *
- * Note that #maxlength is validated by _form_validate() already.
+ * @deprecated Use \Drupal\Core\Render\Element\MachineName::validateMachineName().
  */
-function form_validate_machine_name(&$element, FormStateInterface $form_state) {
-  // Verify that the machine name not only consists of replacement tokens.
-  if (preg_match('@^' . $element['#machine_name']['replace'] . '+$@', $element['#value'])) {
-    form_error($element, $form_state, t('The machine-readable name must contain unique characters.'));
-  }
-
-  // Verify that the machine name contains no disallowed characters.
-  if (preg_match('@' . $element['#machine_name']['replace_pattern'] . '@', $element['#value'])) {
-    if (!isset($element['#machine_name']['error'])) {
-      // Since a hyphen is the most common alternative replacement character,
-      // a corresponding validation error message is supported here.
-      if ($element['#machine_name']['replace'] == '-') {
-        form_error($element, $form_state, t('The machine-readable name must contain only lowercase letters, numbers, and hyphens.'));
-      }
-      // Otherwise, we assume the default (underscore).
-      else {
-        form_error($element, $form_state, t('The machine-readable name must contain only lowercase letters, numbers, and underscores.'));
-      }
-    }
-    else {
-      form_error($element, $form_state, $element['#machine_name']['error']);
-    }
-  }
-
-  // Verify that the machine name is unique.
-  if ($element['#default_value'] !== $element['#value']) {
-    $function = $element['#machine_name']['exists'];
-    if (call_user_func($function, $element['#value'], $element, $form_state)) {
-      form_error($element, $form_state, t('The machine-readable name is already in use. It must be unique.'));
-    }
-  }
+function form_validate_machine_name(&$element, FormStateInterface $form_state, &$complete_form) {
+  Element\MachineName::validateMachineName($element, $form_state, $complete_form);
 }
 
 /**
  * Arranges elements into groups.
  *
- * @param $element
- *   An associative array containing the properties and children of the
- *   element. Note that $element must be taken by reference here, so processed
- *   child elements are taken over into $form_state.
- * @param $form_state
- *   The current state of the form for the form this element belongs to.
- *
- * @return
- *   The processed element.
+ * @deprecated Use \Drupal\Core\Render\Element\FormElement::processGroup().
  */
-function form_process_group(&$element, FormStateInterface $form_state) {
-  $parents = implode('][', $element['#parents']);
-
-  // Each details element forms a new group. The #type 'vertical_tabs' basically
-  // only injects a new details element.
-  $form_state['groups'][$parents]['#group_exists'] = TRUE;
-  $element['#groups'] = &$form_state['groups'];
-
-  // Process vertical tabs group member details elements.
-  if (isset($element['#group'])) {
-    // Add this details element to the defined group (by reference).
-    $group = $element['#group'];
-    $form_state['groups'][$group][] = &$element;
-  }
-
-  return $element;
+function form_process_group(&$element, FormStateInterface $form_state, &$complete_form) {
+  return Element\FormElement::processGroup($element, $form_state, $complete_form);
 }
 
 /**
@@ -1989,7 +1793,7 @@ function template_preprocess_vertical_tabs(&$variables) {
  *
  * @deprecated Use \Drupal\Core\Render\Element\FormElement::processAutocomplete().
  */
-function form_process_autocomplete($element, FormStateInterface $form_state, &$complete_form) {
+function form_process_autocomplete(&$element, FormStateInterface $form_state, &$complete_form) {
   return Element\FormElement::processAutocomplete($element, $form_state, $complete_form);
 }
 
@@ -2011,69 +1815,19 @@ function template_preprocess_input(&$variables) {
 /**
  * Prepares a #type 'button' render element for theme_input().
  *
- * @param array $element
- *   An associative array containing the properties of the element.
- *   Properties used: #attributes, #button_type, #name, #value.
- *
- * The #button_type property accepts any value, though core themes have CSS that
- * styles the following button_types appropriately: 'primary', 'danger'.
- *
- * @return array
- *   The $element with prepared variables ready for theme_input().
+ * @deprecated Use \Drupal\Core\Render\Element\Button::preRenderButton().
  */
 function form_pre_render_button($element) {
-  $element['#attributes']['type'] = 'submit';
-  Element::setAttributes($element, array('id', 'name', 'value'));
-
-  $element['#attributes']['class'][] = 'button';
-  if (!empty($element['#button_type'])) {
-    $element['#attributes']['class'][] = 'button--' . $element['#button_type'];
-  }
-  // @todo Various JavaScript depends on this button class.
-  $element['#attributes']['class'][] = 'form-submit';
-
-  if (!empty($element['#attributes']['disabled'])) {
-    $element['#attributes']['class'][] = 'is-disabled';
-  }
-
-  return $element;
+  return Element\Button::preRenderButton($element);
 }
 
 /**
  * Prepares a #type 'image_button' render element for theme_input().
  *
- * @param array $element
- *   An associative array containing the properties of the element.
- *   Properties used: #attributes, #button_type, #name, #value, #title, #src.
- *
- * The #button_type property accepts any value, though core themes have css that
- * styles the following button_types appropriately: 'primary', 'danger'.
- *
- * @return array
- *   The $element with prepared variables ready for theme_input().
+ * @deprecated Use \Drupal\Core\Render\Element\ImageButton::preRenderButton().
  */
 function form_pre_render_image_button($element) {
-  $element['#attributes']['type'] = 'image';
-  Element::setAttributes($element, array('id', 'name', 'value'));
-
-  $element['#attributes']['src'] = file_create_url($element['#src']);
-  if (!empty($element['#title'])) {
-    $element['#attributes']['alt'] = $element['#title'];
-    $element['#attributes']['title'] = $element['#title'];
-  }
-
-  $element['#attributes']['class'][] = 'image-button';
-  if (!empty($element['#button_type'])) {
-    $element['#attributes']['class'][] = 'image-button--' . $element['#button_type'];
-  }
-  // @todo Various JavaScript depends on this button class.
-  $element['#attributes']['class'][] = 'form-submit';
-
-  if (!empty($element['#attributes']['disabled'])) {
-    $element['#attributes']['class'][] = 'is-disabled';
-  }
-
-  return $element;
+  return Element\ImageButton::preRenderButton($element);
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index f7acb39..88b6fe6 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -10,7 +10,6 @@
 
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\UrlHelper;
 use Drupal\Component\Utility\Xss;
 use Drupal\Core\Config\Config;
 use Drupal\Core\Config\StorageException;
@@ -1961,24 +1960,10 @@ function _template_preprocess_default_variables() {
 /**
  * #pre_render callback for the html element type.
  *
- * @param array $element
- *   A structured array containing the html element type build properties.
- *
- * @see system_element_info()
+ * @deprecated Use \Drupal\Core\Render\Element\Html::preRenderHtml().
  */
 function drupal_pre_render_html(array $element) {
-  // Add favicon.
-  if (theme_get_setting('features.favicon')) {
-    $favicon = theme_get_setting('favicon.url');
-    $type = theme_get_setting('favicon.mimetype');
-    $element['#attached']['drupal_add_html_head_link'][][] = array(
-      'rel' => 'shortcut icon',
-      'href' => UrlHelper::stripDangerousProtocols($favicon),
-      'type' => $type,
-    );
-  }
-
-  return $element;
+  return Element\Html::preRenderHtml($element);
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Render/Element/Button.php b/core/lib/Drupal/Core/Render/Element/Button.php
new file mode 100644
index 0000000..3db1fef
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/Button.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Element\Button.
+ */
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+
+/**
+ * @todo.
+ * Provides an action button.
+ *
+ * When the button is pressed, the form will be submitted to Drupal, where it is
+ * validated and rebuilt. The submit handler is not invoked.
+ *
+ * @FormElement("submit")
+ */
+class Button extends FormElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+    return array(
+      '#input' => TRUE,
+      '#name' => 'op',
+      '#is_button' => TRUE,
+      '#executes_submit_callback' => FALSE,
+      '#limit_validation_errors' => FALSE,
+      '#process' => array(
+        array($class, 'processButton'),
+        array($class, 'processAjaxForm'),
+      ),
+      '#pre_render' => array(
+        array($class, 'preRenderButton'),
+      ),
+      '#theme_wrappers' => array('input__submit'),
+    );
+  }
+
+  /**
+   * Processes a form button element.
+   */
+  public static function processButton(&$element, FormStateInterface $form_state, &$complete_form) {
+    // If this is a button intentionally allowing incomplete form submission
+    // (e.g., a "Previous" or "Add another item" button), then also skip
+    // client-side validation.
+    if (isset($element['#limit_validation_errors']) && $element['#limit_validation_errors'] !== FALSE) {
+      $element['#attributes']['formnovalidate'] = 'formnovalidate';
+    }
+    return $element;
+  }
+
+  /**
+   * Prepares a #type 'button' render element for theme_input().
+   *
+   * @param array $element
+   *   An associative array containing the properties of the element.
+   *   Properties used: #attributes, #button_type, #name, #value.
+   *
+   * The #button_type property accepts any value, though core themes have CSS that
+   * styles the following button_types appropriately: 'primary', 'danger'.
+   *
+   * @return array
+   *   The $element with prepared variables ready for theme_input().
+   */
+  public static function preRenderButton($element) {
+    $element['#attributes']['type'] = 'submit';
+    Element::setAttributes($element, array('id', 'name', 'value'));
+
+    $element['#attributes']['class'][] = 'button';
+    if (!empty($element['#button_type'])) {
+      $element['#attributes']['class'][] = 'button--' . $element['#button_type'];
+    }
+    // @todo Various JavaScript depends on this button class.
+    $element['#attributes']['class'][] = 'form-submit';
+
+    if (!empty($element['#attributes']['disabled'])) {
+      $element['#attributes']['class'][] = 'is-disabled';
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/Html.php b/core/lib/Drupal/Core/Render/Element/Html.php
new file mode 100644
index 0000000..4de3a9d
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/Html.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Element\Html.
+ */
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Component\Utility\UrlHelper;
+
+/**
+ * @todo.
+ *
+ * @RenderElement("html")
+ */
+class Html extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+    return array(
+      '#theme' => 'html',
+      '#pre_render' => array(
+        array($class, 'preRenderHtml'),
+      ),
+      // HTML5 Shiv
+      '#attached' => array(
+        'library' => array('core/html5shiv'),
+      ),
+    );
+  }
+
+  /**
+   * #pre_render callback for the html element type.
+   *
+   * @param array $element
+   *   A structured array containing the html element type build properties.
+   *
+   * @return array
+   *   The processed element.
+   */
+  public static function preRenderHtml($element) {
+    // Add favicon.
+    if (static::themeGetSetting('features.favicon')) {
+      $favicon = static::themeGetSetting('favicon.url');
+      $type = static::themeGetSetting('favicon.mimetype');
+      $element['#attached']['drupal_add_html_head_link'][][] = array(
+        'rel' => 'shortcut icon',
+        'href' => UrlHelper::stripDangerousProtocols($favicon),
+        'type' => $type,
+      );
+    }
+
+    return $element;
+  }
+
+  /**
+   * Wraps theme_get_setting().
+   */
+  protected static function themeGetSetting($setting_name) {
+    return theme_get_setting($setting_name);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/ImageButton.php b/core/lib/Drupal/Core/Render/Element/ImageButton.php
new file mode 100644
index 0000000..744ea4c
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/ImageButton.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Element\ImageButton.
+ */
+
+namespace Drupal\Core\Render\Element;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
+
+/**
+ * Provides a form submit button with an image.
+ *
+ * @FormElement("image_button")
+ */
+class ImageButton extends Submit {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $info = parent::getInfo();
+    unset($info['name']);
+
+    return array(
+      '#return_value' => TRUE,
+      '#has_garbage_value' => TRUE,
+      '#src' => NULL,
+      '#theme_wrappers' => array('input__image_button'),
+    ) + $info;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
+    if ($input !== FALSE) {
+      if (!empty($input)) {
+        // If we're dealing with Mozilla or Opera, we're lucky. It will
+        // return a proper value, and we can get on with things.
+        return $element['#return_value'];
+      }
+      else {
+        // Unfortunately, in IE we never get back a proper value for THIS
+        // form element. Instead, we get back two split values: one for the
+        // X and one for the Y coordinates on which the user clicked the
+        // button. We'll find this element in the #post data, and search
+        // in the same spot for its name, with '_x'.
+        $input = $form_state['input'];
+        foreach (explode('[', $element['#name']) as $element_name) {
+          // chop off the ] that may exist.
+          if (substr($element_name, -1) == ']') {
+            $element_name = substr($element_name, 0, -1);
+          }
+
+          if (!isset($input[$element_name])) {
+            if (isset($input[$element_name . '_x'])) {
+              return $element['#return_value'];
+            }
+            return NULL;
+          }
+          $input = $input[$element_name];
+        }
+        return $element['#return_value'];
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function preRenderButton($element) {
+    $element['#attributes']['type'] = 'image';
+    Element::setAttributes($element, array('id', 'name', 'value'));
+
+    $element['#attributes']['src'] = file_create_url($element['#src']);
+    if (!empty($element['#title'])) {
+      $element['#attributes']['alt'] = $element['#title'];
+      $element['#attributes']['title'] = $element['#title'];
+    }
+
+    $element['#attributes']['class'][] = 'image-button';
+    if (!empty($element['#button_type'])) {
+      $element['#attributes']['class'][] = 'image-button--' . $element['#button_type'];
+    }
+    // @todo Various JavaScript depends on this button class.
+    $element['#attributes']['class'][] = 'form-submit';
+
+    if (!empty($element['#attributes']['disabled'])) {
+      $element['#attributes']['class'][] = 'is-disabled';
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Render/Element/Submit.php b/core/lib/Drupal/Core/Render/Element/Submit.php
new file mode 100644
index 0000000..41e592d
--- /dev/null
+++ b/core/lib/Drupal/Core/Render/Element/Submit.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Render\Element\Submit.
+ */
+
+namespace Drupal\Core\Render\Element;
+
+/**
+ * Provides a form submit button.
+ *
+ * Submit buttons are processed the same as regular buttons, except they trigger
+ * the form's submit handler.
+ *
+ * @FormElement("submit")
+ */
+class Submit extends Button {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    return array(
+      '#executes_submit_callback' => TRUE,
+    ) + parent::getInfo();
+  }
+
+}
diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module
index d0114e2..bead3ba 100644
--- a/core/modules/datetime/datetime.module
+++ b/core/modules/datetime/datetime.module
@@ -52,34 +52,6 @@ function datetime_help($route_name, RouteMatchInterface $route_match) {
  * Implements hook_element_info().
  */
 function datetime_element_info() {
-  $date_format = '';
-  $time_format = '';
-  // Date formats cannot be loaded during install or update.
-  if (!defined('MAINTENANCE_MODE')) {
-    if ($date_format_entity = entity_load('date_format', 'html_date')) {
-      $date_format = $date_format_entity->getPattern();
-    }
-    if ($time_format_entity = entity_load('date_format', 'html_time')) {
-      $time_format = $time_format_entity->getPattern();
-    }
-  }
-  $types['datetime'] = array(
-    '#input' => TRUE,
-    '#element_validate' => array('datetime_datetime_validate'),
-    '#process' => array('datetime_datetime_form_process', 'form_process_group'),
-    '#pre_render' => array('form_pre_render_group'),
-    '#theme' => 'datetime_form',
-    '#theme_wrappers' => array('datetime_wrapper'),
-    '#date_date_format' => $date_format,
-    '#date_date_element' => 'date',
-    '#date_date_callbacks' => array(),
-    '#date_time_format' => $time_format,
-    '#date_time_element' => 'time',
-    '#date_time_callbacks' => array(),
-    '#date_year_range' => '1900:2050',
-    '#date_increment' => 1,
-    '#date_timezone' => '',
-  );
   $types['datelist'] = array(
     '#input' => TRUE,
     '#element_validate' => array('datetime_datelist_validate'),
@@ -264,201 +236,6 @@ function template_preprocess_datetime_wrapper(&$variables) {
 }
 
 /**
- * Expands a datetime element type into date and/or time elements.
- *
- * All form elements are designed to have sane defaults so any or all can be
- * omitted. Both the date and time components are configurable so they can be
- * output as HTML5 datetime elements or not, as desired.
- *
- * Examples of possible configurations include:
- *   HTML5 date and time:
- *     #date_date_element = 'date';
- *     #date_time_element = 'time';
- *   HTML5 datetime:
- *     #date_date_element = 'datetime';
- *     #date_time_element = 'none';
- *   HTML5 time only:
- *     #date_date_element = 'none';
- *     #date_time_element = 'time'
- *   Non-HTML5:
- *     #date_date_element = 'text';
- *     #date_time_element = 'text';
- *
- * Required settings:
- *   - #default_value: A DrupalDateTime object, adjusted to the proper local
- *     timezone. Converting a date stored in the database from UTC to the local
- *     zone and converting it back to UTC before storing it is not handled here.
- *     This element accepts a date as the default value, and then converts the
- *     user input strings back into a new date object on submission. No timezone
- *     adjustment is performed.
- * Optional properties include:
- *   - #date_date_format: A date format string that describes the format that
- *     should be displayed to the end user for the date. When using HTML5
- *     elements the format MUST use the appropriate HTML5 format for that
- *     element, no other format will work. See the format_date() function for a
- *     list of the possible formats and HTML5 standards for the HTML5
- *     requirements. Defaults to the right HTML5 format for the chosen element
- *     if a HTML5 element is used, otherwise defaults to
- *     entity_load('date_format', 'html_date')->getPattern().
- *   - #date_date_element: The date element. Options are:
- *     - datetime: Use the HTML5 datetime element type.
- *     - datetime-local: Use the HTML5 datetime-local element type.
- *     - date: Use the HTML5 date element type.
- *     - text: No HTML5 element, use a normal text field.
- *     - none: Do not display a date element.
- *   - #date_date_callbacks: Array of optional callbacks for the date element.
- *     Can be used to add a jQuery datepicker.
- *   - #date_time_element: The time element. Options are:
- *     - time: Use a HTML5 time element type.
- *     - text: No HTML5 element, use a normal text field.
- *     - none: Do not display a time element.
- *   - #date_time_format: A date format string that describes the format that
- *     should be displayed to the end user for the time. When using HTML5
- *     elements the format MUST use the appropriate HTML5 format for that
- *     element, no other format will work. See the format_date() function for
- *     a list of the possible formats and HTML5 standards for the HTML5
- *     requirements. Defaults to the right HTML5 format for the chosen element
- *     if a HTML5 element is used, otherwise defaults to
- *     entity_load('date_format', 'html_time')->getPattern().
- *   - #date_time_callbacks: An array of optional callbacks for the time
- *     element. Can be used to add a jQuery timepicker or an 'All day' checkbox.
- *   - #date_year_range: A description of the range of years to allow, like
- *     '1900:2050', '-3:+3' or '2000:+3', where the first value describes the
- *     earliest year and the second the latest year in the range. A year
- *     in either position means that specific year. A +/- value describes a
- *     dynamic value that is that many years earlier or later than the current
- *     year at the time the form is displayed. Used in jQueryUI datepicker year
- *     range and HTML5 min/max date settings. Defaults to '1900:2050'.
- *   - #date_increment: The increment to use for minutes and seconds, i.e.
- *    '15' would show only :00, :15, :30 and :45. Used for HTML5 step values and
- *     jQueryUI datepicker settings. Defaults to 1 to show every minute.
- *   - #date_timezone: The local timezone to use when creating dates. Generally
- *     this should be left empty and it will be set correctly for the user using
- *     the form. Useful if the default value is empty to designate a desired
- *     timezone for dates created in form processing. If a default date is
- *     provided, this value will be ignored, the timezone in the default date
- *     takes precedence. Defaults to the value returned by
- *     drupal_get_user_timezone().
- *
- * Example usage:
- * @code
- *   $form = array(
- *     '#type' => 'datetime',
- *     '#default_value' => new DrupalDateTime('2000-01-01 00:00:00'),
- *     '#date_date_element' => 'date',
- *     '#date_time_element' => 'none',
- *     '#date_year_range' => '2010:+3',
- *   );
- * @endcode
- *
- * @param array $element
- *   The form element whose value is being processed.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- *   The current state of the form.
- *
- * @return array
- *   The form element whose value has been processed.
- */
-function datetime_datetime_form_process($element, FormStateInterface $form_state) {
-  $format_settings = array();
-  // The value callback has populated the #value array.
-  $date = !empty($element['#value']['object']) ? $element['#value']['object'] : NULL;
-
-  // Set a fallback timezone.
-  if ($date instanceOf DrupalDateTime) {
-    $element['#date_timezone'] = $date->getTimezone()->getName();
-  }
-  elseif (!empty($element['#timezone'])) {
-    $element['#date_timezone'] = $element['#date_timezone'];
-  }
-  else {
-    $element['#date_timezone'] = drupal_get_user_timezone();
-  }
-
-  $element['#tree'] = TRUE;
-
-  if ($element['#date_date_element'] != 'none') {
-
-    $date_format = $element['#date_date_element'] != 'none' ? datetime_html5_format('date', $element) : '';
-    $date_value = !empty($date) ? $date->format($date_format, $format_settings) : $element['#value']['date'];
-
-    // Creating format examples on every individual date item is messy, and
-    // placeholders are invalid for HTML5 date and datetime, so an example
-    // format is appended to the title to appear in tooltips.
-    $extra_attributes = array(
-      'title' => t('Date (i.e. !format)', array('!format' => datetime_format_example($date_format))),
-      'type' => $element['#date_date_element'],
-    );
-
-    // Adds the HTML5 date attributes.
-    if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
-      $html5_min = clone($date);
-      $range = datetime_range_years($element['#date_year_range'], $date);
-      $html5_min->setDate($range[0], 1, 1)->setTime(0, 0, 0);
-      $html5_max = clone($date);
-      $html5_max->setDate($range[1], 12, 31)->setTime(23, 59, 59);
-
-      $extra_attributes += array(
-        'min' => $html5_min->format($date_format, $format_settings),
-        'max' => $html5_max->format($date_format, $format_settings),
-      );
-    }
-
-    $element['date'] = array(
-      '#type' => 'date',
-      '#title' => t('Date'),
-      '#title_display' => 'invisible',
-      '#value' => $date_value,
-      '#attributes' => $element['#attributes'] + $extra_attributes,
-      '#required' => $element['#required'],
-      '#size' => max(12, strlen($element['#value']['date'])),
-    );
-
-    // Allows custom callbacks to alter the element.
-    if (!empty($element['#date_date_callbacks'])) {
-      foreach ($element['#date_date_callbacks'] as $callback) {
-        if (function_exists($callback)) {
-          $callback($element, $form_state, $date);
-        }
-      }
-    }
-  }
-
-  if ($element['#date_time_element'] != 'none') {
-
-    $time_format = $element['#date_time_element'] != 'none' ? datetime_html5_format('time', $element) : '';
-    $time_value = !empty($date) ? $date->format($time_format, $format_settings) : $element['#value']['time'];
-
-    // Adds the HTML5 attributes.
-    $extra_attributes = array(
-      'title' =>t('Time (i.e. !format)', array('!format' => datetime_format_example($time_format))),
-      'type' => $element['#date_time_element'],
-      'step' => $element['#date_increment'],
-    );
-    $element['time'] = array(
-      '#type' => 'date',
-      '#title' => t('Time'),
-      '#title_display' => 'invisible',
-      '#value' => $time_value,
-      '#attributes' => $element['#attributes'] + $extra_attributes,
-      '#required' => $element['#required'],
-      '#size' => 12,
-    );
-
-    // Allows custom callbacks to alter the element.
-    if (!empty($element['#date_time_callbacks'])) {
-      foreach ($element['#date_time_callbacks'] as $callback) {
-        if (function_exists($callback)) {
-          $callback($element, $form_state, $date);
-        }
-      }
-    }
-  }
-
-  return $element;
-}
-
-/**
  * Value callback for a datetime element.
  *
  * @param array $element
@@ -519,54 +296,6 @@ function form_type_datetime_value($element, $input = FALSE) {
 }
 
 /**
- * Validation callback for a datetime element.
- *
- * If the date is valid, the date object created from the user input is set in
- * the form for use by the caller. The work of compiling the user input back
- * into a date object is handled by the value callback, so we can use it here.
- * We also have the raw input available for validation testing.
- *
- * @param array $element
- *   The form element whose value is being validated.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- *   The current state of the form.
- */
-function datetime_datetime_validate($element, FormStateInterface $form_state) {
-
-  $input_exists = FALSE;
-  $input = NestedArray::getValue($form_state->getValues(), $element['#parents'], $input_exists);
-  if ($input_exists) {
-
-    $title = !empty($element['#title']) ? $element['#title'] : '';
-    $date_format = $element['#date_date_element'] != 'none' ? datetime_html5_format('date', $element) : '';
-    $time_format = $element['#date_time_element'] != 'none' ? datetime_html5_format('time', $element) : '';
-    $format = trim($date_format . ' ' . $time_format);
-
-    // If there's empty input and the field is not required, set it to empty.
-    if (empty($input['date']) && empty($input['time']) && !$element['#required']) {
-      form_set_value($element, NULL, $form_state);
-    }
-    // If there's empty input and the field is required, set an error. A
-    // reminder of the required format in the message provides a good UX.
-    elseif (empty($input['date']) && empty($input['time']) && $element['#required']) {
-      form_error($element, $form_state, t('The %field date is required. Please enter a date in the format %format.', array('%field' => $title, '%format' => datetime_format_example($format))));
-    }
-    else {
-      // If the date is valid, set it.
-      $date = $input['object'];
-      if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
-        form_set_value($element, $date, $form_state);
-      }
-      // If the date is invalid, set an error. A reminder of the required
-      // format in the message provides a good UX.
-      else {
-        form_error($element, $form_state, t('The %field date is invalid. Please enter a date in the format %format.', array('%field' => $title, '%format' => datetime_format_example($format))));
-      }
-    }
-  }
-}
-
-/**
  * Retrieves the right format for a HTML5 date element.
  *
  * The format is important because these elements will not work with any other
diff --git a/core/modules/datetime/src/Element/Datetime.php b/core/modules/datetime/src/Element/Datetime.php
new file mode 100644
index 0000000..edd7d1c
--- /dev/null
+++ b/core/modules/datetime/src/Element/Datetime.php
@@ -0,0 +1,310 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\datetime\Element\Datetime.
+ */
+
+namespace Drupal\datetime\Element;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element\FormElement;
+use Drupal\system\Entity\DateFormat;
+
+/**
+ * @todo.
+ *
+ * @FormElement("datetime")
+ */
+class Datetime extends FormElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $date_format = '';
+    $time_format = '';
+    // Date formats cannot be loaded during install or update.
+    if (!defined('MAINTENANCE_MODE')) {
+      if ($date_format_entity = DateFormat::load('html_date')) {
+        $date_format = $date_format_entity->getPattern();
+      }
+      if ($time_format_entity = DateFormat::load('html_time')) {
+        $time_format = $time_format_entity->getPattern();
+      }
+    }
+
+    $class = get_class($this);
+    return array(
+      '#input' => TRUE,
+      '#element_validate' => array('datetime_datetime_validate'),
+      '#process' => array(
+        array($class, 'processDatetime'),
+        array($class, 'processGroup'),
+      ),
+      '#pre_render' => array(
+        array($class, 'preRenderGroup'),
+      ),
+      '#theme' => 'datetime_form',
+      '#theme_wrappers' => array('datetime_wrapper'),
+      '#date_date_format' => $date_format,
+      '#date_date_element' => 'date',
+      '#date_date_callbacks' => array(),
+      '#date_time_format' => $time_format,
+      '#date_time_element' => 'time',
+      '#date_time_callbacks' => array(),
+      '#date_year_range' => '1900:2050',
+      '#date_increment' => 1,
+      '#date_timezone' => '',
+      '#size' => 60,
+      '#maxlength' => 128,
+      '#autocomplete_route_name' => FALSE,
+    );
+  }
+
+  /**
+   * Validation callback for a datetime element.
+   *
+   * If the date is valid, the date object created from the user input is set in
+   * the form for use by the caller. The work of compiling the user input back
+   * into a date object is handled by the value callback, so we can use it here.
+   * We also have the raw input available for validation testing.
+   *
+   * @param array $element
+   *   The form element whose value is being validated.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   */
+  public static function validateDatetime(&$element, FormStateInterface $form_state, &$complete_form) {
+    $input_exists = FALSE;
+    $input = NestedArray::getValue($form_state['values'], $element['#parents'], $input_exists);
+    if ($input_exists) {
+
+      $title = !empty($element['#title']) ? $element['#title'] : '';
+      $date_format = $element['#date_date_element'] != 'none' ? datetime_html5_format('date', $element) : '';
+      $time_format = $element['#date_time_element'] != 'none' ? datetime_html5_format('time', $element) : '';
+      $format = trim($date_format . ' ' . $time_format);
+
+      // If there's empty input and the field is not required, set it to empty.
+      if (empty($input['date']) && empty($input['time']) && !$element['#required']) {
+        form_set_value($element, NULL, $form_state);
+      }
+      // If there's empty input and the field is required, set an error. A
+      // reminder of the required format in the message provides a good UX.
+      elseif (empty($input['date']) && empty($input['time']) && $element['#required']) {
+        form_error($element, $form_state, t('The %field date is required. Please enter a date in the format %format.', array('%field' => $title, '%format' => datetime_format_example($format))));
+      }
+      else {
+        // If the date is valid, set it.
+        $date = $input['object'];
+        if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
+          form_set_value($element, $date, $form_state);
+        }
+        // If the date is invalid, set an error. A reminder of the required
+        // format in the message provides a good UX.
+        else {
+          form_error($element, $form_state, t('The %field date is invalid. Please enter a date in the format %format.', array('%field' => $title, '%format' => datetime_format_example($format))));
+        }
+      }
+    }
+  }
+
+  /**
+   * Expands a datetime element type into date and/or time elements.
+   *
+   * All form elements are designed to have sane defaults so any or all can be
+   * omitted. Both the date and time components are configurable so they can be
+   * output as HTML5 datetime elements or not, as desired.
+   *
+   * Examples of possible configurations include:
+   *   HTML5 date and time:
+   *     #date_date_element = 'date';
+   *     #date_time_element = 'time';
+   *   HTML5 datetime:
+   *     #date_date_element = 'datetime';
+   *     #date_time_element = 'none';
+   *   HTML5 time only:
+   *     #date_date_element = 'none';
+   *     #date_time_element = 'time'
+   *   Non-HTML5:
+   *     #date_date_element = 'text';
+   *     #date_time_element = 'text';
+   *
+   * Required settings:
+   *   - #default_value: A DrupalDateTime object, adjusted to the proper local
+   *     timezone. Converting a date stored in the database from UTC to the local
+   *     zone and converting it back to UTC before storing it is not handled here.
+   *     This element accepts a date as the default value, and then converts the
+   *     user input strings back into a new date object on submission. No timezone
+   *     adjustment is performed.
+   * Optional properties include:
+   *   - #date_date_format: A date format string that describes the format that
+   *     should be displayed to the end user for the date. When using HTML5
+   *     elements the format MUST use the appropriate HTML5 format for that
+   *     element, no other format will work. See the format_date() function for a
+   *     list of the possible formats and HTML5 standards for the HTML5
+   *     requirements. Defaults to the right HTML5 format for the chosen element
+   *     if a HTML5 element is used, otherwise defaults to
+   *     entity_load('date_format', 'html_date')->getPattern().
+   *   - #date_date_element: The date element. Options are:
+   *     - datetime: Use the HTML5 datetime element type.
+   *     - datetime-local: Use the HTML5 datetime-local element type.
+   *     - date: Use the HTML5 date element type.
+   *     - text: No HTML5 element, use a normal text field.
+   *     - none: Do not display a date element.
+   *   - #date_date_callbacks: Array of optional callbacks for the date element.
+   *     Can be used to add a jQuery datepicker.
+   *   - #date_time_element: The time element. Options are:
+   *     - time: Use a HTML5 time element type.
+   *     - text: No HTML5 element, use a normal text field.
+   *     - none: Do not display a time element.
+   *   - #date_time_format: A date format string that describes the format that
+   *     should be displayed to the end user for the time. When using HTML5
+   *     elements the format MUST use the appropriate HTML5 format for that
+   *     element, no other format will work. See the format_date() function for
+   *     a list of the possible formats and HTML5 standards for the HTML5
+   *     requirements. Defaults to the right HTML5 format for the chosen element
+   *     if a HTML5 element is used, otherwise defaults to
+   *     entity_load('date_format', 'html_time')->getPattern().
+   *   - #date_time_callbacks: An array of optional callbacks for the time
+   *     element. Can be used to add a jQuery timepicker or an 'All day' checkbox.
+   *   - #date_year_range: A description of the range of years to allow, like
+   *     '1900:2050', '-3:+3' or '2000:+3', where the first value describes the
+   *     earliest year and the second the latest year in the range. A year
+   *     in either position means that specific year. A +/- value describes a
+   *     dynamic value that is that many years earlier or later than the current
+   *     year at the time the form is displayed. Used in jQueryUI datepicker year
+   *     range and HTML5 min/max date settings. Defaults to '1900:2050'.
+   *   - #date_increment: The increment to use for minutes and seconds, i.e.
+   *    '15' would show only :00, :15, :30 and :45. Used for HTML5 step values and
+   *     jQueryUI datepicker settings. Defaults to 1 to show every minute.
+   *   - #date_timezone: The local timezone to use when creating dates. Generally
+   *     this should be left empty and it will be set correctly for the user using
+   *     the form. Useful if the default value is empty to designate a desired
+   *     timezone for dates created in form processing. If a default date is
+   *     provided, this value will be ignored, the timezone in the default date
+   *     takes precedence. Defaults to the value returned by
+   *     drupal_get_user_timezone().
+   *
+   * Example usage:
+   * @code
+   *   $form = array(
+   *     '#type' => 'datetime',
+   *     '#default_value' => new DrupalDateTime('2000-01-01 00:00:00'),
+   *     '#date_date_element' => 'date',
+   *     '#date_time_element' => 'none',
+   *     '#date_year_range' => '2010:+3',
+   *   );
+   * @endcode
+   *
+   * @param array $element
+   *   The form element whose value is being processed.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param array $complete_form
+   *   The complete form structure.
+   *
+   * @return array
+   *   The form element whose value has been processed.
+   */
+  public static function processDatetime(&$element, FormStateInterface $form_state, &$complete_form) {
+    $format_settings = array();
+    // The value callback has populated the #value array.
+    $date = !empty($element['#value']['object']) ? $element['#value']['object'] : NULL;
+
+    // Set a fallback timezone.
+    if ($date instanceOf DrupalDateTime) {
+      $element['#date_timezone'] = $date->getTimezone()->getName();
+    }
+    elseif (empty($element['#timezone'])) {
+      $element['#date_timezone'] = drupal_get_user_timezone();
+    }
+
+    $element['#tree'] = TRUE;
+
+    if ($element['#date_date_element'] != 'none') {
+
+      $date_format = $element['#date_date_element'] != 'none' ? datetime_html5_format('date', $element) : '';
+      $date_value = !empty($date) ? $date->format($date_format, $format_settings) : $element['#value']['date'];
+
+      // Creating format examples on every individual date item is messy, and
+      // placeholders are invalid for HTML5 date and datetime, so an example
+      // format is appended to the title to appear in tooltips.
+      $extra_attributes = array(
+        'title' => t('Date (i.e. !format)', array('!format' => datetime_format_example($date_format))),
+        'type' => $element['#date_date_element'],
+      );
+
+      // Adds the HTML5 date attributes.
+      if ($date instanceOf DrupalDateTime && !$date->hasErrors()) {
+        $html5_min = clone($date);
+        $range = datetime_range_years($element['#date_year_range'], $date);
+        $html5_min->setDate($range[0], 1, 1)->setTime(0, 0, 0);
+        $html5_max = clone($date);
+        $html5_max->setDate($range[1], 12, 31)->setTime(23, 59, 59);
+
+        $extra_attributes += array(
+          'min' => $html5_min->format($date_format, $format_settings),
+          'max' => $html5_max->format($date_format, $format_settings),
+        );
+      }
+
+      $element['date'] = array(
+        '#type' => 'date',
+        '#title' => t('Date'),
+        '#title_display' => 'invisible',
+        '#value' => $date_value,
+        '#attributes' => $element['#attributes'] + $extra_attributes,
+        '#required' => $element['#required'],
+        '#size' => max(12, strlen($element['#value']['date'])),
+      );
+
+      // Allows custom callbacks to alter the element.
+      if (!empty($element['#date_date_callbacks'])) {
+        foreach ($element['#date_date_callbacks'] as $callback) {
+          if (function_exists($callback)) {
+            $callback($element, $form_state, $date);
+          }
+        }
+      }
+    }
+
+    if ($element['#date_time_element'] != 'none') {
+
+      $time_format = $element['#date_time_element'] != 'none' ? datetime_html5_format('time', $element) : '';
+      $time_value = !empty($date) ? $date->format($time_format, $format_settings) : $element['#value']['time'];
+
+      // Adds the HTML5 attributes.
+      $extra_attributes = array(
+        'title' =>t('Time (i.e. !format)', array('!format' => datetime_format_example($time_format))),
+        'type' => $element['#date_time_element'],
+        'step' => $element['#date_increment'],
+      );
+      $element['time'] = array(
+        '#type' => 'date',
+        '#title' => t('Time'),
+        '#title_display' => 'invisible',
+        '#value' => $time_value,
+        '#attributes' => $element['#attributes'] + $extra_attributes,
+        '#required' => $element['#required'],
+        '#size' => 12,
+      );
+
+      // Allows custom callbacks to alter the element.
+      if (!empty($element['#date_time_callbacks'])) {
+        foreach ($element['#date_time_callbacks'] as $callback) {
+          if (function_exists($callback)) {
+            $callback($element, $form_state, $date);
+          }
+        }
+      }
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 6875449..b6a604a 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -288,14 +288,6 @@ function system_hook_info() {
  */
 function system_element_info() {
   // Top level elements.
-  $types['html'] = array(
-    '#theme' => 'html',
-    '#pre_render' => array('drupal_pre_render_html'),
-    // HTML5 Shiv
-    '#attached' => array(
-      'library' => array('core/html5shiv'),
-    ),
-  );
   $types['form'] = array(
     '#method' => 'post',
     '#action' => request_uri(),
@@ -336,38 +328,6 @@ function system_element_info() {
   );
 
   // Input elements.
-  $types['submit'] = array(
-    '#input' => TRUE,
-    '#name' => 'op',
-    '#is_button' => TRUE,
-    '#executes_submit_callback' => TRUE,
-    '#limit_validation_errors' => FALSE,
-    '#process' => array('form_process_button', 'ajax_process_form'),
-    '#pre_render' => array('form_pre_render_button'),
-    '#theme_wrappers' => array('input__submit'),
-  );
-  $types['button'] = array(
-    '#input' => TRUE,
-    '#name' => 'op',
-    '#is_button' => TRUE,
-    '#executes_submit_callback' => FALSE,
-    '#limit_validation_errors' => FALSE,
-    '#process' => array('form_process_button', 'ajax_process_form'),
-    '#pre_render' => array('form_pre_render_button'),
-    '#theme_wrappers' => array('input__button'),
-  );
-  $types['image_button'] = array(
-    '#input' => TRUE,
-    '#is_button' => TRUE,
-    '#executes_submit_callback' => TRUE,
-    '#limit_validation_errors' => FALSE,
-    '#process' => array('form_process_button', 'ajax_process_form'),
-    '#return_value' => TRUE,
-    '#has_garbage_value' => TRUE,
-    '#src' => NULL,
-    '#pre_render' => array('form_pre_render_image_button'),
-    '#theme_wrappers' => array('input__image_button'),
-  );
   $types['tel'] = array(
     '#input' => TRUE,
     '#size' => 30,
diff --git a/core/modules/views/src/Element/View.php b/core/modules/views/src/Element/View.php
new file mode 100644
index 0000000..c9e084b
--- /dev/null
+++ b/core/modules/views/src/Element/View.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Element\View.
+ */
+
+namespace Drupal\views\Element;
+
+use Drupal\Core\Render\Element\RenderElement;
+use Drupal\views\Views;
+
+/**
+ * Provides a render element to display a view.
+ *
+ * @RenderElement("view")
+ */
+class View extends RenderElement {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getInfo() {
+    $class = get_class($this);
+    return array(
+      '#theme_wrappers' => array('container'),
+      '#pre_render' => array(
+        array($class, 'preRenderViewElement'),
+      ),
+      '#name' => NULL,
+      '#display_id' => 'default',
+      '#arguments' => array(),
+    );
+  }
+
+  /**
+   * View element pre render callback.
+   */
+  public static function preRenderViewElement($element) {
+    $element['#attributes']['class'][] = 'views-element-container';
+
+    $view = Views::getView($element['#name']);
+    if ($view && $view->access($element['#display_id'])) {
+      $element['view'] = $view->preview($element['#display_id'], $element['#arguments']);
+    }
+
+    return $element;
+  }
+
+}
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 3294836..3feea55 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -47,20 +47,6 @@ function views_help($route_name, RouteMatchInterface $route_match) {
 }
 
 /**
- * Implements hook_element_info().
- */
-function views_element_info() {
-  $types['view'] = array(
-    '#theme_wrappers' => array('container'),
-    '#pre_render' => array('views_pre_render_view_element'),
-    '#name' => NULL,
-    '#display_id' => 'default',
-    '#arguments' => array(),
-  );
-  return $types;
-}
-
-/**
  * Implements hook_views_pre_render().
  */
 function views_views_pre_render($view) {
@@ -92,20 +78,6 @@ function views_views_pre_render($view) {
 }
 
 /**
- * View element pre render callback.
- */
-function views_pre_render_view_element($element) {
-  $element['#attributes']['class'][] = 'views-element-container';
-
-  $view = Views::getView($element['#name']);
-  if ($view && $view->access($element['#display_id'])) {
-    $element['view'] = $view->preview($element['#display_id'], $element['#arguments']);
-  }
-
-  return $element;
-}
-
-/**
  * Implements hook_theme().
  *
  * Register views theming functions and those that are defined via views plugin
