Index: includes/ajax.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/ajax.inc,v
retrieving revision 1.40
diff -u -p -r1.40 ajax.inc
--- includes/ajax.inc	17 Dec 2010 01:03:58 -0000	1.40
+++ includes/ajax.inc	20 Dec 2010 02:39:02 -0000
@@ -407,19 +407,52 @@ function ajax_base_page_theme() {
 /**
  * Package and send the result of a page callback to the browser as an AJAX response.
  *
+ * This function is the equivalent of drupal_deliver_html_page(), but for AJAX
+ * requests. Like that function, it:
+ * - Adds needed HTTP headers.
+ * - Prints rendered output.
+ * - Performs end-of-request tasks.
+ *
  * @param $page_callback_result
  *   The result of a page callback. Can be one of:
  *   - NULL: to indicate no content.
  *   - An integer menu status constant: to indicate an error condition.
  *   - A string of HTML content.
  *   - A renderable array of content.
+ *
+ * @see drupal_deliver_html_page()
  */
 function ajax_deliver($page_callback_result) {
-  $commands = array();
-  $header = TRUE;
+  // Emit an appropriate default Content-Type HTTP header if one hasn't already
+  // been added (for example, by the page callback, or by an alternate delivery
+  // callback that emits custom headers, and then calls this function).
+  $header = drupal_get_http_header('Content-Type');
+  if (!isset($header)) {
+    // The standard media type for a JSON string is application/json
+    // (http://www.ietf.org/rfc/rfc4627.txt?number=4627). However, because
+    // browsers restrict JavaScript from reading the contents of a user's local
+    // files, jQuery submits to an IFRAME rather than using XHR for forms that
+    // contain file input elements (http://malsup.com/jquery/form/#file-upload).
+    // When Internet Explorer receives application/json content in an IFRAME, it
+    // treats it as a file download, prompting the user to save it, rather than
+    // silently rendering it in the window and allowing jQuery to read it. To
+    // get around this limitation, we return the content as text/plain. We only
+    // do this for POST requests, because there is no reason for jQuery to not
+    // use XHR for a GET request, and we don't want the incorrect mime type
+    // ending up in the page cache or proxy server caches.
+    // @see http://drupal.org/node/995854
+    $iframe_upload = !isset($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest';
+    if (1 || $iframe_upload && $_SERVER['REQUEST_METHOD'] == 'POST') {
+      drupal_add_http_header('Content-Type', 'text/javascript; charset=utf-8');
+    }
+    else {
+      drupal_add_http_header('Content-Type', 'application/json; charset=utf-8');
+    }
+  }
 
   // Normalize whatever was returned by the page callback to an AJAX commands
   // array.
+  $commands = array();
   if (!isset($page_callback_result)) {
     // Simply delivering an empty commands array is sufficient. This results
     // in the AJAX request being completed, but nothing being done to the page.
@@ -444,7 +477,6 @@ function ajax_deliver($page_callback_res
     // Complex AJAX callbacks can return a result that contains an error message
     // or a specific set of commands to send to the browser.
     $page_callback_result += element_info('ajax');
-    $header = $page_callback_result['#header'];
     $error = $page_callback_result['#error'];
     if (isset($error) && $error !== FALSE) {
       if ((empty($error) || $error === TRUE)) {
@@ -470,23 +502,11 @@ function ajax_deliver($page_callback_res
     $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
   }
 
-  // This function needs to do the same thing that drupal_deliver_html_page()
-  // does: add any needed http headers, print rendered output, and perform
-  // end-of-request tasks. By default, $header=TRUE, and we add a
-  // 'text/javascript' header. The page callback can override $header by
-  // returning an 'ajax' element with a #header property. This can be set to
-  // FALSE to prevent the 'text/javascript' header from being output, necessary
-  // when outputting to an IFRAME. This can also be set to 'multipart', in which
-  // case, we don't output JSON, but JSON content wrapped in a textarea, making
-  // a 'text/javascript' header incorrect.
-  if ($header && $header !== 'multipart') {
-    drupal_add_http_header('Content-Type', 'text/javascript; charset=utf-8');
-  }
+  // Unlike the recommendation in http://malsup.com/jquery/form/#file-upload, we
+  // do not have to wrap the JSON string in a TEXTAREA, because
+  // drupal_json_encode() returns an HTML-safe JSON string.
   $output = ajax_render($commands);
-  if ($header === 'multipart') {
-    // jQuery file uploads: http://malsup.com/jquery/form/#code-samples
-    $output = '<textarea>' . $output . '</textarea>';
-  }
+  dsm($output);
   print $output;
   ajax_footer();
 }
Index: misc/ajax.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/ajax.js,v
retrieving revision 1.33
diff -u -p -r1.33 ajax.js
--- misc/ajax.js	17 Dec 2010 01:03:58 -0000	1.33
+++ misc/ajax.js	20 Dec 2010 02:34:51 -0000
@@ -149,11 +149,9 @@ Drupal.ajax = function (base, element, e
       return ajax.beforeSerialize(element_settings, options);
     },
     beforeSubmit: function (form_values, element_settings, options) {
-      ajax.ajaxing = true;
       return ajax.beforeSubmit(form_values, element_settings, options);
     },
     beforeSend: function (xmlhttprequest) {
-      ajax.ajaxing = true;
       return ajax.beforeSend(xmlhttprequest, ajax.options);
     },
     success: function (response, status) {
@@ -165,10 +163,15 @@ Drupal.ajax = function (base, element, e
       return ajax.success(response, status);
     },
     complete: function (response, status) {
+      $.debug(response, 'response');
+      $.debug(status, 'status');
+      $.debug(ajax, 'ajax');
       ajax.ajaxing = false;
+//      window.stop();
       if (status == 'error' || status == 'parsererror') {
         return ajax.error(response, ajax.url);
       }
+      return false;
     },
     dataType: 'json',
     type: 'POST'
@@ -218,11 +221,13 @@ Drupal.ajax.prototype.keypressResponse =
 Drupal.ajax.prototype.eventResponse = function (element, event) {
   // Create a synonym for this to reduce code confusion.
   var ajax = this;
+  $.debug(ajax);
 
   // Do not perform another ajax command if one is already in progress.
   if (ajax.ajaxing) {
     return false;
   }
+  ajax.ajaxing = true;
 
   try {
     if (ajax.form) {
@@ -258,7 +263,6 @@ Drupal.ajax.prototype.eventResponse = fu
   else {
     return false;
   }
-
 };
 
 /**
@@ -312,8 +316,9 @@ Drupal.ajax.prototype.beforeSubmit = fun
  * Prepare the AJAX request before it is sent.
  */
 Drupal.ajax.prototype.beforeSend = function (xmlhttprequest, options) {
-  // Disable the element that received the change.
-  $(this.element).addClass('progress-disabled').attr('disabled', true);
+  // Mark the element that received the change as disabled. We cannot actually
+  // disable it or it will not appear in the form submission anymore.
+  $(this.element).addClass('progress-disabled');
 
   // Insert progressbar or throbber.
   if (this.progress.type == 'bar') {
@@ -348,7 +353,7 @@ Drupal.ajax.prototype.success = function
   if (this.progress.object) {
     this.progress.object.stopMonitoring();
   }
-  $(this.element).removeClass('progress-disabled').removeAttr('disabled');
+  $(this.element).removeClass('progress-disabled');
 
   Drupal.freezeHeight();
 
@@ -416,12 +421,13 @@ Drupal.ajax.prototype.error = function (
   // Undo hide.
   $(this.wrapper).show();
   // Re-enable the element.
-  $(this.element).removeClass('progress-disabled').removeAttr('disabled');
+  $(this.element).removeClass('progress-disabled');
   // Reattach behaviors, if they were detached in beforeSerialize().
-  if (this.form) {
+  if (this.form && typeof Drupal != 'undefined') {
     var settings = response.settings || this.settings || Drupal.settings;
     Drupal.attachBehaviors(this.form, settings);
   }
+  return false;
 };
 
 /**
Index: misc/drupal.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.js,v
retrieving revision 1.71
diff -u -p -r1.71 drupal.js
--- misc/drupal.js	22 Nov 2010 04:33:02 -0000	1.71
+++ misc/drupal.js	20 Dec 2010 02:33:37 -0000
@@ -308,13 +308,14 @@ Drupal.ajaxError = function (xmlhttp, ur
   var statusCode, statusText, pathText, responseText, readyStateText, message;
   if (xmlhttp.status) {
     statusCode = "\n" + Drupal.t("An AJAX HTTP error occurred.") +  "\n" + Drupal.t("HTTP Result Code: !status", {'!status': xmlhttp.status});
+    statusText = xmlhttp.statusText ? ("\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)})) : "";
   }
   else {
     statusCode = "\n" + Drupal.t("An AJAX HTTP request terminated abnormally.");
+    statusText = '';
   }
   statusCode += "\n" + Drupal.t("Debugging information follows.");
   pathText = "\n" + Drupal.t("Path: !uri", {'!uri': uri} );
-  statusText = xmlhttp.statusText ? ("\n" + Drupal.t("StatusText: !statusText", {'!statusText': $.trim(xmlhttp.statusText)})) : "";
   responseText = xmlhttp.responseText ? ("\n" + Drupal.t("ResponseText: !responseText", {'!responseText': $.trim(xmlhttp.responseText)})) : "";
   // Make the responseText more readable by stripping HTML tags and newlines.
   responseText = responseText.replace(/<("[^"]*"|'[^']*'|[^'">])*>/gi,"");
Index: modules/file/file.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/file/file.module,v
retrieving revision 1.48
diff -u -p -r1.48 file.module
--- modules/file/file.module	11 Dec 2010 01:32:20 -0000	1.48
+++ modules/file/file.module	20 Dec 2010 01:01:39 -0000
@@ -237,7 +237,7 @@ function file_ajax_upload() {
     drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
     $commands = array();
     $commands[] = ajax_command_replace(NULL, theme('status_messages'));
-    return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
+    return array('#type' => 'ajax', '#commands' => $commands);
   }
 
   list($form, $form_state) = ajax_get_form();
@@ -247,7 +247,7 @@ function file_ajax_upload() {
     drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
     $commands = array();
     $commands[] = ajax_command_replace(NULL, theme('status_messages'));
-    return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
+    return array('#type' => 'ajax', '#commands' => $commands);
   }
 
   // Get the current element and count the number of files.
@@ -280,7 +280,7 @@ function file_ajax_upload() {
 
   $commands = array();
   $commands[] = ajax_command_replace(NULL, $output, $settings);
-  return array('#type' => 'ajax', '#commands' => $commands, '#header' => FALSE);
+  return array('#type' => 'ajax', '#commands' => $commands);
 }
 
 /**
