Index: includes/ajax.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/ajax.inc,v
retrieving revision 1.29
diff -u -p -r1.29 ajax.inc
--- includes/ajax.inc	31 Mar 2010 19:34:56 -0000	1.29
+++ includes/ajax.inc	2 Apr 2010 18:13:50 -0000
@@ -248,6 +248,48 @@ function ajax_get_form() {
 }
 
 /**
+ * Process, and if necessary, rebuild, a form submitted via #ajax during an AJAX callback.
+ *
+ * For non-AJAX submissions, the typical form building and processing pipeline
+ * is managed by drupal_build_form(). A menu callback handling an AJAX
+ * submission should instead call ajax_get_form() to retrieve the form and
+ * ajax_process_form_submission() to process it. These two functions in sequence
+ * closely match what is done by drupal_build_form(), but with some differences
+ * to handle the special needs of AJAX.
+ *
+ * See ajax_form_callback(), the menu callback for the generic 'system/ajax'
+ * path, for the canonical example of an AJAX menu callback.
+ *
+ * @see ajax_get_form()
+ * @see drupal_build_form()
+ * @see ajax_form_callback()
+ *
+ * @todo Rename this function to ajax_process_form() and the ajax_process_form()
+ *   function to ajax_process() or ajax_element_process() in Drupal 8.
+ */
+function ajax_process_form_submission(&$form, &$form_state) {
+  // Build, validate and if possible, submit the form.
+  drupal_process_form($form['#form_id'], $form, $form_state);
+
+  // The workflow for most AJAX-enabled forms is for something about the form to
+  // change as a result of a successful submission. In this way, AJAX-enabled
+  // forms are multi-step forms, and their submit handlers should set
+  // $form_state['rebuild'] as is the case for non-AJAX multi-step forms. This
+  // also enables an AJAX-enabled form to degrade gracefully to a non-AJAX
+  // multi-step form when JavaScript is disabled.
+  if ($form_state['rebuild'] && $form_state['process_input'] && !form_get_errors()) {
+    // drupal_rebuild_form() also takes care of updating the form cache.
+    $form = drupal_rebuild_form($form['#form_id'], $form_state, $form);
+  }
+  // For forms that do not require a rebuild or when there is a validation
+  // error, do not rebuild, but make sure to persist the current $form_state to
+  // the next request.
+  elseif ($form_state['cache'] && empty($form_state['no_cache'])) {
+    form_set_cache($form['#build_id'], NULL, $form_state);
+  }
+}
+
+/**
  * Menu callback; handles AJAX requests for the #ajax Form API property.
  *
  * This rebuilds the form from cache and invokes the defined #ajax['callback']
@@ -263,18 +305,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);
+  list($form, $form_state) = ajax_get_form();
+  ajax_process_form_submission($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.
+  // 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'];
   }
Index: modules/file/file.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.module,v
retrieving revision 1.24
diff -u -p -r1.24 file.module
--- modules/file/file.module	31 Mar 2010 19:34:56 -0000	1.24
+++ modules/file/file.module	2 Apr 2010 18:13:52 -0000
@@ -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,12 +234,8 @@ 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.
-  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);
+  // Process user input and rebuild the updated form.
+  ajax_process_form_submission($form, $form_state);
 
   // Retrieve the element to be rendered.
   foreach ($form_parents as $parent) {
