diff --git a/core/lib/Drupal/Core/Entity/ContentEntityConfirmFormBase.php b/core/lib/Drupal/Core/Entity/ContentEntityConfirmFormBase.php
index 887fcae..f3a23a7 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityConfirmFormBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityConfirmFormBase.php
@@ -86,9 +86,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
       'submit' => array(
         '#type' => 'submit',
         '#value' => $this->getConfirmText(),
-        '#validate' => array(
-          array($this, 'validate'),
-        ),
         '#submit' => array(
           array($this, 'submitForm'),
         ),
@@ -121,9 +118,10 @@ public function delete(array $form, FormStateInterface $form_state) {}
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
+  public function validateForm(array &$form, FormStateInterface $form_state) {
     // Override the default validation implementation as it is not necessary
     // nor possible to validate an entity in a confirmation form.
+    return $this->buildEntity($form, $form_state);
   }
 
 }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityForm.php b/core/lib/Drupal/Core/Entity/ContentEntityForm.php
index 2c1604b..4903130 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityForm.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityForm.php
@@ -64,17 +64,9 @@ public function form(array $form, FormStateInterface $form_state) {
 
   /**
    * {@inheritdoc}
-   *
-   * Note that extending classes should not override this method to add entity
-   * validation logic, but define further validation constraints using the
-   * entity validation API and/or provide a new validation constraint if
-   * necessary. This is the only way to ensure that the validation logic
-   * is correctly applied independently of form submissions; e.g., for REST
-   * requests.
-   * For more information about entity validation, see
-   * https://www.drupal.org/node/2015613.
    */
-  public function validate(array $form, FormStateInterface $form_state) {
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     $entity = $this->buildEntity($form, $form_state);
 
@@ -87,14 +79,25 @@ public function validate(array $form, FormStateInterface $form_state) {
 
     $this->flagViolations($violations, $form, $form_state);
 
-    // @todo Remove this.
-    // Execute legacy global validation handlers.
-    $form_state->setValidateHandlers([]);
-    \Drupal::service('form_validator')->executeValidateHandlers($form, $form_state);
+    // Mark the entity as validated. See ::save().
+    $form_state->setTemporaryValue('entity_validated', TRUE);
+
     return $entity;
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    // Make sure button-level validation handlers do not prevent entity
+    // validation from being executed.
+    if (!$form_state->getTemporaryValue('entity_validated')) {
+      throw new \LogicException('Entity validation was skipped.');
+    }
+    return parent::save($form, $form_state);
+  }
+
+  /**
    * Gets the names of all fields edited in the form.
    *
    * If the entity form customly adds some fields to the form (i.e. without
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityFormInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityFormInterface.php
index 8fae58e..f210611 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityFormInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityFormInterface.php
@@ -36,6 +36,8 @@ public function getFormDisplay(FormStateInterface $form_state);
    *   The form display that the current form operates with.
    * @param \Drupal\Core\Form\FormStateInterface $form_state
    *   The current state of the form.
+   *
+   * @return $this
    */
   public function setFormDisplay(EntityFormDisplayInterface $form_display, FormStateInterface $form_state);
 
@@ -61,4 +63,21 @@ public function getFormLangcode(FormStateInterface $form_state);
    */
   public function isDefaultFormLangcode(FormStateInterface $form_state);
 
+  /**
+   * {@inheritdoc}
+   *
+   * Note that extending classes should not override this method to add entity
+   * validation logic, but define further validation constraints using the
+   * entity validation API and/or provide a new validation constraint if
+   * necessary. This is the only way to ensure that the validation logic
+   * is correctly applied independently of form submissions; e.g., for REST
+   * requests.
+   * For more information about entity validation, see
+   * https://www.drupal.org/node/2015613.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityTypeInterface
+   *   The built entity.
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state);
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityConfirmFormBase.php b/core/lib/Drupal/Core/Entity/EntityConfirmFormBase.php
index a11e27f..7b320e1 100644
--- a/core/lib/Drupal/Core/Entity/EntityConfirmFormBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityConfirmFormBase.php
@@ -81,9 +81,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
       'submit' => array(
         '#type' => 'submit',
         '#value' => $this->getConfirmText(),
-        '#validate' => array(
-          array($this, 'validate'),
-        ),
         '#submit' => array(
           array($this, 'submitForm'),
         ),
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 6d61d0c..e900309 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -219,7 +219,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
     $actions['submit'] = array(
       '#type' => 'submit',
       '#value' => $this->t('Save'),
-      '#validate' => array('::validate'),
       '#submit' => array('::submitForm', '::save'),
     );
 
@@ -246,16 +245,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
 
   /**
    * {@inheritdoc}
-   */
-  public function validate(array $form, FormStateInterface $form_state) {
-    // @todo Remove this.
-    // Execute legacy global validation handlers.
-    $form_state->setValidateHandlers([]);
-    \Drupal::service('form_validator')->executeValidateHandlers($form, $form_state);
-  }
-
-  /**
-   * {@inheritdoc}
    *
    * This is the default entity object builder function. It is called before any
    * other submit handler to build the new entity object to be used by the
diff --git a/core/lib/Drupal/Core/Entity/EntityFormInterface.php b/core/lib/Drupal/Core/Entity/EntityFormInterface.php
index 26ce909..8ae1a91 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormInterface.php
@@ -95,19 +95,6 @@ public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entit
   public function buildEntity(array $form, FormStateInterface $form_state);
 
   /**
-   * Validates the submitted form values of the entity form.
-   *
-   * @param array $form
-   *   A nested array form elements comprising the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   *
-   * @return \Drupal\Core\Entity\ContentEntityTypeInterface
-   *   The built entity.
-   */
-  public function validate(array $form, FormStateInterface $form_state);
-
-  /**
    * Form submission handler for the 'save' action.
    *
    * Normally this method should be overridden to provide specific messages to
diff --git a/core/modules/action/src/ActionFormBase.php b/core/modules/action/src/ActionFormBase.php
index bdddc98..0b972d4 100644
--- a/core/modules/action/src/ActionFormBase.php
+++ b/core/modules/action/src/ActionFormBase.php
@@ -123,8 +123,8 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     if ($this->plugin instanceof PluginFormInterface) {
       $this->plugin->validateConfigurationForm($form, $form_state);
diff --git a/core/modules/aggregator/src/FeedForm.php b/core/modules/aggregator/src/FeedForm.php
index aa5069c..a9ef844 100644
--- a/core/modules/aggregator/src/FeedForm.php
+++ b/core/modules/aggregator/src/FeedForm.php
@@ -22,7 +22,10 @@ class FeedForm extends ContentEntityForm {
   public function save(array $form, FormStateInterface $form_state) {
     $feed = $this->entity;
     $insert = (bool) $feed->id();
-    $feed->save();
+
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
+
     if ($insert) {
       drupal_set_message($this->t('The feed %feed has been updated.', array('%feed' => $feed->label())));
       $form_state->setRedirectUrl($feed->urlInfo('canonical'));
diff --git a/core/modules/block/src/BlockForm.php b/core/modules/block/src/BlockForm.php
index ac9406a..07da65b 100644
--- a/core/modules/block/src/BlockForm.php
+++ b/core/modules/block/src/BlockForm.php
@@ -273,8 +273,8 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // The Block Entity form puts all block plugin form elements in the
     // settings form element, so just pass that to the block for validation.
diff --git a/core/modules/block_content/src/BlockContentForm.php b/core/modules/block_content/src/BlockContentForm.php
index 379e576..839ce9c 100644
--- a/core/modules/block_content/src/BlockContentForm.php
+++ b/core/modules/block_content/src/BlockContentForm.php
@@ -178,7 +178,10 @@ public function save(array $form, FormStateInterface $form_state) {
     }
 
     $insert = $block->isNew();
-    $block->save();
+
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
+
     $context = array('@type' => $block->bundle(), '%info' => $block->label());
     $logger = $this->logger('block_content');
     $block_type = $this->blockContentTypeStorage->load($block->bundle());
@@ -224,7 +227,8 @@ public function save(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
-    if ($this->entity->isNew()) {
+    $entity = parent::validateForm($form, $form_state);
+    if ($entity->isNew()) {
       $exists = $this->blockContentStorage->loadByProperties(array('info' => $form_state->getValue(['info', 0, 'value'])));
       if (!empty($exists)) {
         $form_state->setErrorByName('info', $this->t('A block with description %name already exists.', array(
@@ -232,6 +236,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
         )));
       }
     }
+    return $entity;
   }
 
 }
diff --git a/core/modules/comment/src/CommentForm.php b/core/modules/comment/src/CommentForm.php
index 0bcacee..1244447 100644
--- a/core/modules/comment/src/CommentForm.php
+++ b/core/modules/comment/src/CommentForm.php
@@ -258,7 +258,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
       '#type' => 'submit',
       '#value' => $this->t('Preview'),
       '#access' => $preview_mode != DRUPAL_DISABLED,
-      '#validate' => array('::validate'),
       '#submit' => array('::submitForm', '::preview'),
     );
 
@@ -361,7 +360,9 @@ public function save(array $form, FormStateInterface $form_state) {
     $logger = $this->logger('content');
 
     if ($this->currentUser->hasPermission('post comments') && ($this->currentUser->hasPermission('administer comments') || $entity->{$field_name}->status == CommentItemInterface::OPEN)) {
-      $comment->save();
+      // Make sure we do not skip the parent's entity save logic.
+      parent::save($form, $form_state);
+
       $form_state->setValue('cid', $comment->id());
 
       // Add a log entry.
diff --git a/core/modules/contact/src/ContactFormEditForm.php b/core/modules/contact/src/ContactFormEditForm.php
index 3300c59..c545b0e 100644
--- a/core/modules/contact/src/ContactFormEditForm.php
+++ b/core/modules/contact/src/ContactFormEditForm.php
@@ -112,8 +112,8 @@ public function form(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // Validate and each email recipient.
     $recipients = explode(',', $form_state->getValue('recipients'));
diff --git a/core/modules/contact/src/MessageForm.php b/core/modules/contact/src/MessageForm.php
index 78de6dd..899a346 100644
--- a/core/modules/contact/src/MessageForm.php
+++ b/core/modules/contact/src/MessageForm.php
@@ -168,8 +168,8 @@ public function actions(array $form, FormStateInterface $form_state) {
     $elements = parent::actions($form, $form_state);
     $elements['submit']['#value'] = $this->t('Send message');
     $elements['preview'] = array(
+      '#type' => 'submit',
       '#value' => $this->t('Preview'),
-      '#validate' => array('::validate'),
       '#submit' => array('::submitForm', '::preview'),
     );
     return $elements;
@@ -187,10 +187,8 @@ public function preview(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
-
-    $message = $this->entity;
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $message = parent::validateForm($form, $form_state);
 
     // Check if flood control has been activated for sending emails.
     if (!$this->currentUser()->hasPermission('administer contact forms') && (!$message->isPersonal() || !$this->currentUser()->hasPermission('administer users'))) {
@@ -204,6 +202,8 @@ public function validate(array $form, FormStateInterface $form_state) {
         )));
       }
     }
+
+    return $message;
   }
 
   /**
@@ -225,10 +225,11 @@ public function save(array $form, FormStateInterface $form_state) {
     else {
       $form_state->setRedirect('<front>');
     }
+
     // Save the message. In core this is a no-op but should contrib wish to
     // implement message storage, this will make the task of swapping in a real
     // storage controller straight-forward.
-    $message->save();
+    parent::save($form, $form_state);
   }
 
   /**
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index 28529eb..10cf5f3 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -473,9 +473,7 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En
     $form['#entity_builders'][] = array($this, 'entityFormEntityBuild');
 
     // Handle entity validation.
-    if (isset($form['actions']['submit'])) {
-      $form['actions']['submit']['#validate'][] = array($this, 'entityFormValidate');
-    }
+    $form['#validate'][] = array($this, 'entityFormValidate');
 
     // Handle entity deletion.
     if (isset($form['actions']['delete'])) {
diff --git a/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php b/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php
index d6d801b..350a2e9 100644
--- a/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php
+++ b/core/modules/field_ui/src/Form/EntityDisplayModeAddForm.php
@@ -38,8 +38,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $entity_t
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     $form_state->setValueForElement($form['id'], $this->targetEntityTypeId . '.' . $form_state->getValue('id'));
   }
diff --git a/core/modules/field_ui/src/Form/FieldConfigEditForm.php b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
index 866ff9a..52ace95 100644
--- a/core/modules/field_ui/src/Form/FieldConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldConfigEditForm.php
@@ -150,8 +150,8 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     if (isset($form['default_value'])) {
       $item = $form['#entity']->get($this->entity->getName());
diff --git a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
index 58c4ea9..adafcc7 100644
--- a/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
+++ b/core/modules/field_ui/src/Form/FieldStorageConfigEditForm.php
@@ -146,8 +146,8 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // Validate field cardinality.
     if ($form_state->getValue('cardinality') === 'number' && !$form_state->getValue('cardinality_number')) {
diff --git a/core/modules/filter/src/FilterFormatFormBase.php b/core/modules/filter/src/FilterFormatFormBase.php
index ff140c3..77b5433 100644
--- a/core/modules/filter/src/FilterFormatFormBase.php
+++ b/core/modules/filter/src/FilterFormatFormBase.php
@@ -204,8 +204,8 @@ public function exists($format_id) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // @todo Move trimming upstream.
     $format_format = trim($form_state->getValue('format'));
diff --git a/core/modules/forum/src/Form/ForumForm.php b/core/modules/forum/src/Form/ForumForm.php
index 1a517c0..1f270cd 100644
--- a/core/modules/forum/src/Form/ForumForm.php
+++ b/core/modules/forum/src/Form/ForumForm.php
@@ -76,8 +76,14 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
+    // See ContentEntityForm::save().
+    if (!$form_state->getTemporaryValue('entity_validated')) {
+      throw new \LogicException('Entity validation was skipped.');
+    }
+
     $term = $this->entity;
     $term_storage = $this->entityManager->getStorage('taxonomy_term');
+
     $status = $term_storage->save($term);
 
     $route_name = $this->urlStub == 'container' ? 'entity.taxonomy_term.forum_edit_container_form' : 'entity.taxonomy_term.forum_edit_form';
diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php
index 24f5142..dae7fb5 100644
--- a/core/modules/forum/src/Form/Overview.php
+++ b/core/modules/forum/src/Form/Overview.php
@@ -86,9 +86,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     // Remove the alphabetical reset.
     unset($form['actions']['reset_alphabetical']);
 
-    // The form needs to have submit and validate handlers set explicitly.
     // Use the existing taxonomy overview submit handler.
-    $form['#submit'] = array('::submitForm');
     $form['terms']['#empty'] = $this->t('No containers or forums available. <a href="@container">Add container</a> or <a href="@forum">Add forum</a>.', array(
       '@container' => $this->url('forum.add_container'),
       '@forum' => $this->url('forum.add_forum')
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
index 747c1fd..0398426 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\ContentEntityForm;
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Menu\MenuParentFormSelectorInterface;
@@ -120,18 +121,18 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function save(array $form, FormStateInterface $form_state) {
-    // The entity is rebuilt in parent::submit().
-    $menu_link = $this->entity;
-    $saved = $menu_link->save();
+    try {
+      // Make sure we do not skip the parent's entity save logic.
+      parent::save($form, $form_state);
 
-    if ($saved) {
+      // The entity is rebuilt in parent::submit().
       drupal_set_message($this->t('The menu link has been saved.'));
       $form_state->setRedirect(
         'entity.menu_link_content.canonical',
-        array('menu_link_content' => $menu_link->id())
+        array('menu_link_content' => $this->entity->id())
       );
     }
-    else {
+    catch (EntityStorageException $e) {
       drupal_set_message($this->t('There was an error saving the menu link.'), 'error');
       $form_state->setRebuild();
     }
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index 0c097ba..f26dade 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -415,7 +415,7 @@ function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_stat
   );
   $options_cacheability->applyTo($form['menu']['menu_parent']);
 
-  $form['actions']['submit']['#validate'][] = 'menu_ui_form_node_type_form_validate';
+  $form['#validate'][] = 'menu_ui_form_node_type_form_validate';
   $form['#entity_builders'][] = 'menu_ui_form_node_type_form_builder';
 }
 
diff --git a/core/modules/node/src/NodeForm.php b/core/modules/node/src/NodeForm.php
index c584660..bcb4040 100644
--- a/core/modules/node/src/NodeForm.php
+++ b/core/modules/node/src/NodeForm.php
@@ -297,7 +297,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
       '#access' => $preview_mode != DRUPAL_DISABLED && ($node->access('create') || $node->access('update')),
       '#value' => t('Preview'),
       '#weight' => 20,
-      '#validate' => array('::validate'),
       '#submit' => array('::submitForm', '::preview'),
     );
 
@@ -375,7 +374,10 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
   public function save(array $form, FormStateInterface $form_state) {
     $node = $this->entity;
     $insert = $node->isNew();
-    $node->save();
+
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
+
     $node_link = $node->link($this->t('View'));
     $context = array('@type' => $node->getType(), '%title' => $node->label(), 'link' => $node_link);
     $t_args = array('@type' => node_get_type_label($node), '%title' => $node->label());
diff --git a/core/modules/node/src/NodeTypeForm.php b/core/modules/node/src/NodeTypeForm.php
index 6dfc669..ff757f9 100644
--- a/core/modules/node/src/NodeTypeForm.php
+++ b/core/modules/node/src/NodeTypeForm.php
@@ -204,8 +204,8 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     $id = trim($form_state->getValue('type'));
     // '0' is invalid, since elsewhere we check it using empty().
diff --git a/core/modules/responsive_image/src/ResponsiveImageStyleForm.php b/core/modules/responsive_image/src/ResponsiveImageStyleForm.php
index a22742a..5763bc8 100644
--- a/core/modules/responsive_image/src/ResponsiveImageStyleForm.php
+++ b/core/modules/responsive_image/src/ResponsiveImageStyleForm.php
@@ -142,7 +142,8 @@ public function form(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
     // Only validate on edit.
     if ($form_state->hasValue('keyed_styles')) {
       // Check if another breakpoint group is selected.
diff --git a/core/modules/search/src/Form/SearchPageFormBase.php b/core/modules/search/src/Form/SearchPageFormBase.php
index 95afb4b..c41fd5b 100644
--- a/core/modules/search/src/Form/SearchPageFormBase.php
+++ b/core/modules/search/src/Form/SearchPageFormBase.php
@@ -144,8 +144,8 @@ public function exists($id) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // Ensure each path is unique.
     $path = $this->entityQuery->get('search_page')
diff --git a/core/modules/shortcut/src/ShortcutForm.php b/core/modules/shortcut/src/ShortcutForm.php
index c0f8714..0aa6e53 100644
--- a/core/modules/shortcut/src/ShortcutForm.php
+++ b/core/modules/shortcut/src/ShortcutForm.php
@@ -27,13 +27,16 @@ class ShortcutForm extends ContentEntityForm {
    */
   public function save(array $form, FormStateInterface $form_state) {
     $entity = $this->entity;
-    $status = $entity->save();
+    $insert = $entity->isNew();
 
-    if ($status == SAVED_UPDATED) {
-      $message = $this->t('The shortcut %link has been updated.', array('%link' => $entity->getTitle()));
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
+
+    if ($insert) {
+      $message = $this->t('Added a shortcut for %title.', array('%title' => $entity->getTitle()));
     }
     else {
-      $message = $this->t('Added a shortcut for %title.', array('%title' => $entity->getTitle()));
+      $message = $this->t('The shortcut %link has been updated.', array('%link' => $entity->getTitle()));
     }
     drupal_set_message($message);
 
diff --git a/core/modules/system/src/Form/DateFormatFormBase.php b/core/modules/system/src/Form/DateFormatFormBase.php
index aeaa12a..b8d0e17 100644
--- a/core/modules/system/src/Form/DateFormatFormBase.php
+++ b/core/modules/system/src/Form/DateFormatFormBase.php
@@ -126,8 +126,8 @@ public function form(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // The machine name field should already check to see if the requested
     // machine name is available. Regardless of machine_name or human readable
diff --git a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
index 426ac5b..4f223e3 100644
--- a/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
+++ b/core/modules/system/src/Tests/Entity/FieldWidgetConstraintValidatorTest.php
@@ -89,8 +89,12 @@ protected function getErrorsForEntity(EntityInterface $entity, $hidden_fields =
     \Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state);
 
     // Validate the field constraint.
-    $form_state->getFormObject()->setEntity($entity)->setFormDisplay($display, $form_state);
-    $form_state->getFormObject()->validate($form, $form_state);
+    /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
+    $form_object = $form_state->getFormObject();
+    $form_object
+      ->setEntity($entity)
+      ->setFormDisplay($display, $form_state)
+      ->validateForm($form, $form_state);
 
     return $form_state->getErrors();
   }
diff --git a/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php b/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php
index fb3c486..e679585 100644
--- a/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php
+++ b/core/modules/system/tests/modules/entity_test/src/EntityTestForm.php
@@ -59,7 +59,9 @@ public function save(array $form, FormStateInterface $form_state) {
     }
 
     $is_new = $entity->isNew();
-    $entity->save();
+
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
 
     if ($is_new) {
      $message = t('%entity_type @id has been created.', array('@id' => $entity->id(), '%entity_type' => $entity->getEntityTypeId()));
diff --git a/core/modules/taxonomy/src/TermForm.php b/core/modules/taxonomy/src/TermForm.php
index a0d1279..42f9ee5 100644
--- a/core/modules/taxonomy/src/TermForm.php
+++ b/core/modules/taxonomy/src/TermForm.php
@@ -96,8 +96,8 @@ public function form(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     // Ensure numeric values.
     if ($form_state->hasValue('weight') && !is_numeric($form_state->getValue('weight'))) {
@@ -125,19 +125,19 @@ public function buildEntity(array $form, FormStateInterface $form_state) {
    */
   public function save(array $form, FormStateInterface $form_state) {
     $term = $this->entity;
+    $insert = $term->isNew();
 
-    $result = $term->save();
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
 
     $link = $term->link($this->t('Edit'), 'edit-form');
-    switch ($result) {
-      case SAVED_NEW:
-        drupal_set_message($this->t('Created new term %term.', array('%term' => $term->getName())));
-        $this->logger('taxonomy')->notice('Created new term %term.', array('%term' => $term->getName(), 'link' => $link));
-        break;
-      case SAVED_UPDATED:
-        drupal_set_message($this->t('Updated term %term.', array('%term' => $term->getName())));
-        $this->logger('taxonomy')->notice('Updated term %term.', array('%term' => $term->getName(), 'link' => $link));
-        break;
+    if ($insert) {
+      drupal_set_message($this->t('Created new term %term.', array('%term' => $term->getName())));
+      $this->logger('taxonomy')->notice('Created new term %term.', array('%term' => $term->getName(), 'link' => $link));
+    }
+    else {
+      drupal_set_message($this->t('Updated term %term.', array('%term' => $term->getName())));
+      $this->logger('taxonomy')->notice('Updated term %term.', array('%term' => $term->getName(), 'link' => $link));
     }
 
     $current_parent_count = count($form_state->getValue('parent'));
diff --git a/core/modules/user/src/ProfileForm.php b/core/modules/user/src/ProfileForm.php
index e119693..5cfea9c 100644
--- a/core/modules/user/src/ProfileForm.php
+++ b/core/modules/user/src/ProfileForm.php
@@ -48,9 +48,9 @@ protected function actions(array $form, FormStateInterface $form_state) {
    */
   public function save(array $form, FormStateInterface $form_state) {
     $account = $this->entity;
-    $account->save();
+    // Make sure we do not skip the parent's entity save logic.
+    parent::save($form, $form_state);
     $form_state->setValue('uid', $account->id());
-
     drupal_set_message($this->t('The changes have been saved.'));
   }
 
diff --git a/core/modules/user/src/RegisterForm.php b/core/modules/user/src/RegisterForm.php
index 53dad45..94ad8a5 100644
--- a/core/modules/user/src/RegisterForm.php
+++ b/core/modules/user/src/RegisterForm.php
@@ -102,9 +102,10 @@ public function save(array $form, FormStateInterface $form_state) {
     $admin = $form_state->getValue('administer_users');
     $notify = !$form_state->isValueEmpty('notify');
 
-    // Save has no return value so this cannot be tested.
-    // Assume save has gone through correctly.
-    $account->save();
+    // Save has no return value so this cannot be tested. Assume save has gone
+    // through correctly. Make sure we do not skip the parent's entity save
+    // logic.
+    parent::save($form, $form_state);
 
     $form_state->set('user', $account);
     $form_state->setValue('uid', $account->id());
diff --git a/core/modules/views_ui/src/ViewAddForm.php b/core/modules/views_ui/src/ViewAddForm.php
index 6db3e95..5b816d9 100644
--- a/core/modules/views_ui/src/ViewAddForm.php
+++ b/core/modules/views_ui/src/ViewAddForm.php
@@ -162,7 +162,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
+  public function validateForm(array &$form, FormStateInterface $form_state) {
     $wizard_type = $form_state->getValue(array('show', 'wizard_key'));
     $wizard_instance = $this->wizardManager->createInstance($wizard_type);
     $form_state->set('wizard', $wizard_instance->getPluginDefinition());
diff --git a/core/modules/views_ui/src/ViewDuplicateForm.php b/core/modules/views_ui/src/ViewDuplicateForm.php
index 36f1a7f..ccc1a49 100644
--- a/core/modules/views_ui/src/ViewDuplicateForm.php
+++ b/core/modules/views_ui/src/ViewDuplicateForm.php
@@ -58,7 +58,6 @@ protected function actions(array $form, FormStateInterface $form_state) {
     $actions['submit'] = array(
       '#type' => 'submit',
       '#value' => $this->t('Duplicate'),
-      '#submit' => array('::submitForm'),
     );
     return $actions;
   }
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 9e598f2..b11f54a 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -264,8 +264,8 @@ protected function actions(array $form, FormStateInterface $form_state) {
   /**
    * {@inheritdoc}
    */
-  public function validate(array $form, FormStateInterface $form_state) {
-    parent::validate($form, $form_state);
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    parent::validateForm($form, $form_state);
 
     $view = $this->entity;
     if ($view->isLocked()) {
