diff --git includes/ajax.inc includes/ajax.inc
index c5f3023..3d2ab0b 100644
--- includes/ajax.inc
+++ includes/ajax.inc
@@ -168,7 +168,7 @@
  *   $commands[] = ajax_command_changed('#object-1');
  *   // Menu 'page callback' and #ajax['callback'] functions are supposed to
  *   // return render arrays. If returning an AJAX commands array, it must be
- *   // encapsulated in a render array structure. 
+ *   // encapsulated in a render array structure.
  *   return array('#type' => 'ajax', '#commands' => $commands);
  * @endcode
  *
@@ -263,18 +263,15 @@ function ajax_get_form() {
  * enhanced function.
  */
 function ajax_form_callback() {
-  list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
-
-  // Build, validate and if possible, submit the form.
-  drupal_process_form($form_id, $form, $form_state);
-
-  // This call recreates the form relying solely on the $form_state that
-  // drupal_process_form() set up.
-  $form = drupal_rebuild_form($form_id, $form_state, $form);
-
-  // As part of drupal_process_form(), the element that triggered the form
-  // submission is determined, and in the case of AJAX, it might not be a
-  // button. This lets us route to the appropriate callback.
+  list($form, $form_state) = ajax_get_form();
+  drupal_process_form($form['#form_id'], $form, $form_state);
+
+  // We need to return the part of the form (or some other content) that needs
+  // to be re-rendered so the browser can update the page with changed content.
+  // Since this is the generic menu callback used by many AJAX elements, it is
+  // up to the #ajax['callback'] function of the element (may or may not be a
+  // button) that triggered the AJAX request to determine what needs to be
+  // rendered.
   if (!empty($form_state['triggering_element'])) {
     $callback = $form_state['triggering_element']['#ajax']['callback'];
   }
diff --git includes/batch.inc includes/batch.inc
index 36cfd14..92a5efe 100644
--- includes/batch.inc
+++ includes/batch.inc
@@ -495,9 +495,11 @@ function _batch_finished() {
     // Use drupal_redirect_form() to handle the redirection logic.
     drupal_redirect_form($_batch['form_state']);
 
-    // If no redirection happened, save the final $form_state value to be
-    // retrieved by drupal_get_form() and redirect to the originating page.
-    $_SESSION['batch_form_state'] = $_batch['form_state'];
+    // If no redirection happened redirect to the originating page. In case we
+    // need to rebuild save the final $form_state for drupal_build_form().
+    if (!empty($_batch['form_state']['rebuild'])) {
+      $_SESSION['batch_form_state'] = $_batch['form_state'];
+    }
     $function = $_batch['redirect_callback'];
     if (function_exists($function)) {
       $function($_batch['source_url'], array('query' => array('op' => 'finish', 'id' => $_batch['id'])));
diff --git includes/form.inc includes/form.inc
index 5a81c24..7b2c2a5 100644
--- includes/form.inc
+++ includes/form.inc
@@ -168,102 +168,66 @@ function drupal_build_form($form_id, &$form_state) {
     $form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST;
   }
 
+  // We've been redirected here after a batch processing: The form has already
+  // been processed, but needs to be rebuilt. See _batch_finished().
   if (isset($_SESSION['batch_form_state'])) {
-    // We've been redirected here after a batch processing : the form has
-    // already been processed, so we grab the post-process $form_state value
-    // and move on to form display. See _batch_finished() function.
     $form_state = $_SESSION['batch_form_state'];
     unset($_SESSION['batch_form_state']);
-  }
-  else {
-    // If the incoming input contains a form_build_id, we'll check the
-    // cache for a copy of the form in question. If it's there, we don't
-    // have to rebuild the form to proceed. In addition, if there is stored
-    // form_state data from a previous step, we'll retrieve it so it can
-    // be passed on to the form processing code.
-    if (isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id'])) {
-      $form_build_id = $form_state['input']['form_build_id'];
-      $form = form_get_cache($form_build_id, $form_state);
-    }
-
-    // If the previous bit of code didn't result in a populated $form
-    // object, we're hitting the form for the first time and we need
-    // to build it from scratch.
-    if (!isset($form)) {
-      // Record the filepath of the include file containing the original form,
-      // so the form builder callbacks can be loaded when the form is being
-      // rebuilt from cache on a different path (such as 'system/ajax'). See
-      // form_get_cache(). 
-      // $menu_get_item() is not available at installation time.
-      if (!isset($form_state['build_info']['file']) && !defined('MAINTENANCE_MODE')) {
-        $item = menu_get_item();
-        if (!empty($item['file'])) {
-          $form_state['build_info']['file'] = $item['file'];
-        }
+    return drupal_rebuild_form($form_id, $form_state);
+  }
+
+  // If the incoming input contains a form_build_id, we'll check the cache for a
+  // copy of the form in question. If it's there, we don't have to rebuild the
+  // form to proceed. In addition, if there is stored form_state data from a
+  // previous step, we'll retrieve it so it can be passed on to the form
+  // processing code.
+  if (isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id'])) {
+    $form = form_get_cache($form_state['input']['form_build_id'], $form_state);
+  }
+
+  // If the previous bit of code didn't result in a populated $form object,
+  // we're hitting the form for the first time and we need to build it from
+  // scratch.
+  if (!isset($form)) {
+    // Record the filepath of the include file containing the original form,
+    // so the form builder callbacks can be loaded when the form is being
+    // rebuilt from cache on a different path (such as 'system/ajax'). See
+    // form_get_cache().
+    // $menu_get_item() is not available at installation time.
+    if (!isset($form_state['build_info']['file']) && !defined('MAINTENANCE_MODE')) {
+      $item = menu_get_item();
+      if (!empty($item['file'])) {
+        $form_state['build_info']['file'] = $item['file'];
       }
+    }
 
-      $form = drupal_retrieve_form($form_id, $form_state);
-      $form_build_id = 'form-' . md5(uniqid(mt_rand(), TRUE));
-      $form['#build_id'] = $form_build_id;
+    $form = drupal_retrieve_form($form_id, $form_state);
+    $form['#build_id'] = 'form-' . md5(uniqid(mt_rand(), TRUE));
 
-      // Fix the form method, if it is 'get' in $form_state, but not in $form.
-      if ($form_state['method'] == 'get' && !isset($form['#method'])) {
-        $form['#method'] = 'get';
-      }
+    // Fix the form method, if it is 'get' in $form_state, but not in $form.
+    if ($form_state['method'] == 'get' && !isset($form['#method'])) {
+      $form['#method'] = 'get';
+    }
 
-      drupal_prepare_form($form_id, $form, $form_state);
-      // Store a copy of the unprocessed form to cache in case
-      // $form_state['cache'] is set.
-      $original_form = $form;
-    }
-
-    // Now that we know we have a form, we'll process it (validating,
-    // submitting, and handling the results returned by its submission
-    // handlers. Submit handlers accumulate data in the form_state by
-    // altering the $form_state variable, which is passed into them by
-    // reference.
-    drupal_process_form($form_id, $form, $form_state);
-  }
-
-  // Most simple, single-step forms will be finished by this point --
-  // drupal_process_form() usually redirects to another page (or to
-  // a 'fresh' copy of the form) once processing is complete. If one
-  // of the form's handlers has set $form_state['redirect'] to FALSE,
-  // the form will simply be re-rendered with the values still in its
-  // fields.
-  //
-  // If $form_state['rebuild'] has been set and input has been processed, we
-  // know that we're in a multi-part process of some sort and the form's
-  // workflow is not complete. We need to construct a fresh copy of the form,
-  // passing in the latest $form_state in addition to any other variables passed
-  // into drupal_get_form().
-  if ($form_state['rebuild'] && $form_state['process_input'] && !form_get_errors()) {
-    $form = drupal_rebuild_form($form_id, $form_state);
+    drupal_prepare_form($form_id, $form, $form_state);
+    // Store a copy of the unprocessed form to cache in case
+    // $form_state['cache'] gets set.
+    $original_form = $form;
   }
+
+  // Now that we know we have a form, we'll process it (validating, submitting,
+  // and handling the results returned by its submission handlers. Submit
+  // handlers accumulate data in the form_state by altering the $form_state
+  // variable, which is passed into them by reference.
+  drupal_process_form($form_id, $form, $form_state);
+
   // After processing the form, the form builder or a #process callback may
   // have set $form_state['cache'] to indicate that the original form and the
-  // $form_state shall be cached. But the form may only be cached if the
-  // special 'no_cache' property is not set to TRUE and we are not rebuilding.
-  elseif (isset($form_build_id) && $form_state['cache'] && empty($form_state['no_cache'])) {
-    // Cache the original, unprocessed form upon initial build of the form.
-    if (isset($original_form)) {
-      form_set_cache($form_build_id, $original_form, $form_state);
-    }
-    // After processing a cached form, only update the cached form state.
-    else {
-      form_set_cache($form_build_id, NULL, $form_state);
-    }
+  // $form_state shall be cached. But the form may only be cached if the special
+  // 'no_cache' property is not set to TRUE.
+  if (isset($original_form) && $form_state['cache'] && empty($form_state['no_cache'])) {
+    form_set_cache($form['#build_id'], $original_form, $form_state);
   }
-
-  // Don't override #theme if someone already set it.
-  if (!isset($form['#theme'])) {
-    drupal_theme_initialize();
-    $registry = theme_get_registry();
-    if (isset($registry[$form_id])) {
-      $form['#theme'] = $form_id;
-    }
-  }
-
   return $form;
 }
 
@@ -346,6 +310,7 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
     // We cache the form structure and the form state so it can be retrieved
     // later for validation.
     form_set_cache($form['#build_id'], $form, $form_state);
+    $form_state['no_cache'] = TRUE;
   }
 
   // Clear out all group associations as these might be different when
@@ -588,7 +553,7 @@ function drupal_retrieve_form($form_id, &$form_state) {
  * Processes a form submission.
  *
  * This function is the heart of form API. The form gets built, validated and in
- * appropriate cases, submitted.
+ * appropriate cases, submitted and rebuilt.
  *
  * @param $form_id
  *   The unique string identifying the current form.
@@ -677,6 +642,31 @@ function drupal_process_form($form_id, &$form, &$form_state) {
       // Redirect the form based on values in $form_state.
       drupal_redirect_form($form_state);
     }
+
+    // Don't rebuild or cache form submissions invoked via drupal_form_submit().
+    if (!empty($form_state['programmed'])) {
+      return;
+    }
+
+    // Most simple, single-step forms will be finished by this point --
+    // drupal_process_form() usually redirects to another page (or to
+    // a 'fresh' copy of the form) once processing is complete. If one
+    // of the form's handlers has set $form_state['redirect'] to FALSE,
+    // the form will simply be re-rendered with the values still in its
+    // fields.
+    //
+    // If $form_state['rebuild'] has been set and input has been processed, we
+    // know that we're in a multi-part process of some sort and the form's
+    // workflow is not complete. We need to construct a fresh copy of the form,
+    // passing in the latest $form_state in addition to any other variables
+    // passed into drupal_get_form().
+    if ($form_state['rebuild'] && !form_get_errors()) {
+      $form = drupal_rebuild_form($form_id, $form_state);
+    }
+    // After processing a cached form, only update the cached form state.
+    elseif ($form_state['cache'] && empty($form_state['no_cache'])) {
+      form_set_cache($form['#build_id'], NULL, $form_state);
+    }
   }
 }
 
@@ -761,6 +751,14 @@ function drupal_prepare_form($form_id, &$form, &$form_state) {
     }
   }
 
+  if (!isset($form['#theme'])) {
+    drupal_theme_initialize();
+    $registry = theme_get_registry();
+    if (isset($registry[$form_id])) {
+      $form['#theme'] = $form_id;
+    }
+  }
+
   // Invoke hook_form_FORM_ID_alter() implementations.
   drupal_alter('form_' . $form_id, $form, $form_state);
 
diff --git modules/file/file.module modules/file/file.module
index c8b1fd0..3b695e1 100644
--- modules/file/file.module
+++ modules/file/file.module
@@ -217,7 +217,7 @@ function file_ajax_upload() {
     return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
   }
 
-  list($form, $form_state, $form_id, $form_build_id) = ajax_get_form();
+  list($form, $form_state) = ajax_get_form();
 
   if (!$form) {
     // Invalid form_build_id.
@@ -234,13 +234,9 @@ function file_ajax_upload() {
   }
   $current_file_count = isset($current_element['#file_upload_delta']) ? $current_element['#file_upload_delta'] : 0;
 
-  // Build, validate and if possible, submit the form.
+  // Process user input and rebuild the updated form.
   drupal_process_form($form_id, $form, $form_state);
 
-  // This call recreates the form relying solely on the form_state that the
-  // drupal_process_form() set up.
-  $form = drupal_rebuild_form($form_id, $form_state, $form);
-
   // Retrieve the element to be rendered.
   foreach ($form_parents as $parent) {
     $form = $form[$parent];
