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 25 Feb 2010 19:44:12 -0000 @@ -2651,6 +2651,9 @@ 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. + * - 'browsers': An array containing information specifying which browsers + * should load the CSS item. See drupal_pre_render_conditional_comments() + * for details. * * @return * An array of queued cascading stylesheets. @@ -2677,6 +2680,11 @@ function drupal_add_css($data = NULL, $o 'media' => 'all', 'preprocess' => TRUE, 'data' => $data, + 'browsers' => array(), + ); + $options['browsers'] += array( + 'IE' => TRUE, + '!IE' => TRUE, ); // Always add a tiny value to the weight, to conserve the insertion order. @@ -2722,25 +2730,14 @@ 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. + // Allow modules to alter the CSS items. drupal_alter('css', $css); - // 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 +2753,354 @@ 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( + // Render the HTML needed to load the CSS. + $styles = array( + '#type' => 'styles', + '#items' => $css, + ); + return drupal_render($styles); +} + +/** + * Default callback to group CSS items. + * + * This function arranges the CSS items that are in the #items property of the + * styles element into groups. Arranging the CSS items into groups serves two + * purposes. When aggregation is enabled, files within a group are aggregated + * into a single file, significantly improving page loading performance by + * minimizing network traffic overhead. When aggregation is disabled, grouping + * allows multiple files to be loaded from a single STYLE tag, enabling sites + * with many modules enabled or a complex theme being used to stay within IE's + * 31 CSS inclusion tag limit: http://drupal.org/node/228818. + * + * This function puts multiple items into the same group if they are groupable + * and if they are for the same 'media' and 'browsers'. Items of the 'file' type + * are groupable if their 'preprocess' flag is TRUE, items of the 'inline' type + * are always groupable, and items of the 'external' type are never groupable. + * This function also ensures that the process of grouping items does not change + * their relative order. This requirement may result in multiple groups for the + * same type, media, and browsers, if needed to accomodate other items in + * between. + * + * @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 keys (e.g., 'media', + * 'data', etc.) as a CSS item from the $css parameter, with the value of + * each key applying to the group as a whole. Each group also contains an + * 'items' key, which is the subset of items from $css that are in the group. + * + * @see drupal_pre_render_styles() + */ +function drupal_group_css($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) { + // The browsers for which the CSS item needs to be loaded is part of the + // information that determines when a new group is needed, but the order of + // keys in the array doesn't matter, and we don't want a new group if all + // that's different is that order. + ksort($item['browsers']); + + // 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['browsers']) : FALSE; + break; + case 'inline': + // Always group inline items. + $group_keys = array($item['type'], $item['media'], $item['browsers']); + 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). To optimize download, + * it also compresses the aggregate files by removing comments, whitespace, and + * other unnecessary content. Additionally, this functions aggregates inline + * content together, regardless of the site-wide aggregation setting. + * + * @param $css_groups + * An array of CSS groups as returned by drupal_group_css(). This function + * modifies the group's 'data' property for each group that is aggregated. + * + * @see drupal_group_css() + * @see drupal_pre_render_styles() + */ +function drupal_aggregate_css(&$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 when + // rendering the HTML tag (see drupal_pre_render_styles()). 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) { + // Prefix filename to prevent blocking by firewalls which reject files + // starting with "ad*". + $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; + } + } +} + +/** + * #pre_render callback to add the elements needed for CSS tags to be rendered. + * + * 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. + * + * @param $element + * A render array containing: + * - '#items': The CSS items as returned by drupal_add_css() and altered by + * drupal_get_css(). + * - '#group_callback': A function to call to group #items to enable the use + * of fewer tags by aggregating files and/or using multiple @import + * statements within a single tag. + * - '#aggregate_callback': A function to call to aggregate the items within + * the groups arranged by the #group_callback function. + * + * @return + * A render array that will render to a string of XHTML CSS tags. + * + * @see drupal_get_css() + */ +function drupal_pre_render_styles($elements) { + // Group and aggregate the items. + if (isset($elements['#group_callback'])) { + $elements['#groups'] = $elements['#group_callback']($elements['#items']); + } + if (isset($elements['#aggregate_callback'])) { + $elements['#aggregate_callback']($elements['#groups']); + } + + // 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( + '#type' => '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( + '#type' => 'html_tag', + '#tag' => 'style', + '#attributes' => array( + 'type' => 'text/css', + ), + ); + + // Loop through each group. + foreach ($elements['#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['#browsers'] = $group['browsers']; + $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['#browsers'] = $group['browsers']; + $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['#browsers'] = $group['browsers']; + $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['#browsers'] = $group['browsers']; + $elements[] = $element; + } + else { + foreach ($group['items'] as $item) { + $element = $style_element_defaults; + $element['#value'] = $item['data']; + $element['#attributes']['media'] = $item['media']; + $element['#browsers'] = $group['browsers']; + $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['#browsers'] = $group['browsers']; + $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; } /** @@ -4345,6 +4621,82 @@ function drupal_set_page_content($conten } /** + * #pre_render callback to render #browsers into #prefix and #suffix. + * + * @param $elements + * A render array with a '#browsers' property. The '#browsers' property can + * contain any or all of the following keys: + * - 'IE': If FALSE, the element is not rendered by Internet Explorer. If + * TRUE, the element is rendered by Internet Explorer. Can also be a string + * containing an expression for Internet Explorer to evaluate as part of a + * conditional comment. For example, this can be set to 'lt IE 7' for the + * element to be rendered in Internet Explorer 6, but not in Internet + * Explorer 7 or higher. Defaults to TRUE. + * - '!IE': If FALSE, the element is not rendered by browsers other than + * Internet Explorer. If TRUE, the element is rendered by those browsers. + * Defaults to TRUE. + * Examples: + * - To render an element in all browsers, '#browsers' can be left out or set + * to array('IE' => TRUE, '!IE' => TRUE). + * - To render an element in Internet Explorer only, '#browsers' can be set + * to array('!IE' => FALSE). + * - To render an element in Internet Explorer 6 only, '#browsers' can be set + * to array('IE' => 'lt IE 7', '!IE' => FALSE). + * - To render an element in Internet Explorer 8 and higher and in all other + * browsers, '#browsers' can be set to array('IE' => 'gte IE 8'). + * + * @return + * The passed in element with markup for conditional comments potentially + * added to '#prefix' and '#suffix'. + */ +function drupal_pre_render_conditional_comments($elements) { + $browsers = isset($elements['#browsers']) ? $elements['#browsers'] : array(); + $browsers += array( + 'IE' => TRUE, + '!IE' => TRUE, + ); + + // If rendering in all browsers, no need for conditional comments. + if ($browsers['IE'] === TRUE && $browsers['!IE']) { + return $elements; + } + + // Determine the conditional comment expression for Internet Explorer to + // evaluate. + if ($browsers['IE'] === TRUE) { + $expression = 'IE'; + } + elseif ($browsers['IE'] === FALSE) { + $expression = '!IE'; + } + else { + $expression = $browsers['IE']; + } + + // Wrap the element's potentially existing #prefix and #suffix properties with + // conditional comment markup. The conditional comment expression is evaluated + // by Internet Explorer only. To control the rendering by other browsers, + // either the "downlevel-hidden" or "downlevel-revealed" technique must be + // used. See http://en.wikipedia.org/wiki/Conditional_comment for details. + $elements += array( + '#prefix' => '', + '#suffix' => '', + ); + if (!$browsers['!IE']) { + // "downlevel-hidden". + $elements['#prefix'] = "\n\n"; + } + else { + // "downlevel-revealed". + $elements['#prefix'] = "\n\n" . $elements['#prefix']; + $elements['#suffix'] .= "\n"; + } + + return $elements; +} + +/** * #pre_render callback to render a link into #markup. * * Doing so during pre_render gives modules a chance to alter the link parts. 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 25 Feb 2010 19:44:13 -0000 @@ -2483,8 +2483,8 @@ function theme_get_suggestions($args, $b } /** - * The variables generated here is a mirror of template_preprocess_page(). - * This preprocessor will run it's course when theme_maintenance_page() is + * The variables array generated here is a mirror of template_preprocess_page(). + * This preprocessor will run its 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. * @@ -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 array generated here is a mirror of template_process_html(). + * This processor will run its 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 25 Feb 2010 19:44:13 -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 25 Feb 2010 19:44:15 -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 = ''; + $css_preprocessed = ''; 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.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.890 diff -u -p -r1.890 system.module --- modules/system/system.module 17 Feb 2010 09:09:30 -0000 1.890 +++ modules/system/system.module 25 Feb 2010 19:44:15 -0000 @@ -311,12 +311,18 @@ function system_element_info() { $types['ajax_commands'] = array( '#ajax_commands' => array(), ); - $types['html_tag'] = array( '#theme' => 'html_tag', + '#pre_render' => array('drupal_pre_render_conditional_comments'), '#attributes' => array(), '#value' => NULL, ); + $types['styles'] = array( + '#items' => array(), + '#pre_render' => array('drupal_pre_render_styles'), + '#group_callback' => 'drupal_group_css', + '#aggregate_callback' => 'drupal_aggregate_css', + ); // Input elements. $types['submit'] = array( 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 25 Feb 2010 19:44:16 -0000 @@ -20,9 +20,6 @@ - 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 25 Feb 2010 19:44:16 -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, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), 'preprocess' => FALSE)); } /** @@ -49,7 +53,6 @@ function garland_process_html(&$vars) { if (module_exists('color')) { _color_html_alter($vars); } - $vars['styles'] .= "\n\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 = '' . "\n"; - if ($language->direction == LANGUAGE_RTL) { - $ie_styles .= ' ' . "\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 25 Feb 2010 19:44:16 -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\n"; +function seven_preprocess_html(&$vars) { + // Add conditional CSS for IE6. + drupal_add_css(path_to_theme() . '/ie6.css', array('weight' => CSS_THEME, 'browsers' => array('IE' => 'lt IE 7', '!IE' => FALSE), '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();