Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1279
diff -u -p -r1.1279 common.inc
--- includes/common.inc	21 Dec 2010 19:16:31 -0000	1.1279
+++ includes/common.inc	23 Dec 2010 16:27:30 -0000
@@ -5600,17 +5600,24 @@ function drupal_render(&$elements) {
 
   // Get the children of the element, sorted by weight.
   $children = element_children($elements, TRUE);
+  // Prepare the element for caching by collecting
+  // #attached from all child elements.
+  if (isset($elements['#cache']) || isset($elements['#collect_attached'])) {
+    drupal_render_collect_attached($elements);
+  }
 
   // Initialize this element's #children, unless a #pre_render callback already
   // preset #children.
   if (!isset($elements['#children'])) {
     $elements['#children'] = '';
   }
+  
   // Call the element's #theme function if it is set. Then any children of the
   // element have to be rendered there.
   if (isset($elements['#theme'])) {
     $elements['#children'] = theme($elements['#theme'], $elements);
   }
+
   // If #theme was not set and the element has children, render them now.
   // This is the same process as drupal_render_children() but is inlined
   // for speed.
@@ -5794,9 +5801,21 @@ function drupal_render_cache_set(&$marku
   // ESI command is executed by the content accelerator, the real value can
   // be retrieved and used.
   $data['#markup'] = &$markup;
+
   // Persist attached data associated with this element.
-  if (isset($elements['#attached'])) {
-    $data['#attached'] = $elements['#attached'];
+  $attached = &drupal_static('drupal_render_collect_attached', array());
+
+  // We need to store attached data for this element, and any child elements,
+  // even if those child elements also use drupal_render() caching, in order
+  // that these can be added to the page when the item is pulled from cache. So
+  // get the full array of attached data that has been collected during this
+  // request, then take everything from $element['#attached_index'] onwards.
+  if ($attached_data = array_slice($attached, $elements['#attached_index'])) {
+    $store = array();
+    foreach ($attached_data as $attached) {
+      $store = array_merge($store, $attached);
+    }
+    $data['#attached'] = $store;
   }
   $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache';
   $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : CACHE_PERMANENT;
@@ -5804,6 +5823,53 @@ function drupal_render_cache_set(&$marku
 }
 
 /**
+ * Collect #attached for an element and all child elements into a single array.
+ *
+ * When caching elements, it is necessary to collect all libraries, javascript
+ * and CSS into a single array, from both the element itself and all child
+ * elements. This allows drupal_render() to add these back to the page when the
+ * element is returned from cache.
+ */
+function drupal_render_collect_attached(&$element) {
+  $attached = &drupal_static(__FUNCTION__, array());
+
+  // Since drupal_render() is recursive, and any number of child elements may
+  // use caching and/or #attached, it is necessary to keep track of #attached for
+  // all elements that set #cache, and to collect #attached for all of their
+  // descendents. When an element specifies #cache, keep track of its index
+  // in the attached array, to allow only #attached from the element and its
+  // children to be cached later on.
+  if (isset($element['#cache'])) {
+    $element['#attached_index'] = count($attached);
+    $attached[$element['#attached_index']] = array();
+  }
+   
+  // Collect all #attached for this element.
+  if (isset($element['#attached'])) {
+    $element['#attached_collected'] = TRUE;
+    if (isset($element['#attached_index'])) {
+      $attached[$element['#attached_index']] = array();
+    }
+    foreach (array('library', 'css', 'js') as $key) {
+      if (isset($element['#attached'][$key])) {
+        foreach ($attached as $parent => $value) {
+          if (!isset($attached[$parent][$key])) {
+            $attached[$parent][$key] = array();
+          }
+          $attached[$parent][$key] = array_merge($attached[$parent][$key], $element['#attached'][$key]);
+        }
+      }
+    }
+  }
+  foreach ($element as $key => $value) {
+    if (is_array($value)) {
+      $element[$key]['#collect_attached'] = TRUE;
+      drupal_render_collect_attached($value);
+    }
+  }
+}
+
+/**
  * Prepare an element for caching based on a query. This smart caching strategy
  * saves Drupal from querying and rendering to HTML when the underlying query is
  * unchanged.
