Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.1077 diff -u -p -r1.1077 common.inc --- includes/common.inc 4 Jan 2010 23:08:34 -0000 1.1077 +++ includes/common.inc 6 Jan 2010 04:28:24 -0000 @@ -3281,16 +3281,8 @@ 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( - '#tag' => 'link', - '#attributes' => array( - 'type' => 'text/css', - 'rel' => 'stylesheet', - ), - ); - $rendered_css = array(); - $inline_css = ''; - $external_css = ''; + $files = array(); + $inline = array(); $preprocess_items = array(); foreach ($css as $data => $item) { // Loop through each of the stylesheets, including them appropriately based @@ -3299,28 +3291,22 @@ function drupal_get_css($css = NULL) { 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)); + $files[] = array('media' => $item['media'], 'href' => file_create_url($item['data']) . $query_string, 'external' => FALSE); } 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'] = ''; + $files['preprocess_' . $item['media']] = NULL; } break; case 'inline': // Include inline stylesheets. - $inline_css .= drupal_load_stylesheet_content($item['data'], $item['preprocess']); + $inline[] = drupal_load_stylesheet_content($item['data'], $item['preprocess']); break; 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)); + $files[] = array('media' => $item['media'], 'href' => $item['data'], 'external' => TRUE); break; } } @@ -3329,24 +3315,13 @@ function drupal_get_css($css = NULL) { 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)); + $files['preprocess_' . $media] .= array('media' => $item['media'], 'href' => file_create_url(drupal_build_css_cache($items, $filename)), 'external' => FALSE); } } - // 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; + // Output all the CSS files. + return theme('stylesheets', array('files' => $files, 'inline' => $inline)); } /** @@ -5485,6 +5460,9 @@ function drupal_common_theme() { 'html_tag' => array( 'render element' => 'element', ), + 'stylesheets' => array( + 'variables' => array('files' => array(), 'inline' => array()), + ), // from theme.maintenance.inc 'maintenance_page' => array( 'variables' => array('content' => NULL, 'show_messages' => TRUE), Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.563 diff -u -p -r1.563 theme.inc --- includes/theme.inc 5 Jan 2010 18:56:48 -0000 1.563 +++ includes/theme.inc 6 Jan 2010 04:28:24 -0000 @@ -2033,6 +2033,150 @@ function theme_html_tag($variables) { } /** + * Generate the LINK and STYLE tags for adding CSS stylesheets to the page. + * + * @param $variables + * An associative array containing: + * - files: An array of CSS files to include. Each item in the array has the + * following keys: + * - media: The media for which the CSS file is appropriate. + * - href: The URL of the CSS file. + * - external: Boolean. TRUE if the CSS file is external to the website (if + * it was added to drupal_add_css() using 'external' for the type option). + * - inline: An array of inline CSS content. Each item in the array is a CSS + * string. + * - max_tags: (optional) The maximum number of combined LINK and STYLE tags + * to output. If not specified, defaults to 31. This is to work around IE's + * limitation of only being able to process this many total stylesheet + * tags. A preprocess function can set this variable to something smaller to + * accomodate a page that outputs additional LINK (rel="stylesheet") or + * STYLE tags. LINK tags with a "rel" attribute of something other than + * "stylesheet" doesn't count against IE's limit, so there is no need for a + * preprocess function to decrease this number to accomodate non-stylesheet + * LINK tags. This function includes as many stylesheets as it can using + * LINK tags, and combines the rest into groups of upto 31 @import + * statements per STYLE tag. Site administrators are encouraged to enable + * the CSS aggregation setting on the /admin/config/development/performance + * page once the website is no longer undergoing active theme development. + * When aggregation is enabled, the total number of stylesheets should be + * much smaller than this limit, so all of them can be included using a LINK + * tag. + * + * @ingroup themeable + */ +function theme_stylesheets($variables) { + $output = ''; + + // Order all the external CSS files to be after the non-external ones. + // @todo Add a comment explaining why. + $internal = array(); + $external = array(); + foreach ($variables['files'] as $file) { + if ($file['external']) { + $external[] = $file; + } + else { + $internal[] = $file; + } + } + $files = array_merge($internal, $external); + + // Default max_tags to 31, if it isn't specified. Then decrement if there is + // inline CSS since that will use up one. + $max_tags = !empty($variables['max_tags']) ? $variables['max_tags'] : 31; + if (!empty($variables['inline'])) { + $max_tags--; + } + + // Without changing their order, split $files into groups, where each group + // contains files of the same media type and each group is restricted to 31 + // items. This enables the use of STYLE tags on a per-group basis if needed + // to stay within the $max_tags limit. We must ensure that each file within a + // group is for the same media, because IE7 does not support media declaration + // in the @import statement, so the media declaration must be on the STYLE + // tag. + $file_groups = array(); + $file_group = array(); + $file_group_media = NULL; + $file_group_counts = array(); + foreach ($files as $file) { + if (count($file_group) == 31 || ($file_group_media && $file['media'] != $file_group_media)) { + if (!empty($file_group)) { + $file_groups[] = $file_group; + $file_group_counts[] = count($file_group); + $file_group = array(); + } + $file_group_media = $file['media']; + } + $file_group[] = $file; + } + if (!empty($file_group)) { + $file_groups[] = $file_group; + $file_group_counts[] = count($file_group); + } + + // Split file groups into ones that can use a LINK tag vs. ones that must + // use a STYLE tag. After this code, $file_groups will contain files that can + // use a LINK tag and $file_groups_requiring_style_tag will contain the ones + // that must use a STYLE tag. + $file_groups_requiring_style_tag = array(); + while (!empty($file_groups) && array_sum($file_group_counts) > $max_tags) { + $file_groups_requiring_style_tag[] = array_pop($file_groups); + array_pop($file_group_counts); + $max_tags--; + } + $file_groups_requiring_style_tag = array_reverse($file_groups_requiring_style_tag); + + // Defaults for LINK and STYLE elements. + $link_element_defaults = array( + '#tag' => 'link', + '#attributes' => array( + 'type' => 'text/css', + 'rel' => 'stylesheet', + ), + ); + $style_element_defaults = array( + '#tag' => 'style', + '#attributes' => array( + 'type' => 'text/css', + ), + ); + + // Add the LINK tags. + foreach ($file_groups as $group) { + foreach ($group as $file) { + $element = $link_element_defaults; + $element['#attributes']['media'] = $file['media']; + $element['#attributes']['href'] = $file['href']; + $output .= theme('html_tag', array('element' => $element)); + } + } + + // Add the STYLE tags. + if (!empty($file_groups_requiring_style_tag)) { + foreach ($file_groups_requiring_style_tag as $group) { + $import = ''; + foreach ($group as $file) { + $import .= '@import url("' . $file['href'] . '");' . "\n"; + } + $element = $style_element_defaults; + $element['#value'] = $import; + $element['#attributes']['media'] = $group[0]['media']; + $output .= theme('html_tag', array('element' => $element)); + } + } + + // Add inline CSS content. + if (!empty($variables['inline'])) { + $element = $style_element_defaults; + $element['#value'] = implode($variables['inline']); + $output .= "\n" . theme('html_tag', array('element' => $element)); + } + + return $output; +} + +/** * Returns code that emits the 'more' link used on blocks. * * @param $variables