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	8 Apr 2010 20:26:21 -0000
@@ -193,11 +193,24 @@
  *   functions.
  */
 function ajax_render($commands = array()) {
+  // Update the page state and determine the new CSS and JavaScript that needs
+  // to be loaded. If there is no prior page state, something is wrong.
+  drupal_static_reset('drupal_page_state_id');
+  if ($prior_page_state = drupal_get_page_state()) {
+    $new_page_state = $prior_page_state;
+    drupal_update_page_state($new_page_state);
+    $new_css = array_diff_key($new_page_state['css'], $prior_page_state['css']);
+    $new_js = array_diff_key($new_page_state['js'], $prior_page_state['js']);
+    unset($new_js['settings']);
+  }
+
   // Automatically extract any 'settings' added via drupal_add_js() and make
   // them the first command.
   $scripts = drupal_add_js(NULL, NULL);
   if (!empty($scripts['settings'])) {
     array_unshift($commands, ajax_command_settings(call_user_func_array('array_merge_recursive', $scripts['settings']['data'])));
+    $commands[0]['styles'] = isset($new_css) ? drupal_get_css($new_css) : '';
+    $commands[0]['scripts'] = isset($new_js) ? drupal_get_js('header', $new_js) : '';
   }
 
   // Allow modules to alter any AJAX response.
@@ -518,6 +531,7 @@ function ajax_process_form($element, &$f
 
     $form_state['cache'] = TRUE;
   }
+  drupal_page_state_enabled(TRUE);
   return $element;
 }
 
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1145
diff -u -p -r1.1145 common.inc
--- includes/common.inc	7 Apr 2010 17:30:43 -0000	1.1145
+++ includes/common.inc	8 Apr 2010 20:26:22 -0000
@@ -6647,3 +6647,79 @@ function drupal_get_updaters() {
   }
   return $updaters;
 }
+
+/**
+ *
+ */
+function drupal_page_state_id($new_id = NULL) {
+  $id = &drupal_static(__FUNCTION__);
+  if (isset($new_id)) {
+    $id = $new_id;
+  }
+  // Security care must always be taken when using raw $_POST data.
+  elseif (!isset($id) && isset($_POST['_page_state_id']) && is_string($_POST['_page_state_id']) && preg_match('/^[0-9a-f]{32}$/', $_POST['_page_state_id'])) {
+    $id = $_POST['_page_state_id'];
+  }
+  return $id;
+}
+
+/**
+ *
+ */
+function drupal_get_page_state($id = NULL) {
+  if (!isset($id)) {
+    $id = drupal_page_state_id();
+  }
+  if ($id && $cached = cache_get('page_state_' . $id, 'cache_form')) {
+    $page_state = $cached->data;
+  }
+  else {
+    $page_state = array();
+  }
+  return $page_state;
+}
+
+/**
+ *
+ */
+function drupal_page_state_enabled($enable = NULL) {
+  $enabled = &drupal_static(__FUNCTION__);
+  if (isset($enable)) {
+    $enabled = $enable;
+  }
+  elseif (!isset($enabled)) {
+    $enabled = (bool) drupal_page_state_id();
+  }
+  return $enabled;
+}
+
+/**
+ *
+ */
+function drupal_update_page_state(&$page_state = NULL) {
+  if (!isset($page_state)) {
+    $page_state = drupal_get_page_state();
+  }
+  foreach (module_implements('page_state_update') as $module) {
+    $function = $module . '_page_state_update';
+    $function($page_state);
+  }
+
+  // There can be many users viewing pages that need their page state persisted.
+  // We don't want each one having a randomly generated id, as that would flood
+  // the cache bin with many records. By basing the id on the page state
+  // content, we only need as many records as there are unique states.
+  $page_state_id = md5(serialize($page_state));
+
+  // $page_state serves a similar purpose to $form_state. Therefore, persist it
+  // in the same cache bin with the same 6 hour life time.
+  cache_set('page_state_' . $page_state_id, $page_state, 'cache_form', REQUEST_TIME + 21600);
+
+  // The browser will need to know the new page state id.
+  drupal_add_js(array('pageStateId' => $page_state_id), 'setting');
+
+  // Future calls to drupal_page_state_id() during this page request should
+  // retrieve the new id.
+  drupal_page_state_id($page_state_id);
+  return $page_state_id;
+}
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.588
diff -u -p -r1.588 theme.inc
--- includes/theme.inc	6 Apr 2010 17:56:40 -0000	1.588
+++ includes/theme.inc	8 Apr 2010 20:26:23 -0000
@@ -2390,6 +2390,13 @@ function template_process_html(&$variabl
   $variables['head']    = drupal_get_html_head();
   $variables['css']     = drupal_add_css();
   $variables['styles']  = drupal_get_css();
+
+  // If necessary, persist page state information to enable AJAX functionality.
+  // This updates a JavaScript variable, so do this before calling
+  // drupal_get_js().
+  if (drupal_page_state_enabled()) {
+    drupal_update_page_state();
+  }
   $variables['scripts'] = drupal_get_js();
 }
 
Index: misc/ajax.js
===================================================================
RCS file: /cvs/drupal/drupal/misc/ajax.js,v
retrieving revision 1.15
diff -u -p -r1.15 ajax.js
--- misc/ajax.js	7 Apr 2010 17:30:43 -0000	1.15
+++ misc/ajax.js	8 Apr 2010 20:26:24 -0000
@@ -200,6 +200,9 @@ Drupal.ajax.prototype.beforeSubmit = fun
   // Disable the element that received the change.
   $(this.element).addClass('progress-disabled').attr('disabled', true);
 
+  // Give the server information about the page state.
+  form_values.push({ name: '_page_state_id', value: Drupal.settings.pageStateId });
+
   // Prevent duplicate HTML ids in the returned markup.
   // @see drupal_html_id()
   $('[id]').each(function () {
@@ -408,6 +411,12 @@ Drupal.ajax.prototype.commands = {
    * Command to set the settings that will be used for other commands in this response.
    */
   settings: function (ajax, response, status) {
+    if (response.styles) {
+      $('head').append(response.styles);
+    }
+    if (response.scripts) {
+      $('head').append(response.scripts);
+    }
     if (response.merge) {
       $.extend(true, Drupal.settings, response.settings);
     }
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.918
diff -u -p -r1.918 system.module
--- modules/system/system.module	7 Apr 2010 16:35:03 -0000	1.918
+++ modules/system/system.module	8 Apr 2010 20:26:29 -0000
@@ -3667,3 +3667,15 @@ function system_admin_paths() {
   );
   return $paths;
 }
+
+/**
+ * Implements hook_page_state_update().
+ */
+function system_page_state_update(&$page_state) {
+  $page_state += array(
+    'css' => array(),
+    'js' => array(),
+  );
+  $page_state['css'] = array_merge($page_state['css'], drupal_add_css());
+  $page_state['js'] = array_merge($page_state['js'], drupal_add_js());
+}
