diff --git modules/book/book.module modules/book/book.module
index d60ab18..318b483 100644
--- modules/book/book.module
+++ modules/book/book.module
@@ -399,7 +399,7 @@ function book_get_books() {
 function book_form_alter(&$form, &$form_state, $form_id) {
   if (!empty($form['#node_edit_form'])) {
     // Add elements to the node form.
-    $node = $form['#node'];
+    $node = $form_state['node'];
 
     $access = user_access('administer book outlines');
     if (!$access) {
@@ -419,13 +419,18 @@ function book_form_alter(&$form, &$form_state, $form_id) {
          // won't be changed via AJAX, a button is provided in the node form to submit
          // the form and generate options in the parent select corresponding to the
          // selected book. This is similar to what happens during a node preview.
-        '#submit' => array('node_form_submit_build_node'),
+        '#submit' => array('book_node_form_rebuild'),
         '#weight' => 20,
       );
     }
   }
 }
 
+function book_node_form_rebuild($form, &$form_state) {
+  $form_state['node']->book = $form_state['values']['book'];
+  $form_state['rebuild'] = TRUE;
+}
+
 /**
  * Build the parent selection form element for the node form or outline tab.
  *
diff --git modules/field/field.form.inc modules/field/field.form.inc
index 63b9a84..89e60e6 100644
--- modules/field/field.form.inc
+++ modules/field/field.form.inc
@@ -131,11 +131,6 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
  * - drag-n-drop value reordering
  */
 function field_multiple_value_form($field, $instance, $langcode, $items, &$form, &$form_state) {
-  // This form has its own multistep persistance.
-  if ($form_state['rebuild']) {
-    $form_state['input'] = array();
-  }
-
   $field_name = $field['field_name'];
 
   // Determine the number of widgets to display.
@@ -368,16 +363,16 @@ function field_default_form_errors($entity_type, $entity, $field, $instance, $la
  * to return just the changed part of the form.
  */
 function field_add_more_submit($form, &$form_state) {
-  // Set the form to rebuild and run submit handlers.
+  // If a builder function has been specified, execute it. Entity forms can
+  // use it to implement their own form persistence mode.
   if (isset($form['#builder_function']) && function_exists($form['#builder_function'])) {
-    $entity = $form['#builder_function']($form, $form_state);
-
-    // Make the changes we want to the form state.
-    $field_name = $form_state['clicked_button']['#field_name'];
-    $langcode = $form_state['clicked_button']['#language'];
-    if ($form_state['values'][$field_name . '_add_more']) {
-      $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name][$langcode]);
-    }
+    $form['#builder_function']($form, $form_state);
+  }
+  // Make the changes we want to the form state.
+  $field_name = $form_state['clicked_button']['#field_name'];
+  $langcode = $form_state['clicked_button']['#language'];
+  if ($form_state['values'][$field_name . '_add_more']) {
+    $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name][$langcode]);
   }
 }
 
diff --git modules/field/tests/field.test modules/field/tests/field.test
index 29a9e33..5205fec 100644
--- modules/field/tests/field.test
+++ modules/field/tests/field.test
@@ -1562,18 +1562,17 @@ class FieldFormTestCase extends FieldTestCase {
       $edit["$this->field_name[$langcode][$delta][value]"] = $value;
       $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
       // We'll need three slightly different formats to check the values.
-      $values[$weight] = $value;
+      $values[$delta] = $value;
+      $weights[$delta] = $weight;
       $field_values[$weight]['value'] = (string)$value;
       $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
     }
 
     // Press 'add more' button -> 4 widgets
     $this->drupalPost(NULL, $edit, t('Add another item'));
-    ksort($values);
-    $values = array_values($values);
     for ($delta = 0; $delta <= $delta_range; $delta++) {
       $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
-      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "Widget $delta has the right weight");
+      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
     }
     ksort($pattern);
     $pattern = implode('.*', array_values($pattern));
@@ -1632,7 +1631,8 @@ class FieldFormTestCase extends FieldTestCase {
       $edit["$this->field_name[$langcode][$delta][value]"] = $value;
       $edit["$this->field_name[$langcode][$delta][_weight]"] = $weight;
       // We'll need three slightly different formats to check the values.
-      $values[$weight] = $value;
+      $values[$delta] = $value;
+      $weights[$delta] = $weight;
       $field_values[$weight]['value'] = (string)$value;
       $pattern[$weight] = "<input [^>]*value=\"$value\" [^>]*";
     }
@@ -1641,11 +1641,9 @@ class FieldFormTestCase extends FieldTestCase {
     $commands = $this->drupalPostAJAX(NULL, $edit, $this->field_name . '_add_more');
     $this->content = $commands[1]['data'];
 
-    ksort($values);
-    $values = array_values($values);
     for ($delta = 0; $delta <= $delta_range; $delta++) {
       $this->assertFieldByName("$this->field_name[$langcode][$delta][value]", $values[$delta], "Widget $delta is displayed and has the right value");
-      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $delta, "Widget $delta has the right weight");
+      $this->assertFieldByName("$this->field_name[$langcode][$delta][_weight]", $weights[$delta], "Widget $delta has the right weight");
     }
     ksort($pattern);
     $pattern = implode('.*', array_values($pattern));
