File indexing completed on 2024-12-22 05:36:21

0001 <?php
0002 
0003 /**
0004  * Defines allowed CSS attributes and what their values are.
0005  * @see HTMLPurifier_HTMLDefinition
0006  */
0007 class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
0008 {
0009 
0010     public $type = 'CSS';
0011 
0012     /**
0013      * Assoc array of attribute name to definition object.
0014      * @type HTMLPurifier_AttrDef[]
0015      */
0016     public $info = array();
0017 
0018     /**
0019      * Constructs the info array.  The meat of this class.
0020      * @param HTMLPurifier_Config $config
0021      */
0022     protected function doSetup($config)
0023     {
0024         $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
0025             array('left', 'right', 'center', 'justify'),
0026             false
0027         );
0028 
0029         $border_style =
0030             $this->info['border-bottom-style'] =
0031             $this->info['border-right-style'] =
0032             $this->info['border-left-style'] =
0033             $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
0034                 array(
0035                     'none',
0036                     'hidden',
0037                     'dotted',
0038                     'dashed',
0039                     'solid',
0040                     'double',
0041                     'groove',
0042                     'ridge',
0043                     'inset',
0044                     'outset'
0045                 ),
0046                 false
0047             );
0048 
0049         $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
0050 
0051         $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
0052             array('none', 'left', 'right', 'both'),
0053             false
0054         );
0055         $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
0056             array('none', 'left', 'right'),
0057             false
0058         );
0059         $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
0060             array('normal', 'italic', 'oblique'),
0061             false
0062         );
0063         $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
0064             array('normal', 'small-caps'),
0065             false
0066         );
0067 
0068         $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
0069             array(
0070                 new HTMLPurifier_AttrDef_Enum(array('none')),
0071                 new HTMLPurifier_AttrDef_CSS_URI()
0072             )
0073         );
0074 
0075         $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
0076             array('inside', 'outside'),
0077             false
0078         );
0079         $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
0080             array(
0081                 'disc',
0082                 'circle',
0083                 'square',
0084                 'decimal',
0085                 'lower-roman',
0086                 'upper-roman',
0087                 'lower-alpha',
0088                 'upper-alpha',
0089                 'none'
0090             ),
0091             false
0092         );
0093         $this->info['list-style-image'] = $uri_or_none;
0094 
0095         $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
0096 
0097         $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
0098             array('capitalize', 'uppercase', 'lowercase', 'none'),
0099             false
0100         );
0101         $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
0102 
0103         $this->info['background-image'] = $uri_or_none;
0104         $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
0105             array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
0106         );
0107         $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
0108             array('scroll', 'fixed')
0109         );
0110         $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
0111 
0112         $border_color =
0113             $this->info['border-top-color'] =
0114             $this->info['border-bottom-color'] =
0115             $this->info['border-left-color'] =
0116             $this->info['border-right-color'] =
0117             $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(
0118                 array(
0119                     new HTMLPurifier_AttrDef_Enum(array('transparent')),
0120                     new HTMLPurifier_AttrDef_CSS_Color()
0121                 )
0122             );
0123 
0124         $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
0125 
0126         $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
0127 
0128         $border_width =
0129             $this->info['border-top-width'] =
0130             $this->info['border-bottom-width'] =
0131             $this->info['border-left-width'] =
0132             $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(
0133                 array(
0134                     new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
0135                     new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
0136                 )
0137             );
0138 
0139         $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
0140 
0141         $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
0142             array(
0143                 new HTMLPurifier_AttrDef_Enum(array('normal')),
0144                 new HTMLPurifier_AttrDef_CSS_Length()
0145             )
0146         );
0147 
0148         $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
0149             array(
0150                 new HTMLPurifier_AttrDef_Enum(array('normal')),
0151                 new HTMLPurifier_AttrDef_CSS_Length()
0152             )
0153         );
0154 
0155         $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
0156             array(
0157                 new HTMLPurifier_AttrDef_Enum(
0158                     array(
0159                         'xx-small',
0160                         'x-small',
0161                         'small',
0162                         'medium',
0163                         'large',
0164                         'x-large',
0165                         'xx-large',
0166                         'larger',
0167                         'smaller'
0168                     )
0169                 ),
0170                 new HTMLPurifier_AttrDef_CSS_Percentage(),
0171                 new HTMLPurifier_AttrDef_CSS_Length()
0172             )
0173         );
0174 
0175         $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(
0176             array(
0177                 new HTMLPurifier_AttrDef_Enum(array('normal')),
0178                 new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
0179                 new HTMLPurifier_AttrDef_CSS_Length('0'),
0180                 new HTMLPurifier_AttrDef_CSS_Percentage(true)
0181             )
0182         );
0183 
0184         $margin =
0185             $this->info['margin-top'] =
0186             $this->info['margin-bottom'] =
0187             $this->info['margin-left'] =
0188             $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
0189                 array(
0190                     new HTMLPurifier_AttrDef_CSS_Length(),
0191                     new HTMLPurifier_AttrDef_CSS_Percentage(),
0192                     new HTMLPurifier_AttrDef_Enum(array('auto'))
0193                 )
0194             );
0195 
0196         $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
0197 
0198         // non-negative
0199         $padding =
0200             $this->info['padding-top'] =
0201             $this->info['padding-bottom'] =
0202             $this->info['padding-left'] =
0203             $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
0204                 array(
0205                     new HTMLPurifier_AttrDef_CSS_Length('0'),
0206                     new HTMLPurifier_AttrDef_CSS_Percentage(true)
0207                 )
0208             );
0209 
0210         $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
0211 
0212         $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
0213             array(
0214                 new HTMLPurifier_AttrDef_CSS_Length(),
0215                 new HTMLPurifier_AttrDef_CSS_Percentage()
0216             )
0217         );
0218 
0219         $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(
0220             array(
0221                 new HTMLPurifier_AttrDef_CSS_Length('0'),
0222                 new HTMLPurifier_AttrDef_CSS_Percentage(true),
0223                 new HTMLPurifier_AttrDef_Enum(array('auto'))
0224             )
0225         );
0226         $max = $config->get('CSS.MaxImgLength');
0227 
0228         $this->info['min-width'] =
0229         $this->info['max-width'] =
0230         $this->info['min-height'] =
0231         $this->info['max-height'] =
0232         $this->info['width'] =
0233         $this->info['height'] =
0234             $max === null ?
0235                 $trusted_wh :
0236                 new HTMLPurifier_AttrDef_Switch(
0237                     'img',
0238                     // For img tags:
0239                     new HTMLPurifier_AttrDef_CSS_Composite(
0240                         array(
0241                             new HTMLPurifier_AttrDef_CSS_Length('0', $max),
0242                             new HTMLPurifier_AttrDef_Enum(array('auto'))
0243                         )
0244                     ),
0245                     // For everyone else:
0246                     $trusted_wh
0247                 );
0248 
0249         $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
0250 
0251         $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
0252 
0253         // this could use specialized code
0254         $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
0255             array(
0256                 'normal',
0257                 'bold',
0258                 'bolder',
0259                 'lighter',
0260                 '100',
0261                 '200',
0262                 '300',
0263                 '400',
0264                 '500',
0265                 '600',
0266                 '700',
0267                 '800',
0268                 '900'
0269             ),
0270             false
0271         );
0272 
0273         // MUST be called after other font properties, as it references
0274         // a CSSDefinition object
0275         $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
0276 
0277         // same here
0278         $this->info['border'] =
0279         $this->info['border-bottom'] =
0280         $this->info['border-top'] =
0281         $this->info['border-left'] =
0282         $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
0283 
0284         $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
0285             array('collapse', 'separate')
0286         );
0287 
0288         $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
0289             array('top', 'bottom')
0290         );
0291 
0292         $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
0293             array('auto', 'fixed')
0294         );
0295 
0296         $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
0297             array(
0298                 new HTMLPurifier_AttrDef_Enum(
0299                     array(
0300                         'baseline',
0301                         'sub',
0302                         'super',
0303                         'top',
0304                         'text-top',
0305                         'middle',
0306                         'bottom',
0307                         'text-bottom'
0308                     )
0309                 ),
0310                 new HTMLPurifier_AttrDef_CSS_Length(),
0311                 new HTMLPurifier_AttrDef_CSS_Percentage()
0312             )
0313         );
0314 
0315         $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
0316 
0317         // These CSS properties don't work on many browsers, but we live
0318         // in THE FUTURE!
0319         $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
0320             array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
0321         );
0322 
0323         if ($config->get('CSS.Proprietary')) {
0324             $this->doSetupProprietary($config);
0325         }
0326 
0327         if ($config->get('CSS.AllowTricky')) {
0328             $this->doSetupTricky($config);
0329         }
0330 
0331         if ($config->get('CSS.Trusted')) {
0332             $this->doSetupTrusted($config);
0333         }
0334 
0335         $allow_important = $config->get('CSS.AllowImportant');
0336         // wrap all attr-defs with decorator that handles !important
0337         foreach ($this->info as $k => $v) {
0338             $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
0339         }
0340 
0341         $this->setupConfigStuff($config);
0342     }
0343 
0344     /**
0345      * @param HTMLPurifier_Config $config
0346      */
0347     protected function doSetupProprietary($config)
0348     {
0349         // Internet Explorer only scrollbar colors
0350         $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
0351         $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
0352         $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
0353         $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
0354         $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
0355         $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
0356 
0357         // vendor specific prefixes of opacity
0358         $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
0359         $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
0360 
0361         // only opacity, for now
0362         $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
0363 
0364         // more CSS3
0365         $this->info['page-break-after'] =
0366         $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
0367             array(
0368                 'auto',
0369                 'always',
0370                 'avoid',
0371                 'left',
0372                 'right'
0373             )
0374         );
0375         $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
0376 
0377         $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
0378             array(
0379                 new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
0380                 new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
0381             ));
0382 
0383         $this->info['border-top-left-radius'] =
0384         $this->info['border-top-right-radius'] =
0385         $this->info['border-bottom-right-radius'] =
0386         $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
0387         // TODO: support SLASH syntax
0388         $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
0389 
0390     }
0391 
0392     /**
0393      * @param HTMLPurifier_Config $config
0394      */
0395     protected function doSetupTricky($config)
0396     {
0397         $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
0398             array(
0399                 'inline',
0400                 'block',
0401                 'list-item',
0402                 'run-in',
0403                 'compact',
0404                 'marker',
0405                 'table',
0406                 'inline-block',
0407                 'inline-table',
0408                 'table-row-group',
0409                 'table-header-group',
0410                 'table-footer-group',
0411                 'table-row',
0412                 'table-column-group',
0413                 'table-column',
0414                 'table-cell',
0415                 'table-caption',
0416                 'none'
0417             )
0418         );
0419         $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
0420             array('visible', 'hidden', 'collapse')
0421         );
0422         $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
0423         $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
0424     }
0425 
0426     /**
0427      * @param HTMLPurifier_Config $config
0428      */
0429     protected function doSetupTrusted($config)
0430     {
0431         $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
0432             array('static', 'relative', 'absolute', 'fixed')
0433         );
0434         $this->info['top'] =
0435         $this->info['left'] =
0436         $this->info['right'] =
0437         $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
0438             array(
0439                 new HTMLPurifier_AttrDef_CSS_Length(),
0440                 new HTMLPurifier_AttrDef_CSS_Percentage(),
0441                 new HTMLPurifier_AttrDef_Enum(array('auto')),
0442             )
0443         );
0444         $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
0445             array(
0446                 new HTMLPurifier_AttrDef_Integer(),
0447                 new HTMLPurifier_AttrDef_Enum(array('auto')),
0448             )
0449         );
0450     }
0451 
0452     /**
0453      * Performs extra config-based processing. Based off of
0454      * HTMLPurifier_HTMLDefinition.
0455      * @param HTMLPurifier_Config $config
0456      * @todo Refactor duplicate elements into common class (probably using
0457      *       composition, not inheritance).
0458      */
0459     protected function setupConfigStuff($config)
0460     {
0461         // setup allowed elements
0462         $support = "(for information on implementing this, see the " .
0463             "support forums) ";
0464         $allowed_properties = $config->get('CSS.AllowedProperties');
0465         if ($allowed_properties !== null) {
0466             foreach ($this->info as $name => $d) {
0467                 if (!isset($allowed_properties[$name])) {
0468                     unset($this->info[$name]);
0469                 }
0470                 unset($allowed_properties[$name]);
0471             }
0472             // emit errors
0473             foreach ($allowed_properties as $name => $d) {
0474                 // :TODO: Is this htmlspecialchars() call really necessary?
0475                 $name = htmlspecialchars($name);
0476                 trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
0477             }
0478         }
0479 
0480         $forbidden_properties = $config->get('CSS.ForbiddenProperties');
0481         if ($forbidden_properties !== null) {
0482             foreach ($this->info as $name => $d) {
0483                 if (isset($forbidden_properties[$name])) {
0484                     unset($this->info[$name]);
0485                 }
0486             }
0487         }
0488     }
0489 }
0490 
0491 // vim: et sw=4 sts=4