=== modified file 'includes/form.inc'
--- includes/form.inc	2009-11-27 15:14:58 +0000
+++ includes/form.inc	2009-11-29 13:32:25 +0000
@@ -333,11 +333,6 @@ function drupal_rebuild_form($form_id, &
     form_set_cache($form_build_id, $form, $form_state);
   }
 
-  // Clear out all post data, as we don't want the previous step's
-  // data to pollute this one and trigger validate/submit handling,
-  // then process the form for rendering.
-  $form_state['input'] = array();
-
   // Also clear out all group associations as these might be different
   // when rerendering the form.
   $form_state['groups'] = array();
@@ -931,9 +926,11 @@ function _form_validate($elements, &$for
  */
 function form_execute_handlers($type, &$form, &$form_state) {
   $return = FALSE;
+  // If there was a button pressed, use its handlers.
   if (isset($form_state[$type . '_handlers'])) {
     $handlers = $form_state[$type . '_handlers'];
   }
+  // Otherwise, check for a form level handler.
   elseif (isset($form['#' . $type])) {
     $handlers = $form['#' . $type];
   }
@@ -1205,21 +1202,18 @@ function _form_builder_handle_input_elem
       foreach ($element['#parents'] as $parent) {
         $input = isset($input[$parent]) ? $input[$parent] : NULL;
       }
-      // If we have input for the current element, assign it to the #value property.
-      if (!$form_state['programmed'] || isset($input)) {
-        // Call #type_value to set the form value;
-        if (function_exists($value_callback)) {
-          $element['#value'] = $value_callback($element, $input, $form_state);
-        }
-        if (!isset($element['#value']) && isset($input)) {
-          $element['#value'] = $input;
-        }
+      // Call #type_value to set the form value;
+      if (function_exists($value_callback)) {
+        $element['#value'] = $value_callback($element, $input, $form_state);
       }
-      // Mark all posted values for validation.
-      if (isset($element['#value']) || (!empty($element['#required']))) {
-        $element['#needs_validation'] = TRUE;
+      if (!isset($element['#value']) && isset($input)) {
+        $element['#value'] = $input;
       }
     }
+    // Mark all posted values for validation.
+    if (isset($element['#value']) || (!empty($element['#required']))) {
+      $element['#needs_validation'] = TRUE;
+    }
     // Load defaults.
     if (!isset($element['#value'])) {
       // Call #type_value without a second argument to request default_value handling.

=== modified file 'modules/node/node.pages.inc'
--- modules/node/node.pages.inc	2009-11-08 10:02:41 +0000
+++ modules/node/node.pages.inc	2009-11-29 14:31:32 +0000
@@ -111,6 +111,7 @@ function node_form($form, &$form_state, 
   global $user;
 
   if (isset($form_state['node'])) {
+    $form_state['input'] = array();
     $node = (object)($form_state['node'] + (array)$node);
   }
   if (isset($form_state['node_preview'])) {

=== modified file 'modules/simpletest/tests/ajax.test'
--- modules/simpletest/tests/ajax.test	2009-11-22 02:48:37 +0000
+++ modules/simpletest/tests/ajax.test	2009-11-29 13:08:27 +0000
@@ -141,3 +141,48 @@ class AJAXCommandsTestCase extends AJAXT
     $this->assertTrue($command['command'] == 'restripe' && $command['selector'] == '#restripe_table', "'restripe' AJAX command issued with correct selector");
   }
 }
+
+/**
+ * Test that $form_state['values'] is properly delivered to $ajax['callback'].
+ */
+class AJAXFormValuesTestCase extends AJAXTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'AJAX command form values',
+      'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
+      'group' => 'AJAX',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->web_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Create a simple form, then POST to system/ajax to change to it.
+   */
+  function testSimpleAJAXFormValue() {
+    // Verify form values of a select element.
+    foreach(array('red', 'green', 'blue') as $item) {
+      $edit = array(
+        'select' => $item,
+      );
+      $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select');
+      $data_command = $commands[2];
+      $this->assertEqual($data_command['value'], $item);
+    }
+
+    // Verify form values of a checkbox element.
+    foreach(array(FALSE, TRUE) as $item) {
+      $edit = array(
+        'checkbox' => $item,
+      );
+      $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox');
+      $data_command = $commands[2];
+      $this->assertEqual((int) $data_command['value'], (int) $item);
+    }
+  }
+}

=== modified file 'modules/simpletest/tests/ajax_forms_test.module'
--- modules/simpletest/tests/ajax_forms_test.module	2009-11-22 02:48:37 +0000
+++ modules/simpletest/tests/ajax_forms_test.module	2009-11-29 13:34:09 +0000
@@ -57,6 +57,7 @@ function ajax_forms_test_simple_form($fo
     '#type' => 'submit',
     '#value' => t('submit'),
   );
+
   return $form;
 }
 

=== modified file 'modules/simpletest/tests/form.test'
--- modules/simpletest/tests/form.test	2009-11-18 18:51:11 +0000
+++ modules/simpletest/tests/form.test	2009-11-29 13:00:35 +0000
@@ -70,7 +70,8 @@ class FormsTestCase extends DrupalWebTes
       foreach ($data['empty_values'] as $key => $empty) {
         foreach (array(TRUE, FALSE) as $required) {
           $form_id = $this->randomName();
-          $form = $form_state = array();
+          $form = array();
+          $form_state = form_state_defaults();
           form_clear_error();
           $form['op'] = array('#type' => 'submit', '#value' => t('Submit'));
           $element = $data['element']['#title'];
@@ -131,9 +132,7 @@ class FormsTestCase extends DrupalWebTes
     $this->assertRaw(t('!name field is required.', array('!name' => 'required_checkbox')), t('A required checkbox is actually mandatory'));
 
     // Now try to submit the form correctly.
-    $this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit'));
-
-    $values = json_decode($this->drupalGetContent(), TRUE);
+    $values = drupal_json_decode($this->drupalPost(NULL, array('required_checkbox' => 1), t('Submit')));
     $expected_values = array(
       'disabled_checkbox_on' => 'disabled_checkbox_on',
       'disabled_checkbox_off' => '',
@@ -504,8 +503,7 @@ class FormStateValuesCleanTestCase exten
    * Tests form_state_values_clean().
    */
   function testFormStateValuesClean() {
-    $this->drupalPost('form_test/form-state-values-clean', array(), t('Submit'));
-    $values = json_decode($this->content, TRUE);
+    $values = drupal_json_decode($this->drupalPost('form_test/form-state-values-clean', array(), t('Submit')));
 
     // Setup the expected result.
     $result = array(
@@ -533,3 +531,36 @@ class FormStateValuesCleanTestCase exten
   }
 }
 
+/**
+ * Tests form rebuilding.
+ *
+ * @todo Add tests for other aspects of form rebuilding.
+ */
+class FormsRebuildTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Form rebuilding',
+      'description' => 'Tests functionality of drupal_rebuild_form().',
+      'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+
+    $this->web_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($this->web_user);
+  }
+
+  /**
+   * Tests preservation of values during an "add another item" operation.
+   */
+  function testRebuildPreservesValues() {
+    $edit = array(
+      'grocery' => 'apple',
+    );
+    $this->drupalPost('form-test/form-rebuild-preserve-values', $edit, 'Submit');
+
+    $this->assertFieldByName('grocery', 'apple', t('Item value kept in a rebuild.'));
+  }
+}

=== modified file 'modules/simpletest/tests/form_test.module'
--- modules/simpletest/tests/form_test.module	2009-11-21 17:01:31 +0000
+++ modules/simpletest/tests/form_test.module	2009-11-29 12:58:56 +0000
@@ -78,6 +78,14 @@ function form_test_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  $items['form-test/form-rebuild-preserve-values'] = array(
+    'title' => 'Form values preservation during rebuild test',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_form_rebuild_preserve_values_form'),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+
   return $items;
 }
 
@@ -477,3 +485,29 @@ function _form_test_checkbox_submit($for
   drupal_json_output($form_state['values']);
   exit();
 }
+
+/**
+ * Form builder for testing preservation of values during a rebuild.
+ *
+ * Creates a form with two "add another item" buttons, each controlling a
+ * different element. Used to test that a rebuilt form has the expected values.
+ */
+function form_test_form_rebuild_preserve_values_form($form, &$form_state) {
+  $form['grocery'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Grocery item',
+    '#default_value' => 'DEFAULT',
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => 'Submit',
+  );
+  return $form;
+}
+
+/**
+ * Form submit handler for form_test_form_rebuild_preserve_values_form().
+ */
+function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state) {
+  $form_state['rebuild'] = TRUE;
+}