diff --git modules/node/node.pages.inc modules/node/node.pages.inc
index fce7bfd..b9aaaf4 100644
--- modules/node/node.pages.inc
+++ modules/node/node.pages.inc
@@ -72,6 +72,7 @@ function node_add($type) {
 }
 
 function node_form_validate($form, &$form_state) {
+  // @todo for D8: Fix to don't pass that half baken $node object around.
   $node = (object)$form_state['values'];
   node_validate($node, $form);
 
@@ -85,14 +86,11 @@ function node_form_validate($form, &$form_state) {
  */
 function node_form($form, &$form_state, $node) {
   global $user;
-  // This form has its own multistep persistence.
-  if ($form_state['rebuild']) {
-    $form_state['input'] = array();
-  }
 
-  if (isset($form_state['node'])) {
-    $node = (object)($form_state['node'] + (array)$node);
-  }
+  // Submit handlers may have updated $form_state['node'] so keep this changes.
+  $form_state += array('node' => $node);
+  $node = $form_state['node'];
+
   if (isset($form_state['node_preview'])) {
     $form['#prefix'] = $form_state['node_preview'];
   }
@@ -134,6 +132,7 @@ function node_form($form, &$form_state, $node) {
     $form['title']['#weight'] = -5;
   }
 
+  // @todo Drop this for D8 so only $form_state[$entity_type] is used.
   $form['#node'] = $node;
 
   $form['additional_settings'] = array(
@@ -266,7 +265,6 @@ function node_form($form, &$form_state, $node) {
   $form['#validate'][] = 'node_form_validate';
   $form['#theme'] = array($node->type . '_node_form', 'node_form');
 
-  $form['#builder_function'] = 'node_form_submit_build_node';
   field_attach_form('node', $node, $form, $form_state, $node->language);
 
   return $form;
@@ -289,6 +287,7 @@ function node_form_delete_submit($form, &$form_state) {
 function node_form_build_preview($form, &$form_state) {
   $node = node_form_submit_build_node($form, $form_state);
   $form_state['node_preview'] = node_preview($node);
+  $form_state['rebuild'] = TRUE;
 }
 
 /**
@@ -403,7 +402,6 @@ function node_form_submit($form, &$form_state) {
     drupal_set_message(t('@type %title has been updated.', $t_args));
   }
   if ($node->nid) {
-    unset($form_state['rebuild']);
     $form_state['values']['nid'] = $node->nid;
     $form_state['nid'] = $node->nid;
     $form_state['redirect'] = 'node/' . $node->nid;
@@ -412,23 +410,30 @@ function node_form_submit($form, &$form_state) {
     // In the unlikely case something went wrong on save, the node will be
     // rebuilt and node form redisplayed the same way as in preview.
     drupal_set_message(t('The post could not be saved.'), 'error');
+    $form_state['rebuild'] = TRUE;
   }
 }
 
 /**
- * Build a node by processing submitted form values and prepare for a form rebuild.
+ * Build a node by processing submitted form values and prepare for a form
+ * rebuild. As this function invokes form level submit handlers only call it
+ * if the form has already been fully validated.
  */
 function node_form_submit_build_node($form, &$form_state) {
   // Unset any button-level handlers, execute all the form-level submit
   // functions to process the form values into an updated node.
   unset($form_state['submit_handlers']);
   form_execute_handlers('submit', $form, $form_state);
-  $node = node_submit((object)$form_state['values']);
+  $node = node_submit((object)($form_state['values'] + (array)$form_state['node']));
 
+  // Invoke the node submit hook.
+  foreach (module_implements('node_submit') as $module) {
+    $function = $module . '_node_submit';
+    $function($node, $form, $form_state);
+  }
   field_attach_submit('node', $node, $form, $form_state);
+  $form_state['node'] = $node;
 
-  $form_state['node'] = (array)$node;
-  $form_state['rebuild'] = TRUE;
   return $node;
 }
 
diff --git modules/poll/poll.module modules/poll/poll.module
index 41ac9e2..b51b9ec 100644
--- modules/poll/poll.module
+++ modules/poll/poll.module
@@ -355,15 +355,15 @@ function poll_form($node, &$form_state) {
  * return just the changed part of the form.
  */
 function poll_more_choices_submit($form, &$form_state) {
-  include_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'node') . '/node.pages.inc';
-  // Set the form to rebuild and run submit handlers.
-  node_form_submit_build_node($form, $form_state);
-
   // Make the changes we want to the form state.
   if ($form_state['values']['poll_more']) {
     $n = $_GET['q'] == 'system/ajax' ? 1 : 5;
     $form_state['choice_count'] = count($form_state['values']['choice']) + $n;
   }
+  $form_state['node']->choice = array_values($form_state['values']['choice']);
+  // Disable the form API persistence for the choice form elements.
+  unset($form_state['input']['choice']);
+  $form_state['rebuild'] = TRUE;
 }
 
 function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight = 0, $size = 10) {
@@ -425,6 +425,8 @@ function poll_node_form_submit(&$form, &$form_state) {
   // Renumber fields
   $form_state['values']['choice'] = array_values($form_state['values']['choice']);
   $form_state['values']['teaser'] = poll_teaser((object)$form_state['values']);
+  // Disable the form API persistence for the choice form elements.
+  unset($form_state['input']['choice']);
 }
 
 /**
