#329015 by Damien Tournoud: the BatchAPI should be able to read fatal error and exception messages

From: Damien Tournoud <damien@tournoud.net>


---

 common.inc |   44 +++++++++++++++++++++++++-------------------
 drupal.js  |   14 +++++++-------
 2 files changed, 32 insertions(+), 26 deletions(-)

diff --git includes/common.inc includes/common.inc
index e380270..787fe8c 100644
--- includes/common.inc
+++ includes/common.inc
@@ -775,7 +775,7 @@ function _drupal_decode_exception($exception) {
  *   TRUE if the error is fatal.
  */
 function _drupal_log_error($error, $fatal = FALSE) {
-  // Initialize a maintenance theme early if the boostrap was not complete.
+  // Initialize a maintenance theme if the boostrap was not complete.
   // Do it early because drupal_set_message() triggers an init_theme().
   if ($fatal && (drupal_get_bootstrap_phase() != DRUPAL_BOOTSTRAP_FULL)) {
     unset($GLOBALS['theme']);
@@ -802,34 +802,40 @@ function _drupal_log_error($error, $fatal = FALSE) {
     $number++;
   }
 
-  // Force display of error messages in update.php or if the proper error
-  // reporting level is set.
-  $error_level = variable_get('error_level', 2);
-  if ($error_level == 2 || ($error_level == 1 && $error['%type'] != 'Notice') || (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update')) {
-    drupal_set_message(t('%type: %message in %function (line %line of %file).', $error), 'error');
-  }
-
   try {
     watchdog('php', '%type: %message in %function (line %line of %file).', $error, WATCHDOG_ERROR);
   }
   catch (Exception $e) {
-    $new_error = _drupal_decode_exception($e);
-    drupal_set_message(t('%type: %message in %function (line %line of %file).', $new_error), 'error');
+    // Ignore any additional watchdog exception, as that probably means
+    // that the database was not initialized correctly.
   }
 
   if ($fatal) {
-    drupal_set_header($_SERVER['SERVER_PROTOCOL'] . ' Service unavailable');
-    drupal_set_title(t('Error'));
-    if (!defined('MAINTENANCE_MODE') && drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
-      // To conserve CPU and bandwidth, omit the blocks.
-      $page = drupal_get_page(t('The website encountered an unexpected error. Please try again later.'));
-      $page['#show_blocks'] = FALSE;
-      print drupal_render_page($page);
+    drupal_set_header($_SERVER['SERVER_PROTOCOL'] . ' 500 Service unavailable (with message)');
+  }
+
+  if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
+    if ($fatal) {
+      // When called from javascript, simply output the error message.
+      print t('%type: %message in %function (line %line of %file).', $error);
+      exit;
     }
-    else {
+  }
+  else {
+    // Force display of error messages in update.php or if the proper error
+    // reporting level is set.
+    $error_level = variable_get('error_level', 2);
+    if ($error_level == 2 || ($error_level == 1 && $error['%type'] != 'Notice') || (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update')) {
+      drupal_set_message(t('%type: %message in %function (line %line of %file).', $error), 'error');
+    }
+
+    if ($fatal) {
+      drupal_set_title(t('Error'));
+      // We fallback to a maintenance page at this point, because the page generation
+      // itself can generate errors.
       print theme('maintenance_page', t('The website encountered an unexpected error. Please try again later.'), FALSE);
+      exit;
     }
-    exit;
   }
 }
 
diff --git misc/drupal.js misc/drupal.js
index 817bb4e..efed55a 100644
--- misc/drupal.js
+++ misc/drupal.js
@@ -298,19 +298,19 @@ Drupal.getSelection = function (element) {
  * Build an error message from ahah response.
  */
 Drupal.ahahError = function(xmlhttp, uri) {
-  if (xmlhttp.status == 200) {
-    if ($.trim(xmlhttp.responseText)) {
-      var message = Drupal.t("An error occurred. \n@uri\n@text", {'@uri': uri, '@text': xmlhttp.responseText });
+  if (xmlhttp.status == 200 || (xmlhttp.status == 500 && xmlhttp.statusText == 'Service unavailable (with message)')) {
+    if (jQuery.trim(xmlhttp.responseText)) {
+      var message = Drupal.t("An error occurred. \nPath: @uri\nMessage: !text", {'@uri': uri, '!text': xmlhttp.responseText });
     }
     else {
-      var message = Drupal.t("An error occurred. \n@uri\n(no information available).", {'@uri': uri });
+      var message = Drupal.t("An error occurred. \nPath: @uri\n(no information available).", {'@uri': uri });
     }
   }
   else {
-    var message = Drupal.t("An HTTP error @status occurred. \n@uri", {'@uri': uri, '@status': xmlhttp.status });
+    var message = Drupal.t("An HTTP error @status occurred. \nPath: @uri", {'@uri': uri, '@status': xmlhttp.status });
   }
-  return message.replace(/\n/g, '<br />');
-};
+  return message.replace(/\n/g, '<br />');;
+}
 
 // Global Killswitch on the <html> element.
 if (Drupal.jsEnabled) {
