MDL-33468 css_optimiser: Fixed aggressive optimisation so that it only combines sibli...
authorSam Hemelryk <sam@moodle.com>
Tue, 12 Jun 2012 04:49:18 +0000 (16:49 +1200)
committerSam Hemelryk <sam@moodle.com>
Thu, 14 Jun 2012 05:25:19 +0000 (17:25 +1200)
lib/csslib.php
lib/tests/cssslib_test.php

index 0b4523f..8058e19 100644 (file)
@@ -776,6 +776,16 @@ class css_optimiser {
             $buffer .= $char;
         }
 
+        foreach ($medias as $media) {
+            $this->optimise($media);
+        }
+        $css = $this->produce_css($charset, $imports, $medias, $keyframes);
+
+        $this->timecomplete = microtime(true);
+        return trim($css);
+    }
+
+    protected function produce_css($charset, array $imports, array $medias, array $keyframes) {
         $css = '';
         if (!empty($charset)) {
             $imports[] = $charset;
@@ -785,14 +795,12 @@ class css_optimiser {
             $css .= "\n\n";
         }
 
+        $cssreset = array();
+        $cssstandard = array();
+        $csskeyframes = array();
+
         // Process each media declaration individually
         foreach ($medias as $media) {
-            $media->organise_rules_by_selectors();
-            $this->optimisedrules += $media->count_rules();
-            $this->optimisedselectors +=  $media->count_selectors();
-            if ($media->has_errors()) {
-                $this->errors += $media->get_errors();
-            }
             // If this declaration applies to all media types
             if (in_array('all', $media->get_types())) {
                 // Collect all rules that represet reset rules and remove them from the media object at the same time.
@@ -800,33 +808,50 @@ class css_optimiser {
                 // can't end up out of order because of optimisation.
                 $resetrules = $media->get_reset_rules(true);
                 if (!empty($resetrules)) {
-                    $css .= css_writer::media('all', $resetrules);
+                    $cssreset[] = css_writer::media('all', $resetrules);
                 }
             }
-        }
-
-        // Now process every media object and produce CSS for it.
-        foreach ($medias as $media) {
-            $css .= $media->out();
+            // Get the standard cSS
+            $cssstandard[] = $media->out();
         }
 
         // Finally if there are any keyframe declarations process them now.
         if (count($keyframes) > 0) {
-            $css .= "\n";
             foreach ($keyframes as $keyframe) {
                 $this->optimisedrules += $keyframe->count_rules();
                 $this->optimisedselectors +=  $keyframe->count_selectors();
                 if ($keyframe->has_errors()) {
                     $this->errors += $keyframe->get_errors();
                 }
-                $css .= $keyframe->out();
+                $csskeyframes[] = $keyframe->out();
             }
         }
 
+        // Join it all together
+        $css .= join('', $cssreset);
+        $css .= join('', $cssstandard);
+        $css .= join('', $csskeyframes);
+
+        // Record the strlenght of the now optimised CSS.
         $this->optimisedstrlen = strlen($css);
 
-        $this->timecomplete = microtime(true);
-        return trim($css);
+        // Return the now produced CSS
+        return $css;
+    }
+
+    /**
+     * Optimises the CSS rules within a rule collection of one form or another
+     *
+     * @param css_rule_collection $media
+     * @return void This function acts in reference
+     */
+    protected function optimise(css_rule_collection $media) {
+        $media->organise_rules_by_selectors();
+        $this->optimisedrules += $media->count_rules();
+        $this->optimisedselectors +=  $media->count_selectors();
+        if ($media->has_errors()) {
+            $this->errors += $media->get_errors();
+        }
     }
 
     /**
@@ -1189,7 +1214,7 @@ abstract class css_writer {
 
         $output = '';
         if ($typestring !== 'all') {
-            $output .= $nl.$nl."@media {$typestring} {".$nl;
+            $output .= "\n@media {$typestring} {".$nl;
             self::increase_indent();
         }
         foreach ($rules as $rule) {
@@ -1213,7 +1238,7 @@ abstract class css_writer {
     public static function keyframe($for, $name, array &$rules) {
         $nl = self::get_separator();
 
-        $output = $nl."@{$for} {$name} {";
+        $output = "\n@{$for} {$name} {";
         foreach ($rules as $rule) {
             $output .= $rule->out();
         }
@@ -1796,17 +1821,24 @@ abstract class css_rule_collection {
     public function organise_rules_by_selectors() {
         $optimised = array();
         $beforecount = count($this->rules);
+        $lasthash = null;
+        $lastrule = null;
         foreach ($this->rules as $rule) {
             $hash = $rule->get_style_hash();
-            if (!array_key_exists($hash, $optimised)) {
-                $optimised[$hash] = clone($rule);
-            } else {
+            if ($lastrule !== null && $lasthash !== null && $hash === $lasthash) {
                 foreach ($rule->get_selectors() as $selector) {
-                    $optimised[$hash]->add_selector($selector);
+                    $lastrule->add_selector($selector);
                 }
+                continue;
             }
+            $lastrule = clone($rule);
+            $lasthash = $hash;
+            $optimised[] = $lastrule;
+        }
+        $this->rules = array();
+        foreach ($optimised as $optimised) {
+            $this->rules[$optimised->get_selector_hash()] = $optimised;
         }
-        $this->rules = $optimised;
         $aftercount = count($this->rules);
         return ($beforecount < $aftercount);
     }
@@ -3685,6 +3717,16 @@ class css_style_background extends css_style {
             $value = str_replace($matches[1], '', $value);
         }
 
+        // Switch out the brackets so that they don't get messed up when we explode
+        $brackets = array();
+        $bracketcount = 0;
+        while (preg_match('#\([^\)\(]+\)#', $value, $matches)) {
+            $key = "##BRACKET-{$bracketcount}##";
+            $bracketcount++;
+            $brackets[$key] = $matches[0];
+            $value = str_replace($matches[0], $key, $value);
+        }
+
         $important = (stripos($value, '!important') !== false);
         if ($important) {
             // Great some genius put !important in the background shorthand property
@@ -3694,6 +3736,12 @@ class css_style_background extends css_style {
         $value = preg_replace('#\s+#', ' ', $value);
         $bits = explode(' ', $value);
 
+        foreach ($bits as $key => $bit) {
+            $bits[$key] = self::replace_bracket_placeholders($bit, $brackets);
+        }
+        unset($bracketcount);
+        unset($brackets);
+
         $repeats = array('repeat', 'repeat-x', 'repeat-y', 'no-repeat', 'inherit');
         $attachments = array('scroll' , 'fixed', 'inherit');
         $positions = array('top', 'left', 'bottom', 'right', 'center');
@@ -3705,7 +3753,6 @@ class css_style_background extends css_style {
         if (count($bits) > 0 && css_is_colour(reset($bits))) {
             $color = array_shift($bits);
         }
-        $return[] = new css_style_backgroundcolor('background-color', $color);
 
         $image = self::NULL_VALUE;
         if (count($bits) > 0 && preg_match('#^\s*(none|inherit|url\(\))\s*$#', reset($bits))) {
@@ -3714,20 +3761,17 @@ class css_style_background extends css_style {
                 $image = "url({$imageurl})";
             }
         }
-        $return[] = new css_style_backgroundimage('background-image', $image);
 
         $repeat = self::NULL_VALUE;
         if (count($bits) > 0 && in_array(reset($bits), $repeats)) {
             $repeat = array_shift($bits);
         }
-        $return[] = new css_style_backgroundrepeat('background-repeat', $repeat);
 
         $attachment = self::NULL_VALUE;
         if (count($bits) > 0 && in_array(reset($bits), $attachments)) {
             // scroll , fixed, inherit
             $attachment = array_shift($bits);
         }
-        $return[] = new css_style_backgroundattachment('background-attachment', $attachment);
 
         $position = self::NULL_VALUE;
         if (count($bits) > 0) {
@@ -3743,20 +3787,33 @@ class css_style_background extends css_style {
                 $position = join(' ',$widthbits);
             }
         }
-        $return[] = new css_style_backgroundposition('background-position', $position);
 
         if (count($unknownbits)) {
             foreach ($unknownbits as $bit) {
+                $bit = trim($bit);
                 if ($color === self::NULL_VALUE && css_is_colour($bit)) {
-                    $return[] = new css_style_backgroundcolor('background-color', $bit);
+                    $color = $bit;
                 } else if ($repeat === self::NULL_VALUE && in_array($bit, $repeats)) {
-                    $return[] = new css_style_backgroundrepeat('background-repeat', $bit);
+                    $repeat = $bit;
                 } else if ($attachment === self::NULL_VALUE && in_array($bit, $attachments)) {
-                    $return[] = new css_style_backgroundattachment('background-attachment', $bit);
+                    $attachment = $bit;
+                } else if ($bit !== '') {
+                    $return[] = css_style_background_advanced::init($bit);
                 }
             }
         }
 
+        if ($color === self::NULL_VALUE && $image === self::NULL_VALUE && $repeat === self::NULL_VALUE && $attachment === self::NULL_VALUE && $position === self::NULL_VALUE) {
+            // All primaries are null, return without doing anything else. There may be advanced madness there.
+            return $return;
+        }
+
+        $return[] = new css_style_backgroundcolor('background-color', $color);
+        $return[] = new css_style_backgroundimage('background-image', $image);
+        $return[] = new css_style_backgroundrepeat('background-repeat', $repeat);
+        $return[] = new css_style_backgroundattachment('background-attachment', $attachment);
+        $return[] = new css_style_backgroundposition('background-position', $position);
+
         if ($important) {
             foreach ($return as $style) {
                 $style->set_important();
@@ -3766,6 +3823,20 @@ class css_style_background extends css_style {
         return $return;
     }
 
+    /**
+     * Static helper method to switch in bracket replacements
+     *
+     * @param string $value
+     * @param array $placeholders
+     * @return string
+     */
+    protected static function replace_bracket_placeholders($value, array $placeholders) {
+        while (preg_match('/##BRACKET-\d+##/', $value, $matches)) {
+            $value = str_replace($matches[0], $placeholders[$matches[0]], $value);
+        }
+        return $value;
+    }
+
     /**
      * Consolidates background styles into a single background style
      *
@@ -3884,6 +3955,37 @@ class css_style_background extends css_style {
     }
 }
 
+/**
+ * A advanced background style that allows multiple values to preserve unknown entities
+ *
+ * @package core_css
+ * @category css
+ * @copyright 2012 Sam Hemelryk
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class css_style_background_advanced extends css_style_generic {
+    /**
+     * Creates a new background colour style
+     *
+     * @param string $value The value of the style
+     * @return css_style_backgroundimage
+     */
+    public static function init($value) {
+        $value = preg_replace('#\s+#', ' ', $value);
+        return new css_style_background_advanced('background', $value);
+    }
+
+    /**
+     * Returns true because the advanced background image supports multiple values.
+     * e.g. -webkit-linear-gradient and -moz-linear-gradient.
+     *
+     * @return boolean
+     */
+    public function allows_multiple_values() {
+        return true;
+    }
+}
+
 /**
  * A background colour style.
  *
@@ -4609,4 +4711,4 @@ class css_style_float extends css_style_generic {
         }
         return trim($value);
     }
-}
\ No newline at end of file
+}
index 6677b7b..c5f1223 100644 (file)
@@ -39,6 +39,15 @@ require_once($CFG->libdir . '/csslib.php');
  */
 class css_optimiser_testcase extends advanced_testcase {
 
+    protected $optimiser;
+
+    public function get_optimiser() {
+        if (!$this->optimiser instanceof css_optimiser) {
+            $this->optimiser = new css_optimiser;
+        }
+        return $this->optimiser;
+    }
+
     /**
      * Sets up the test class
      */
@@ -59,21 +68,12 @@ class css_optimiser_testcase extends advanced_testcase {
     public function test_process() {
         $optimiser = new css_optimiser;
 
-        $this->check_background($optimiser);
-        $this->check_borders($optimiser);
-        $this->check_colors($optimiser);
-        $this->check_margins($optimiser);
-        $this->check_padding($optimiser);
-        $this->check_widths($optimiser);
-        $this->check_cursor($optimiser);
-        $this->check_vertical_align($optimiser);
-
-        $this->try_broken_css_found_in_moodle($optimiser);
-        $this->try_invalid_css_handling($optimiser);
-        $this->try_bulk_processing($optimiser);
-        $this->try_break_things($optimiser);
-        $this->try_media_rules($optimiser);
-        $this->try_keyframe_css_animation($optimiser);
+        $this->try_broken_css_found_in_moodle();
+        $this->try_invalid_css_handling();
+        $this->try_bulk_processing();
+        $this->try_break_things();
+        $this->try_media_rules();
+        $this->try_keyframe_css_animation();
     }
 
     /**
@@ -105,7 +105,8 @@ class css_optimiser_testcase extends advanced_testcase {
      *
      * @param css_optimiser $optimiser
      */
-    protected function check_background(css_optimiser $optimiser) {
+    public function test_background() {
+        $optimiser = $this->get_optimiser();
 
         $cssin = '.test {background-color: #123456;}';
         $cssout = '.test{background-color:#123456;}';
@@ -209,13 +210,18 @@ class css_optimiser_testcase extends advanced_testcase {
         $cssin = '.userenrolment {background: inherit !important;background-image:url(test.png);}';
         $cssout = '.userenrolment{background:inherit !important;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
+
+        $css = '#filesskin .yui3-widget-hd{background:#CCC;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC));background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC);}';
+        $this->assertEquals($css, $optimiser->process($css));
     }
 
     /**
      * Border tests
      * @param css_optimiser $optimiser
      */
-    protected function check_borders(css_optimiser $optimiser) {
+    public function test_borders() {
+        $optimiser = $this->get_optimiser();
+
         $cssin = '.test {border: 1px solid #654321} .test {border-bottom-color: #123456}';
         $cssout = '.test{border:1px solid;border-color:#654321 #654321 #123456;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
@@ -305,7 +311,9 @@ class css_optimiser_testcase extends advanced_testcase {
      * Test colour styles
      * @param css_optimiser $optimiser
      */
-    protected function check_colors(css_optimiser $optimiser) {
+    public function test_colors() {
+        $optimiser = $this->get_optimiser();
+
         $css = '.css{}';
         $this->assertEquals($css, $optimiser->process($css));
 
@@ -397,7 +405,9 @@ class css_optimiser_testcase extends advanced_testcase {
         $this->assertEquals($css, $optimiser->process($css));
     }
 
-    protected function check_widths(css_optimiser $optimiser) {
+    public function test_widths() {
+        $optimiser = $this->get_optimiser();
+
         $cssin  = '.css {width:0}';
         $cssout = '.css{width:0;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
@@ -427,7 +437,9 @@ class css_optimiser_testcase extends advanced_testcase {
      * Test margin styles
      * @param css_optimiser $optimiser
      */
-    protected function check_margins(css_optimiser $optimiser) {
+    public function test_margins() {
+        $optimiser = $this->get_optimiser();
+
         $cssin = '.one {margin: 1px 2px 3px 4px}';
         $cssout = '.one{margin:1px 2px 3px 4px;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
@@ -449,7 +461,7 @@ class css_optimiser_testcase extends advanced_testcase {
         $this->assertEquals($cssout, $optimiser->process($cssin));
 
         $cssin = '.one, .two, .one.two, .one .two {margin:0;} .one.two {margin:0 7px;}';
-        $cssout = '.one, .two, .one .two{margin:0;} .one.two{margin:0 7px;}';
+        $cssout = '.one, .two{margin:0;} .one.two{margin:0 7px;} .one .two{margin:0;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
 
         $cssin = '.block {margin-top: 0px !important;margin-bottom: 0px !important;}';
@@ -470,7 +482,9 @@ class css_optimiser_testcase extends advanced_testcase {
      *
      * @param css_optimiser $optimiser
      */
-    protected function check_padding(css_optimiser $optimiser) {
+    public function test_padding() {
+        $optimiser = $this->get_optimiser();
+
         $cssin = '.one {padding: 1px 2px 3px 4px}';
         $cssout = '.one{padding:1px 2px 3px 4px;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
@@ -504,7 +518,7 @@ class css_optimiser_testcase extends advanced_testcase {
         $this->assertEquals($cssout, $optimiser->process($cssin));
 
         $cssin = '.one, .two, .one.two, .one .two {padding:0;} .one.two {padding:0 7px;}';
-        $cssout = '.one, .two, .one .two{padding:0;} .one.two{padding:0 7px;}';
+        $cssout = '.one, .two{padding:0;} .one.two{padding:0 7px;} .one .two{padding:0;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
 
         $cssin = '.block {padding-top: 0px !important;padding-bottom: 0px !important;}';
@@ -520,7 +534,9 @@ class css_optimiser_testcase extends advanced_testcase {
         $this->assertEquals($cssout, $optimiser->process($cssin));
     }
 
-    protected function check_cursor(css_optimiser $optimiser) {
+    public function test_cursor() {
+        $optimiser = $this->get_optimiser();
+
         // Valid cursor
         $cssin = '.one {cursor: pointer;}';
         $cssout = '.one{cursor:pointer;}';
@@ -542,7 +558,9 @@ class css_optimiser_testcase extends advanced_testcase {
         $this->assertEquals($cssout, $optimiser->process($cssin));
     }
 
-    protected function check_vertical_align(css_optimiser $optimiser) {
+    public function test_vertical_align() {
+        $optimiser = $this->get_optimiser();
+
         // Valid vertical aligns
         $cssin = '.one {vertical-align: baseline;}';
         $cssout = '.one{vertical-align:baseline;}';
@@ -563,7 +581,9 @@ class css_optimiser_testcase extends advanced_testcase {
         $this->assertEquals($cssout, $optimiser->process($cssin));
     }
 
-    protected function check_float(css_optimiser $optimiser) {
+    public function test_float() {
+        $optimiser = $this->get_optimiser();
+
         // Valid vertical aligns
         $cssin = '.one {float: inherit;}';
         $cssout = '.one{float:inherit;}';
@@ -589,7 +609,8 @@ class css_optimiser_testcase extends advanced_testcase {
      *
      * @param css_optimiser $optimiser
      */
-    protected function try_invalid_css_handling(css_optimiser $optimiser) {
+    protected function try_invalid_css_handling() {
+        $optimiser = $this->get_optimiser();
 
         $cssin = array(
             '.one{}',
@@ -655,7 +676,9 @@ class css_optimiser_testcase extends advanced_testcase {
      * Try to break some things
      * @param css_optimiser $optimiser
      */
-    protected function try_break_things(css_optimiser $optimiser) {
+    protected function try_break_things() {
+        $optimiser = $this->get_optimiser();
+
         // Wildcard test
         $cssin  = '* {color: black;}';
         $cssout = '*{color:#000;}';
@@ -717,7 +740,7 @@ class css_optimiser_testcase extends advanced_testcase {
      * A bulk processing test
      * @param css_optimiser $optimiser
      */
-    protected function try_bulk_processing(css_optimiser $optimiser) {
+    protected function try_bulk_processing() {
         global $CFG;
         $cssin = <<<CSS
 .test .one {
@@ -763,18 +786,16 @@ CSS;
 .test #one{margin:30px;}
 #new.style{color:#000;}
 
-
 @media print {
   #test .one{margin:40px;color:#123456;}
   #test #one{margin:45px;}
 }
-
 @media print,screen {
   #test .one{color:#654321;}
 }
 CSS;
         $CFG->cssoptimiserpretty = 1;
-        $this->assertEquals($optimiser->process($cssin), $cssout);
+        $this->assertEquals($this->get_optimiser()->process($cssin), $cssout);
     }
 
     /**
@@ -874,7 +895,9 @@ CSS;
      *
      * @param css_optimiser $optimiser
      */
-    public function try_broken_css_found_in_moodle(css_optimiser $optimiser) {
+    public function try_broken_css_found_in_moodle() {
+        $optimiser = $this->get_optimiser();
+
         // Notice how things are out of order here but that they get corrected
         $cssin = '.test {background:url([[pix:theme|pageheaderbgred]]) top center no-repeat}';
         $cssout = '.test{background:url([[pix:theme|pageheaderbgred]]) no-repeat top center;}';
@@ -916,7 +939,9 @@ CSS;
      * Test keyframe declarations
      * @param css_optimiser $optimiser
      */
-    public function try_keyframe_css_animation(css_optimiser $optimiser) {
+    public function try_keyframe_css_animation() {
+        $optimiser = $this->get_optimiser();
+
         $css = '.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url(\'[[pix:theme|fp/dnd_arrow]]\') no-repeat center;margin-left:-28px;}';
         $this->assertEquals($css, $optimiser->process($css));
 
@@ -954,11 +979,9 @@ CSS;
 .testtwo{color:#888;}
 .dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
 
-
 @media print {
   .test{background-color:#333;}
 }
-
 @keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
 @-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
 @-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
@@ -984,15 +1007,14 @@ CSS;
 CSS;
 
         $cssout = <<<CSS
-.dndupload-target,
-.filemanager.fp-select .fp-select-loading,
-.filemanager.fp-select.loading form{display:none;}
-.dndsupported .dndupload-ready .dndupload-target,
-.dndupload-uploading .dndupload-uploadinprogress,
-.filemanager.fp-select.loading .fp-select-loading{display:block;}
+.dndupload-target{display:none;}
+.dndsupported .dndupload-ready .dndupload-target{display:block;}
 .dndupload-uploadinprogress{display:none;text-align:center;}
+.dndupload-uploading .dndupload-uploadinprogress{display:block;}
 .dndupload-arrow{background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;width:56px;height:47px;position:absolute;margin-left:-28px;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}
-
+.filemanager.fp-select .fp-select-loading{display:none;}
+.filemanager.fp-select.loading .fp-select-loading{display:block;}
+.filemanager.fp-select.loading form{display:none;}
 
 @keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
 @-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
@@ -1005,7 +1027,9 @@ CSS;
      * Test media declarations
      * @param css_optimiser $optimiser
      */
-    public function try_media_rules(css_optimiser $optimiser) {
+    public function try_media_rules() {
+        $optimiser = $this->get_optimiser();
+
         $cssin = "@media print {\n  .test{background-color:#333;}\n}";
         $cssout = "@media print {\n  .test{background-color:#333;}\n}";
         $this->assertEquals($cssout, $optimiser->process($cssin));
@@ -1035,7 +1059,19 @@ CSS;
         $this->assertEquals($cssout, $optimiser->process($cssin));
 
         $cssin = "@media screen and (min-width:30px) {\n  #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:31px) {\n  #region-main-box{background-color:#FFF;}\n}";
-        $cssout = "@media screen and (min-width:30px) {\n  #region-main-box{background-color:#000;}\n}\n\n@media screen and (min-width:31px) {\n  #region-main-box{background-color:#FFF;}\n}";
+        $cssout = "@media screen and (min-width:30px) {\n  #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:31px) {\n  #region-main-box{background-color:#FFF;}\n}";
+        $this->assertEquals($cssout, $optimiser->process($cssin));
+    }
+
+
+    public function test_css_optimisation_ordering() {
+        $optimiser = $this->get_optimiser();
+
+        $css = '.test{display:none;} .dialogue{display:block;} .dialogue-hidden{display:none;}';
+        $this->assertEquals($css, $optimiser->process($css));
+
+        $cssin = '.test{display:none;} .dialogue-hidden{display:none;} .dialogue{display:block;}';
+        $cssout = '.test, .dialogue-hidden{display:none;} .dialogue{display:block;}';
         $this->assertEquals($cssout, $optimiser->process($cssin));
     }
 }
\ No newline at end of file