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