jenkins-bot has submitted this change and it was merged.

Change subject: General cleanup of CSSRenderer
......................................................................


General cleanup of CSSRenderer

* Add phpdoc comments
* Rename some variables to be a bit more clear for new readers
* Break up render() to make things more readable and reduce cyclomatic
  complexity

Change-Id: Iceeb1f6eb09b61efe6b81f359d28741f54fe88ad
---
M CSSRenderer.php
M tests/phpunit/CSSParseRenderTest.php
2 files changed, 104 insertions(+), 47 deletions(-)

Approvals:
  Jforrester: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/CSSRenderer.php b/CSSRenderer.php
index 70d2631..40d7614 100644
--- a/CSSRenderer.php
+++ b/CSSRenderer.php
@@ -1,6 +1,4 @@
 <?php
-
-
 /**
  * @file
  * @ingroup Extensions
@@ -13,85 +11,131 @@
  */
 class CSSRenderer {
 
-       private $bymedia;
+       /** @var array $byMedia */
+       private $byMedia;
 
        function __construct() {
-               $this->bymedia = [];
+               $this->byMedia = [];
        }
 
        /**
         * Adds (and merge) a parsed CSS tree to the render list.
         *
         * @param array $rules The parsed tree as created by CSSParser::rules()
-        * @param string $media Forcibly specified @media block selector.  
Normally unspecified
-        *  and defaults to the empty string.
+        * @param string $media Forcibly specified @media block selector.
         */
        function add( $rules, $media = '' ) {
-               if ( !array_key_exists( $media, $this->bymedia ) ) {
-                       $this->bymedia[$media] = [];
+               if ( !array_key_exists( $media, $this->byMedia ) ) {
+                       $this->byMedia[$media] = [];
                }
 
-               foreach ( $rules as $at ) {
-                       switch ( strtolower( $at['name'] ) ) {
+               foreach ( $rules as $rule ) {
+                       switch ( strtolower( $rule['name'] ) ) {
                                case '@media':
                                        if ( $media == '' ) {
-                                               $this->add( $at['rules'], 
"@media ".$at['text'] );
+                                               $this->add(
+                                                       $rule['rules'], "@media 
{$rule['text']}"
+                                               );
                                        }
                                        break;
                                case '':
-                                       $this->bymedia[$media] = array_merge( 
$this->bymedia[$media], $at['rules'] );
+                                       $this->byMedia[$media] = array_merge(
+                                               $this->byMedia[$media], 
$rule['rules']
+                                       );
                                        break;
                        }
                }
        }
 
        /**
-        * Renders the collected CSS trees into a string suitable for inclusion
+        * Render the collected CSS trees into a string suitable for inclusion
         * in a <style> tag.
         *
+        * @param array $functionWhitelist List of functions that are allowed
+        * @param array $propertyBlacklist List of properties that not allowed
         * @return string Rendered CSS
         */
-       function render( $functionWhitelist = [], $propertyBlacklist = [] ) {
+       function render(
+               array $functionWhitelist = [],
+               array $propertyBlacklist = []
+       ) {
+               // Normalize whitelist and blacklist values to lowercase
+               $functionWhitelist = array_map( 'strtolower', 
$functionWhitelist );
+               $propertyBlacklist = array_map( 'strtolower', 
$propertyBlacklist );
 
                $css = '';
-
-               foreach ( $this->bymedia as $at => $rules ) {
-                       if ( $at != '' ) {
-                               $css .= "$at {\n";
+               foreach ( $this->byMedia as $media => $rules ) {
+                       if ( $media !== '' ) {
+                               $css .= "{$media} {\n";
                        }
                        foreach ( $rules as $rule ) {
-                               if ( $rule
-                                       and array_key_exists( 'selectors', 
$rule )
-                                       and array_key_exists( 'decls', $rule ) )
-                               {
-                                       $css .= implode( ',', 
$rule['selectors'] ) . "{";
-                                       foreach ( $rule['decls'] as $key => 
$value ) {
-                                               if ( !in_array( strtolower( 
$key ), $propertyBlacklist ) ) {
-                                                       $blacklisted = false;
-                                                       foreach ( $value as 
$prop ) {
-                                                               if ( 
preg_match( '/^ ([^ \n\t]+) [ \n\t]* \( $/x', $prop, $match ) ) {
-                                                                       if ( 
!in_array( strtolower( $match[1] ), $functionWhitelist ) ) {
-                                                                               
$blacklisted = true;
-                                                                               
break;
-                                                                       }
-                                                               }
-                                                       }
-                                                       if ( !$blacklisted ) {
-                                                               $css .= "$key:" 
. implode( '', $value ) . ';';
-                                                       }
-                                               }
-                                       }
-                                       $css .= "} ";
+                               if ( $rule !== null ) {
+                                       $css .= $this->renderRule(
+                                               $rule, $functionWhitelist, 
$propertyBlacklist );
                                }
                        }
-                       if ( $at != '' ) {
-                               $css .= "} ";
+                       if ( $media !== '' ) {
+                               $css .= '} ';
                        }
                }
-
                return $css;
        }
 
+       /**
+        * Render a single rule.
+        *
+        * @param array $rule Parsed rule
+        * @param array $functionWhitelist List of functions that are allowed
+        * @param array $propertyBlacklist List of properties that not allowed
+        * @return string Rendered CSS
+        */
+       private function renderRule(
+               array $rule,
+               array $functionWhitelist,
+               array $propertyBlacklist
+       ) {
+               $css = '';
+               if ( $rule &&
+                       array_key_exists( 'selectors', $rule ) &&
+                       array_key_exists( 'decls', $rule )
+               ) {
+                       $css .= implode( ',', $rule['selectors'] ) . '{';
+                       foreach ( $rule['decls'] as $prop => $values ) {
+                               $css .= $this->renderDecl(
+                                       $prop, $values, $functionWhitelist, 
$propertyBlacklist );
+                       }
+                       $css .= '} ';
+               }
+               return $css;
+       }
+
+       /**
+        * Render a property declaration.
+        *
+        * @param string $prop Property name
+        * @param array $values Parsed property values
+        * @param array $functionWhitelist List of functions that are allowed
+        * @param array $propertyBlacklist List of properties that not allowed
+        * @return string Rendered CSS
+        */
+       private function renderDecl(
+               $prop,
+               array $values,
+               array $functionWhitelist,
+               array $propertyBlacklist
+       ) {
+               if ( in_array( strtolower( $prop ), $propertyBlacklist ) ) {
+                       // Property is blacklisted
+                       return '';
+               }
+               foreach ( $values as $value ) {
+                       if ( preg_match( '/^ (\S+) \s* \( $/x', $value, $match 
) ) {
+                               if ( !in_array( strtolower( $match[1] ), 
$functionWhitelist ) ) {
+                                       // Function is blacklisted
+                                       return '';
+                               }
+                       }
+               }
+               return $prop . ':' . implode( '', $values ) . ';';
+       }
 }
-
-
diff --git a/tests/phpunit/CSSParseRenderTest.php 
b/tests/phpunit/CSSParseRenderTest.php
index c6ac683..251d721 100644
--- a/tests/phpunit/CSSParseRenderTest.php
+++ b/tests/phpunit/CSSParseRenderTest.php
@@ -19,8 +19,8 @@
                $expect,
                $source,
                $baseSelector = '.X ',
-               $functionWhitelist = [ 'whitelisted' ],
-               $propertyBlacklist = [ '-evil' ]
+               array $functionWhitelist = [ 'whitelisted' ],
+               array $propertyBlacklist = [ '-evil' ]
        ) {
                $tree = new CSSParser( $source );
                $rules = $tree->rules( $baseSelector );
@@ -239,6 +239,19 @@
        ;prop3          :not/**/whitelisted( val3 );}
 CSS
                        ],
+                       'Whitelist normalized' => [
+                               'expect' => '.foo {bar:whitelisted(1);} ',
+                               'css' => '.foo { bar: whitelisted(1); baz: 
url(1); }',
+                               'prefix' => '',
+                               'whitelist' => [ 'WHITELISTED' ],
+                       ],
+                       'Blacklist normalized' => [
+                               'expect' => '.foo {baz:1;} ',
+                               'css' => '.foo { blacklisted: 1; baz: 1; }',
+                               'prefix' => '',
+                               'whitelist' => [],
+                               'blacklist' => [ 'BLACKLISTED' ],
+                       ],
                ];
        }
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/284629
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Iceeb1f6eb09b61efe6b81f359d28741f54fe88ad
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/extensions/TemplateStyles
Gerrit-Branch: master
Gerrit-Owner: BryanDavis <bda...@wikimedia.org>
Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to