Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.1115
diff -u -p -r1.1115 common.inc
--- includes/common.inc	22 Feb 2010 19:21:34 -0000	1.1115
+++ includes/common.inc	24 Feb 2010 19:41:55 -0000
@@ -2651,6 +2651,17 @@ function drupal_add_html_head_link($attr
  *   - 'preprocess': If TRUE, Allows the CSS to be aggregated and compressed if
  *     the Optimize CSS feature has been turned on under the performance
  *     section.  Defaults to TRUE.
+ *   - 'conditional_comment_expression': An expression to control which versions
+ *     of IE should load the CSS. For example, setting this to 'lt IE 7' makes
+ *     the CSS load in IE versions less than 7 only; this is the standard way
+ *     of adding an extra CSS file that compensates for the poor standards
+ *     support in IE6. See http://en.wikipedia.org/wiki/Conditional_comment.
+ *   - 'conditional_comment_downlevel_revealed': If TRUE, non-IE browsers will
+ *     load the CSS regardless of the conditional comment expression. Otherwise,
+ *     if there's a conditional comment expression, regardless what it is,
+ *     non-IE browsers will not load the CSS. For example, to add a CSS file for
+ *     IE8+ and non-IE browsers, set 'conditional_comment_expression' to
+ *     'gte IE 8' and 'conditional_comment_downlevel_revealed' to TRUE.
  *
  * @return
  *   An array of queued cascading stylesheets.
@@ -2722,25 +2733,22 @@ function drupal_add_css($data = NULL, $o
  *   A string of XHTML CSS tags.
  */
 function drupal_get_css($css = NULL) {
-  $output = '';
   if (!isset($css)) {
     $css = drupal_add_css();
   }
 
-  $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
-  $directory = file_directory_path('public');
-  $is_writable = is_dir($directory) && is_writable($directory);
-
-  // A dummy query-string is added to filenames, to gain control over
-  // browser-caching. The string changes on every update or full cache
-  // flush, forcing browsers to load a new copy of the files, as the
-  // URL changed.
-  $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1);
-
-  // Allow modules to alter the css items.
-  drupal_alter('css', $css);
+  // Allow modules to alter the CSS items. Also allow them to override the
+  // default callbacks that will be used to group, aggregate, and render the
+  // CSS.
+  $callbacks = array();
+  drupal_alter('css', $css, $callbacks);
+  $callbacks += array(
+    'group' => '_drupal_css_group',
+    'aggregate' => '_drupal_css_aggregate',
+    'render' => '_drupal_css_render',
+  );
 
-  // Sort css items according to their weights.
+  // Sort CSS items according to their weights.
   uasort($css, 'drupal_sort_weight');
 
   // Remove the overridden CSS files. Later CSS files override former ones.
@@ -2756,75 +2764,369 @@ function drupal_get_css($css = NULL) {
     }
   }
 
-  // If CSS preprocessing is off, we still need to output the styles.
-  // Additionally, go through any remaining styles if CSS preprocessing is on
-  // and output the non-cached ones.
-  $css_element = array(
+  // Merge markup needed for conditional comments into generic 'html_prefix' and
+  // 'html_suffix' keys. These keys are not documented in drupal_add_css(),
+  // because there's no identified use-case for them independent of conditional
+  // comments, but if these are passed in when drupal_add_css() is called or set
+  // by hook_css_alter(), don't override them, but wrap them with the
+  // conditional comment markup.
+  foreach ($css as $key => $item) {
+    $item += array(
+      'html_prefix' => '',
+      'html_suffix' => '',
+    );
+    if (!empty($item['conditional_comment_expression'])) {
+      $expression = $item['conditional_comment_expression'];
+      if (empty($item['conditional_comment_downlevel_revealed'])) {
+        $item['html_prefix'] = "\n<!--[if $expression]>\n" . $item['html_prefix'];
+        $item['html_suffix'] .= "<![endif]-->\n";
+      }
+      else {
+        // http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
+        $item['html_prefix'] = "\n<!--[if $expression]><!-->\n" . $item['html_prefix'];
+        $item['html_suffix'] .= "<!--<![endif]-->\n";
+      }
+    }
+    $css[$key] = $item;
+  }
+
+  // Group the items, aggregate the groups, and render the tags.
+  $css_groups = $callbacks['group']($css);
+  $callbacks['aggregate']($css_groups);
+  $output = $callbacks['render']($css_groups);
+  if (is_array($output)) {
+    $output = drupal_render($output);
+  }
+  return $output;
+}
+
+/**
+ * Default callback to group CSS items.
+ *
+ * Once drupal_get_css() has an array of CSS items, the items then need to be
+ * grouped. Grouping allows files to be aggregated into fewer files which
+ * significantly reduces network traffic and improves page loading speed.
+ * When aggregation is disabled, grouping allows multiple files to be loaded
+ * from a single STYLE tag, enabling us to stay within IE's limit of 31 CSS
+ * inclusion tags: http://drupal.org/node/228818.
+ *
+ * A hook_css_alter() implementation may override this function with a custom
+ * 'group' callback. For example, a module may want module CSS files and theme
+ * CSS files in different groups.
+ *
+ * @param $css
+ *   An array of CSS items, as returned by drupal_add_css(), but after
+ *   alteration performed by drupal_get_css().
+ *
+ * @return
+ *   An array of CSS groups. Each group contains the same properties as a CSS
+ *   item, where the property value applies to the group as a whole. Each group
+ *   also contains an 'items' property which is an array of CSS items, like
+ *   $css, but only with the items in the group.
+ *
+ * @see drupal_get_css()
+ * @see _drupal_css_aggregate()
+ * @see hook_css_alter()
+ */
+function _drupal_css_group($css) {
+  $groups = array();
+  // If a group can contain multiple items, we track the information that must
+  // be the same for each item in the group, so that when we iterate the next
+  // item, we can determine if it can be put into the current group, or if a
+  // new group needs to be made for it.
+  $current_group_keys = NULL;
+  // When creating a new group, we pre-increment $i, so by initializing it to
+  // -1, the first group will have index 0.
+  $i = -1;
+  foreach ($css as $item) {
+    // If the item can be grouped with other items, set $group_keys to an array
+    // of information that must be the same for all items in its group. If the
+    // item can't be grouped with other items, set $group_keys to FALSE. We
+    // put items into a group that can be aggregated together: whether they will
+    // be aggregated is up to the _drupal_css_aggregate() function or an
+    // override of that function specified in hook_css_alter(), but regardless
+    // of the details of that function, a group represents items that can be
+    // aggregated. Since a group may be rendered with a single HTML tag, all
+    // items in the group must share the same information that would need to be
+    // part of that HTML tag.
+    switch ($item['type']) {
+      case 'file':
+        // Group file items if their 'preprocess' flag is TRUE.
+        $group_keys = $item['preprocess'] ? array($item['type'], $item['media'], $item['html_prefix'], $item['html_suffix']) : FALSE;
+        break;
+      case 'inline':
+        // Always group inline items.
+        $group_keys = array($item['type'], $item['media'], $item['html_prefix'], $item['html_suffix']);
+        break;
+      case 'external':
+        // Do not group external items.
+        $group_keys = FALSE;
+        break;
+    }
+    // If the group keys don't match the most recent group we're working with,
+    // then a new group must be made.
+    if ($group_keys !== $current_group_keys) {
+      $i++;
+      // Initialize the new group with the same properties as the first item
+      // being placed into it. The item's 'data' and 'weight' properties are
+      // unique to the item and should not be carried over to the group.
+      $groups[$i] = $item;
+      unset($groups[$i]['data'], $groups[$i]['weight']);
+      $groups[$i]['items'] = array();
+      $current_group_keys = $group_keys ? $group_keys : NULL;
+    }
+    // Add the item to the current group.
+    $groups[$i]['items'][] = $item;
+  }
+  return $groups;
+}
+
+/**
+ * Default callback to aggregate CSS files and inline content.
+ *
+ * Having the browser load fewer CSS files results in much faster page loads
+ * than when it loads many small files. This function aggregates files within
+ * the same group into a single file unless the site-wide setting to do so is
+ * disabled (commonly the case during site development). It also aggregates
+ * inline content together. This function also compresses the aggregate files
+ * (removes comments, whitespace, and other unnecessary content) for faster
+ * download.
+ *
+ * A module wanting to implement custom aggregation functionality can register
+ * an alternate 'aggregate' callback in hook_css_alter().
+ *
+ * @param $css_groups
+ *   An array of CSS groups as returned by _drupal_css_group() or a function
+ *   that overrides it. This function modifies the group's 'data' property for
+ *   each group that is aggregated.
+ *
+ * @see _drupal_css_group()
+ * @see hook_css_alter()
+ */
+function _drupal_css_aggregate(&$css_groups) {
+  $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
+  $directory = file_directory_path('public');
+  $is_writable = is_dir($directory) && is_writable($directory);
+
+  // For non-aggregated files, a dummy query string is added to the URL (@see
+  // _drupal_css_render()). For aggregated files, it is instead added to the
+  // data from which an md5 hash is generated, so that a changed query string
+  // triggers a new aggregate file to be created.
+  $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1);
+
+  // For each group that needs aggregation, aggregate its items.
+  foreach ($css_groups as $key => $group) {
+    switch ($group['type']) {
+      // If a file group can be aggregated into a single file, do so, and set
+      // the group's data property to the file path of the aggregate file.
+      case 'file':
+        if ($group['preprocess'] && $preprocess_css && $is_writable) {
+          $filename = 'css_' . md5(serialize($group['items']) . $query_string) . '.css';
+          $css_groups[$key]['data'] = drupal_build_css_cache($group['items'], $filename);
+        }
+        break;
+      // Aggregate all inline CSS content into the group's data property.
+      case 'inline':
+        $css_groups[$key]['data'] = '';
+        foreach ($group['items'] as $item) {
+          $css_groups[$key]['data'] .= drupal_load_stylesheet_content($item['data'], $item['preprocess']);
+        }
+        break;
+    }
+  }
+}
+
+/**
+ * Default callback to render CSS tags.
+ *
+ * For production websites, LINK tags are preferable to STYLE tags with @import
+ * statements, because:
+ * - They are the standard tag intended for linking to a resource.
+ * - On Firefox 2 and perhaps other browsers, CSS files included with @import
+ *   statements don't get saved when saving the complete web page for offline
+ *   use: http://drupal.org/node/145218.
+ * - On IE, if only LINK tags and no @import statements are used, all the CSS
+ *   files are downloaded in parallel, resulting in faster page load, but if
+ *   @import statements are used and span across multiple STYLE tags, all the
+ *   ones from one STYLE tag must be downloaded before downloading begins for
+ *   the next STYLE tag. Furthermore, IE7 does not support media declaration on
+ *   the @import statement, so multiple STYLE tags must be used when different
+ *   files are for different media types. Non-IE browsers always download in
+ *   parallel, so this is an IE-specific performance quirk:
+ *   http://www.stevesouders.com/blog/2009/04/09/dont-use-import/.
+ *
+ * However, IE has an annoying limit of 31 total CSS inclusion tags
+ * (http://drupal.org/node/228818) and LINK tags are limited to one file per
+ * tag, whereas STYLE tags can contain multiple @import statements allowing
+ * multiple files to be loaded per tag. When CSS aggregation is disabled, a
+ * Drupal site can easily have more than 31 CSS files that need to be loaded, so
+ * using LINK tags exclusively would result in a site that would display
+ * incorrectly in IE. Depending on different needs, different strategies can be
+ * employed to decide when to use LINK tags and when to use STYLE tags.
+ *
+ * The strategy employed by this function is to use LINK tags for all aggregate
+ * files and for all files that cannot be aggregated (e.g., if 'preprocess' is
+ * set to FALSE or the type is 'external'), and to use STYLE tags for groups
+ * of files that could be aggregated together but aren't (e.g., if the site-wide
+ * aggregation setting is disabled). This results in all LINK tags when
+ * aggregation is enabled, a guarantee that as many or only slightly more tags
+ * are used with aggregation disabled than enabled (so that if the limit were to
+ * be crossed with aggregation enabled, the site developer would also notice the
+ * problem while aggregation is disabled), and an easy way for a developer to
+ * view HTML source while aggregation is disabled and know what files will be
+ * aggregated together when aggregation becomes enabled.
+ *
+ * This function evaluates the aggregation enabled/disabled condition on a group
+ * by group basis by testing whether an aggregate file has been made for the
+ * group rather than by testing the site-wide aggregation setting. This allows
+ * this function to work correctly even if modules have implemented custom
+ * logic for grouping and aggregating files.
+ *
+ * A module can specify a custom 'render' callback within a hook_css_alter()
+ * implementation in order to override this default implementation.
+ *
+ * @param $css_groups
+ *   An array of css groups as returned by _drupal_css_group() or a function
+ *   that overrides it.
+ * @return
+ *   A render array that will render to a string of XHTML CSS tags.
+ *
+ * @see _drupal_css_group()
+ * @see _drupal_css_aggregate()
+ * @see hook_css_alter()
+ */
+function _drupal_css_render($css_groups) {
+  $elements = array();
+
+  // A dummy query-string is added to filenames, to gain control over
+  // browser-caching. The string changes on every update or full cache
+  // flush, forcing browsers to load a new copy of the files, as the
+  // URL changed.
+  $query_string = '?' . substr(variable_get('css_js_query_string', '0'), 0, 1);
+
+  // Defaults for LINK and STYLE elements.
+  $link_element_defaults = array(
+    '#theme' => 'html_tag',
     '#tag' => 'link',
     '#attributes' => array(
       'type' => 'text/css',
       'rel' => 'stylesheet',
     ),
   );
-  $rendered_css = array();
-  $inline_css = '';
-  $external_css = '';
-  $preprocess_items = array();
-  foreach ($css as $data => $item) {
-    // Loop through each of the stylesheets, including them appropriately based
-    // on their type.
-    switch ($item['type']) {
+  $style_element_defaults = array(
+    '#theme' => 'html_tag',
+    '#tag' => 'style',
+    '#attributes' => array(
+      'type' => 'text/css',
+    ),
+  );
+
+  // Loop through each group.
+  foreach ($css_groups as $group) {
+    switch ($group['type']) {
+      // For file items, there are three possibilites.
+      // - The group has been aggregated: in this case, output a LINK tag for
+      //   the aggregate file.
+      // - The group can be aggregated but has not been (most likely because
+      //   the site administrator disabled the site-wide setting): in this case,
+      //   output as few STYLE tags for the group as possible, using @import
+      //   statement for each file in the group. This enables us to stay within
+      //   IE's limit of 31 total CSS inclusion tags.
+      // - The group contains items not eligible for aggregation (their
+      //   'preprocess' flag has been set to FALSE): in this case, output a LINK
+      //   tag for each file. 
       case 'file':
-        // Depending on whether aggregation is desired, include the file.
-        if (!$item['preprocess'] || !($is_writable && $preprocess_css)) {
-          $element = $css_element;
-          $element['#attributes']['media'] = $item['media'];
-          $element['#attributes']['href'] = file_create_url($item['data']) . $query_string;
-          $rendered_css[] = theme('html_tag', array('element' => $element));
+        // The group has been aggregated into a single file: output a LINK tag
+        // for the aggregate file.
+        if (isset($group['data'])) {
+          $element = $link_element_defaults;
+          // The aggregate file name already incorporates the dummy query
+          // string information. The query string does not need to be added to
+          // the URL.
+          $element['#attributes']['href'] = file_create_url($group['data']);
+          $element['#attributes']['media'] = $group['media'];
+          $element['#prefix'] = $group['html_prefix'];
+          $element['#suffix'] = $group['html_suffix'];
+          $elements[] = $element;
+        }
+        // The group can be aggregated, but hasn't been: combine multiple items
+        // into as few STYLE tags as possible.
+        elseif ($group['preprocess']) {
+          $import = array();
+          foreach ($group['items'] as $item) {
+            // The dummy query string needs to be added to the URL to control
+            // browser-caching. IE7 does not support a media type on the @import
+            // statement, so we instead specify the media for the group on the
+            // STYLE tag.
+            $import[] = '@import url("' . file_create_url($item['data']) . $query_string . '");';
+          }
+          // In addition to IE's limit of 31 total CSS inclusion tags, it also
+          // has a limit of 31 @import statements per STYLE tag.
+          while (!empty($import)) {
+            $import_batch = array_slice($import, 0, 31);
+            $import = array_slice($import, 31);
+            $element = $style_element_defaults;
+            $element['#value'] = implode("\n", $import_batch);
+            $element['#attributes']['media'] = $group['media'];
+            $element['#prefix'] = $group['html_prefix'];
+            $element['#suffix'] = $group['html_suffix'];
+            $elements[] = $element;
+          }
         }
+        // The group contains items ineligible for aggregation: output a LINK
+        // tag for each file.
         else {
-          $preprocess_items[$item['media']][] = $item;
-          // Mark the position of the preprocess element,
-          // it should be at the position of the first preprocessed file.
-          $rendered_css['preprocess'] = '';
+          foreach ($group['items'] as $item) {
+            $element = $link_element_defaults;
+            // The dummy query string needs to be added to the URL to control
+            // browser-caching.
+            $element['#attributes']['href'] = file_create_url($item['data']) . $query_string;
+            $element['#attributes']['media'] = $item['media'];
+            $element['#prefix'] = $group['html_prefix'];
+            $element['#suffix'] = $group['html_suffix'];
+            $elements[] = $element;
+          }
         }
         break;
+      // For inline content, the 'data' property contains the CSS content. If
+      // the group's 'data' property is set, then output it in a single STYLE
+      // tag. Otherwise, output a separate STYLE tag for each item.
       case 'inline':
-        // Include inline stylesheets.
-        $inline_css .= drupal_load_stylesheet_content($item['data'], $item['preprocess']);
+        if (isset($group['data'])) {
+          $element = $style_element_defaults;
+          $element['#value'] = $group['data'];
+          $element['#attributes']['media'] = $group['media'];
+          $element['#prefix'] = $group['html_prefix'];
+          $element['#suffix'] = $group['html_suffix'];
+          $elements[] = $element;
+        }
+        else {
+          foreach ($group['items'] as $item) {
+            $element = $style_element_defaults;
+            $element['#value'] = $item['data'];
+            $element['#attributes']['media'] = $item['media'];
+            $element['#prefix'] = $group['html_prefix'];
+            $element['#suffix'] = $group['html_suffix'];
+            $elements[] = $element;
+          }
+        }
         break;
+      // Output a LINK tag for each external item. The item's 'data' property
+      // contains the full URL.
       case 'external':
-        // Preprocessing for external CSS files is ignored.
-        $element = $css_element;
-        $element['#attributes']['media'] = $item['media'];
-        $element['#attributes']['href'] = $item['data'];
-        $external_css .= theme('html_tag', array('element' => $element));
+        foreach ($group['items'] as $item) {
+          $element = $link_element_defaults;
+          $element['#attributes']['href'] = $item['data'];
+          $element['#attributes']['media'] = $item['media'];
+          $element['#prefix'] = $group['html_prefix'];
+          $element['#suffix'] = $group['html_suffix'];
+          $elements[] = $element;
+        }
         break;
     }
   }
 
-  if (!empty($preprocess_items)) {
-    foreach ($preprocess_items as $media => $items) {
-      // Prefix filename to prevent blocking by firewalls which reject files
-      // starting with "ad*".
-      $element = $css_element;
-      $element['#attributes']['media'] = $media;
-      $filename = 'css_' . md5(serialize($items) . $query_string) . '.css';
-      $element['#attributes']['href'] = file_create_url(drupal_build_css_cache($items, $filename));
-      $rendered_css['preprocess'] .= theme('html_tag', array('element' => $element));
-    }
-  }
-  // Enclose the inline CSS with the style tag if required.
-  if (!empty($inline_css)) {
-    $element = $css_element;
-    $element['#tag'] = 'style';
-    $element['#value'] = $inline_css;
-    unset($element['#attributes']['rel']);
-    $inline_css = "\n" . theme('html_tag', array('element' => $element));
-  }
-
-  // Output all the CSS files with the inline stylesheets showing up last.
-  return implode($rendered_css) . $external_css . $inline_css;
+  return $elements;
 }
 
 /**
Index: includes/theme.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.inc,v
retrieving revision 1.578
diff -u -p -r1.578 theme.inc
--- includes/theme.inc	23 Feb 2010 18:32:00 -0000	1.578
+++ includes/theme.inc	24 Feb 2010 19:41:56 -0000
@@ -2545,7 +2545,6 @@ function template_preprocess_maintenance
   $variables['front_page']        = url();
   $variables['breadcrumb']        = '';
   $variables['feed_icons']        = '';
-  $variables['head']              = drupal_get_html_head();
   $variables['help']              = '';
   $variables['language']          = $language;
   $variables['language']->dir     = $language->direction ? 'rtl' : 'ltr';
@@ -2555,9 +2554,6 @@ function template_preprocess_maintenance
   $variables['secondary_menu']    = array();
   $variables['site_name']         = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : '');
   $variables['site_slogan']       = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : '');
-  $variables['css']               = drupal_add_css();
-  $variables['styles']            = drupal_get_css();
-  $variables['scripts']           = drupal_get_js();
   $variables['tabs']              = '';
   $variables['title']             = drupal_get_title();
   $variables['closure']           = '';
@@ -2585,6 +2581,21 @@ function template_preprocess_maintenance
 }
 
 /**
+ * The variables generated here is a mirror of template_process_html().
+ * This processor will run it's course when theme_maintenance_page() is
+ * invoked. It is also used in theme_install_page() and theme_update_page() to
+ * keep all the variables consistent.
+ *
+ * @see maintenance-page.tpl.php
+ */
+function template_process_maintenance_page(&$variables) {
+  $variables['head']    = drupal_get_html_head();
+  $variables['css']     = drupal_add_css();
+  $variables['styles']  = drupal_get_css();
+  $variables['scripts'] = drupal_get_js();
+}
+
+/**
  * Preprocess variables for region.tpl.php
  *
  * Prepare the values passed to the theme_region function to be passed into a
Index: includes/theme.maintenance.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/theme.maintenance.inc,v
retrieving revision 1.53
diff -u -p -r1.53 theme.maintenance.inc
--- includes/theme.maintenance.inc	9 Jan 2010 23:03:21 -0000	1.53
+++ includes/theme.maintenance.inc	24 Feb 2010 19:41:56 -0000
@@ -137,6 +137,7 @@ function theme_install_page($variables) 
   template_preprocess($variables, 'install_page');
   template_preprocess_maintenance_page($variables);
   template_process($variables, 'install_page');
+  template_process_maintenance_page($variables);
 
   // Special handling of error messages
   $messages = drupal_set_message();
@@ -197,6 +198,7 @@ function theme_update_page($variables) {
   template_preprocess($variables, 'update_page');
   template_preprocess_maintenance_page($variables);
   template_process($variables, 'update_page');
+  template_process_maintenance_page($variables);
 
   // Special handling of warning messages.
   $messages = drupal_set_message();
Index: modules/simpletest/tests/common.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/common.test,v
retrieving revision 1.102
diff -u -p -r1.102 common.test
--- modules/simpletest/tests/common.test	11 Feb 2010 17:44:47 -0000	1.102
+++ modules/simpletest/tests/common.test	24 Feb 2010 19:41:57 -0000
@@ -558,7 +558,9 @@ class CascadingStylesheetsTestCase exten
     $css = 'http://example.com/style.css';
     drupal_add_css($css, 'external');
     $styles = drupal_get_css();
-    $this->assertTrue(strpos($styles, 'href="' . $css) > 0, t('Rendering an external CSS file.'));
+    // Stylesheet URL may be the href of a LINK tag or in an @import statement
+    // of a STYLE tag.
+    $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, t('Rendering an external CSS file.'));
   }
 
   /**
@@ -566,10 +568,10 @@ class CascadingStylesheetsTestCase exten
    */
   function testRenderInlinePreprocess() {
     $css = 'body { padding: 0px; }';
-    $css_preprocessed = '<style type="text/css">' . drupal_load_stylesheet_content($css, TRUE) . '</style>';
+    $css_preprocessed = '<style type="text/css" media="all">' . drupal_load_stylesheet_content($css, TRUE) . '</style>';
     drupal_add_css($css, 'inline');
     $styles = drupal_get_css();
-    $this->assertEqual($styles, "\n" . $css_preprocessed . "\n", t('Rendering preprocessed inline CSS adds it to the page.'));
+    $this->assertEqual(trim($styles), $css_preprocessed, t('Rendering preprocessed inline CSS adds it to the page.'));
   }
 
   /**
@@ -631,8 +633,10 @@ class CascadingStylesheetsTestCase exten
 
 
     $styles = drupal_get_css();
-    if (preg_match_all('/href="' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
-      $result = $matches[1];
+    // Stylesheet URL may be the href of a LINK tag or in an @import statement
+    // of a STYLE tag.
+    if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
+      $result = $matches[2];
     }
     else {
       $result = array();
Index: modules/system/system.api.php
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.api.php,v
retrieving revision 1.136
diff -u -p -r1.136 system.api.php
--- modules/system/system.api.php	23 Feb 2010 05:04:50 -0000	1.136
+++ modules/system/system.api.php	24 Feb 2010 19:41:58 -0000
@@ -603,13 +603,28 @@ function hook_library_alter(&$libraries,
  * Alter CSS files before they are output on the page.
  *
  * @param $css
- *   An array of all CSS items (files and inline CSS) being requested on the page.
+ *   An array of all CSS items (files and inline CSS) being requested on the
+ *   page.
+ * @param $callbacks
+ *   An array of callbacks to override Drupal's default implementation of how
+ *   to group CSS items, aggregate the files within a group, and render the CSS
+ *   tags. Can contain the following keys:
+ *   - group: A function to run instead of _drupal_css_group().
+ *   - aggregate: A function to run instead of _drupal_css_aggregate().
+ *   - render: A function to run instead of _drupal_css_render().
+ *
  * @see drupal_add_css()
  * @see drupal_get_css()
  */
-function hook_css_alter(&$css) {
+function hook_css_alter(&$css, &$callbacks) {
   // Remove defaults.css file.
   unset($css[drupal_get_path('module', 'system') . '/defaults.css']);
+
+  // This site will definitely have few enough CSS files to not encroach on IE's
+  // limit, but for some reason needs to run with aggregation disabled, and we
+  // want all CSS files included with LINK tags instead @import statements, so
+  // override _drupal_css_render().
+  $callbacks['render'] = 'mymodule_css_render';
 }
 
 /**
Index: themes/garland/maintenance-page.tpl.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/maintenance-page.tpl.php,v
retrieving revision 1.14
diff -u -p -r1.14 maintenance-page.tpl.php
--- themes/garland/maintenance-page.tpl.php	30 Jan 2010 07:59:26 -0000	1.14
+++ themes/garland/maintenance-page.tpl.php	24 Feb 2010 19:41:59 -0000
@@ -20,9 +20,6 @@
     <?php print $head ?>
     <?php print $styles ?>
     <?php print $scripts ?>
-    <!--[if lt IE 7]>
-      <?php print garland_get_ie_styles(); ?>
-    <![endif]-->
   </head>
   <body class="<?php print $classes ?>">
 
Index: themes/garland/template.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/garland/template.php,v
retrieving revision 1.37
diff -u -p -r1.37 template.php
--- themes/garland/template.php	30 Jan 2010 07:59:26 -0000	1.37
+++ themes/garland/template.php	24 Feb 2010 19:41:59 -0000
@@ -25,10 +25,12 @@ function garland_breadcrumb($variables) 
  * Override or insert variables into the maintenance page template.
  */
 function garland_preprocess_maintenance_page(&$vars) {
-  // Toggle fixed or fluid width.
-  if (theme_get_setting('garland_width') == 'fluid') {
-    $vars['classes_array'][] = 'fluid-width';
-  }
+  // While markup for normal pages is split into page.tpl.php and html.tpl.php,
+  // the markup for the maintenance page is all in the single
+  // maintenance-page.tpl.php template. So, to have what's done in
+  // garland_preprocess_html() also happen on the maintenance page, it has to be
+  // called here.
+  garland_preprocess_html($vars);
 }
 
 /**
@@ -39,6 +41,8 @@ function garland_preprocess_html(&$vars)
   if (theme_get_setting('garland_width') == 'fluid') {
     $vars['classes_array'][] = 'fluid-width';
   }
+  // Add conditional CSS for IE6.
+  drupal_add_css(path_to_theme() . '/fix-ie.css', array('weight' => CSS_THEME, 'conditional_comment_expression' => 'lt IE 7', 'preprocess' => FALSE));
 }
 
 /**
@@ -49,7 +53,6 @@ function garland_process_html(&$vars) {
   if (module_exists('color')) {
     _color_html_alter($vars);
   }
-  $vars['styles'] .= "\n<!--[if lt IE 7]>\n" . garland_get_ie_styles() . "<![endif]-->\n";
 }
 
 /**
@@ -136,17 +139,3 @@ function garland_preprocess_region(&$var
 function garland_menu_local_tasks() {
   return menu_primary_local_tasks();
 }
-
-/**
- * Generates IE CSS links for LTR and RTL languages.
- */
-function garland_get_ie_styles() {
-  global $language;
-
-  $ie_styles = '<link type="text/css" rel="stylesheet" media="all" href="' . file_create_url(path_to_theme() . '/fix-ie.css') . '" />' . "\n";
-  if ($language->direction == LANGUAGE_RTL) {
-    $ie_styles .= '      <style type="text/css" media="all">@import "' . file_create_url(path_to_theme() . '/fix-ie-rtl.css') . '";</style>' . "\n";
-  }
-
-  return $ie_styles;
-}
Index: themes/seven/template.php
===================================================================
RCS file: /cvs/drupal/drupal/themes/seven/template.php,v
retrieving revision 1.12
diff -u -p -r1.12 template.php
--- themes/seven/template.php	4 Jan 2010 03:46:31 -0000	1.12
+++ themes/seven/template.php	24 Feb 2010 19:41:59 -0000
@@ -2,11 +2,16 @@
 // $Id: template.php,v 1.12 2010/01/04 03:46:31 webchick Exp $
 
 /**
- * Override or insert variables into the page template.
+ * Override or insert variables into the html template.
  */
-function seven_process_html(&$vars) {
-  $vars['styles'] .= "\n<!--[if lt IE 7]>\n" . '<link type="text/css" rel="stylesheet" media="all" href="' . file_create_url(path_to_theme() . '/ie6.css') . '" />' . "\n" . "<![endif]-->\n";
+function seven_preprocess_html(&$vars) {
+  // Add conditional CSS for IE6.
+  drupal_add_css(path_to_theme() . '/ie6.css', array('weight' => CSS_THEME, 'conditional_comment_expression' => 'lt IE 7', 'preprocess' => FALSE));
 }
+
+/**
+ * Override or insert variables into the page template.
+ */
 function seven_preprocess_page(&$vars) {
   $vars['primary_local_tasks'] = menu_primary_local_tasks();
   $vars['secondary_local_tasks'] = menu_secondary_local_tasks();
