Deal with old libxml incompatibilities.
[htmlpurifier.git] / library / HTMLPurifier / CSSDefinition.php
blob47dfd1f66609f95e4638ed585a4dc3bda21a9e92
1 <?php
3 /**
4 * Defines allowed CSS attributes and what their values are.
5 * @see HTMLPurifier_HTMLDefinition
6 */
7 class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
10 public $type = 'CSS';
12 /**
13 * Assoc array of attribute name to definition object.
14 * @type HTMLPurifier_AttrDef[]
16 public $info = array();
18 /**
19 * Constructs the info array. The meat of this class.
20 * @param HTMLPurifier_Config $config
22 protected function doSetup($config)
24 $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum(
25 array('left', 'right', 'center', 'justify'),
26 false
29 $border_style =
30 $this->info['border-bottom-style'] =
31 $this->info['border-right-style'] =
32 $this->info['border-left-style'] =
33 $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum(
34 array(
35 'none',
36 'hidden',
37 'dotted',
38 'dashed',
39 'solid',
40 'double',
41 'groove',
42 'ridge',
43 'inset',
44 'outset'
46 false
49 $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style);
51 $this->info['clear'] = new HTMLPurifier_AttrDef_Enum(
52 array('none', 'left', 'right', 'both'),
53 false
55 $this->info['float'] = new HTMLPurifier_AttrDef_Enum(
56 array('none', 'left', 'right'),
57 false
59 $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum(
60 array('normal', 'italic', 'oblique'),
61 false
63 $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum(
64 array('normal', 'small-caps'),
65 false
68 $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite(
69 array(
70 new HTMLPurifier_AttrDef_Enum(array('none')),
71 new HTMLPurifier_AttrDef_CSS_URI()
75 $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum(
76 array('inside', 'outside'),
77 false
79 $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum(
80 array(
81 'disc',
82 'circle',
83 'square',
84 'decimal',
85 'lower-roman',
86 'upper-roman',
87 'lower-alpha',
88 'upper-alpha',
89 'none'
91 false
93 $this->info['list-style-image'] = $uri_or_none;
95 $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config);
97 $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum(
98 array('capitalize', 'uppercase', 'lowercase', 'none'),
99 false
101 $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color();
103 $this->info['background-image'] = $uri_or_none;
104 $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum(
105 array('repeat', 'repeat-x', 'repeat-y', 'no-repeat')
107 $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum(
108 array('scroll', 'fixed')
110 $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition();
112 $border_color =
113 $this->info['border-top-color'] =
114 $this->info['border-bottom-color'] =
115 $this->info['border-left-color'] =
116 $this->info['border-right-color'] =
117 $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(
118 array(
119 new HTMLPurifier_AttrDef_Enum(array('transparent')),
120 new HTMLPurifier_AttrDef_CSS_Color()
124 $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
126 $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
128 $border_width =
129 $this->info['border-top-width'] =
130 $this->info['border-bottom-width'] =
131 $this->info['border-left-width'] =
132 $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(
133 array(
134 new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')),
135 new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative
139 $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
141 $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
142 array(
143 new HTMLPurifier_AttrDef_Enum(array('normal')),
144 new HTMLPurifier_AttrDef_CSS_Length()
148 $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(
149 array(
150 new HTMLPurifier_AttrDef_Enum(array('normal')),
151 new HTMLPurifier_AttrDef_CSS_Length()
155 $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(
156 array(
157 new HTMLPurifier_AttrDef_Enum(
158 array(
159 'xx-small',
160 'x-small',
161 'small',
162 'medium',
163 'large',
164 'x-large',
165 'xx-large',
166 'larger',
167 'smaller'
170 new HTMLPurifier_AttrDef_CSS_Percentage(),
171 new HTMLPurifier_AttrDef_CSS_Length()
175 $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(
176 array(
177 new HTMLPurifier_AttrDef_Enum(array('normal')),
178 new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives
179 new HTMLPurifier_AttrDef_CSS_Length('0'),
180 new HTMLPurifier_AttrDef_CSS_Percentage(true)
184 $margin =
185 $this->info['margin-top'] =
186 $this->info['margin-bottom'] =
187 $this->info['margin-left'] =
188 $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
189 array(
190 new HTMLPurifier_AttrDef_CSS_Length(),
191 new HTMLPurifier_AttrDef_CSS_Percentage(),
192 new HTMLPurifier_AttrDef_Enum(array('auto'))
196 $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
198 // non-negative
199 $padding =
200 $this->info['padding-top'] =
201 $this->info['padding-bottom'] =
202 $this->info['padding-left'] =
203 $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(
204 array(
205 new HTMLPurifier_AttrDef_CSS_Length('0'),
206 new HTMLPurifier_AttrDef_CSS_Percentage(true)
210 $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
212 $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
213 array(
214 new HTMLPurifier_AttrDef_CSS_Length(),
215 new HTMLPurifier_AttrDef_CSS_Percentage()
219 $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(
220 array(
221 new HTMLPurifier_AttrDef_CSS_Length('0'),
222 new HTMLPurifier_AttrDef_CSS_Percentage(true),
223 new HTMLPurifier_AttrDef_Enum(array('auto'))
226 $max = $config->get('CSS.MaxImgLength');
228 $this->info['min-width'] =
229 $this->info['max-width'] =
230 $this->info['min-height'] =
231 $this->info['max-height'] =
232 $this->info['width'] =
233 $this->info['height'] =
234 $max === null ?
235 $trusted_wh :
236 new HTMLPurifier_AttrDef_Switch(
237 'img',
238 // For img tags:
239 new HTMLPurifier_AttrDef_CSS_Composite(
240 array(
241 new HTMLPurifier_AttrDef_CSS_Length('0', $max),
242 new HTMLPurifier_AttrDef_Enum(array('auto'))
245 // For everyone else:
246 $trusted_wh
249 $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
251 $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
253 // this could use specialized code
254 $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
255 array(
256 'normal',
257 'bold',
258 'bolder',
259 'lighter',
260 '100',
261 '200',
262 '300',
263 '400',
264 '500',
265 '600',
266 '700',
267 '800',
268 '900'
270 false
273 // MUST be called after other font properties, as it references
274 // a CSSDefinition object
275 $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
277 // same here
278 $this->info['border'] =
279 $this->info['border-bottom'] =
280 $this->info['border-top'] =
281 $this->info['border-left'] =
282 $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
284 $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
285 array('collapse', 'separate')
288 $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
289 array('top', 'bottom')
292 $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
293 array('auto', 'fixed')
296 $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
297 array(
298 new HTMLPurifier_AttrDef_Enum(
299 array(
300 'baseline',
301 'sub',
302 'super',
303 'top',
304 'text-top',
305 'middle',
306 'bottom',
307 'text-bottom'
310 new HTMLPurifier_AttrDef_CSS_Length(),
311 new HTMLPurifier_AttrDef_CSS_Percentage()
315 $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
317 // These CSS properties don't work on many browsers, but we live
318 // in THE FUTURE!
319 $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
320 array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
323 if ($config->get('CSS.Proprietary')) {
324 $this->doSetupProprietary($config);
327 if ($config->get('CSS.AllowTricky')) {
328 $this->doSetupTricky($config);
331 if ($config->get('CSS.Trusted')) {
332 $this->doSetupTrusted($config);
335 $allow_important = $config->get('CSS.AllowImportant');
336 // wrap all attr-defs with decorator that handles !important
337 foreach ($this->info as $k => $v) {
338 $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
341 $this->setupConfigStuff($config);
345 * @param HTMLPurifier_Config $config
347 protected function doSetupProprietary($config)
349 // Internet Explorer only scrollbar colors
350 $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
351 $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
352 $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
353 $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
354 $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
355 $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
357 // vendor specific prefixes of opacity
358 $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
359 $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
361 // only opacity, for now
362 $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
364 // more CSS3
365 $this->info['page-break-after'] =
366 $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
367 array(
368 'auto',
369 'always',
370 'avoid',
371 'left',
372 'right'
375 $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
377 $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
378 array(
379 new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
380 new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
383 $this->info['border-top-left-radius'] =
384 $this->info['border-top-right-radius'] =
385 $this->info['border-bottom-right-radius'] =
386 $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
387 // TODO: support SLASH syntax
388 $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
393 * @param HTMLPurifier_Config $config
395 protected function doSetupTricky($config)
397 $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
398 array(
399 'inline',
400 'block',
401 'list-item',
402 'run-in',
403 'compact',
404 'marker',
405 'table',
406 'inline-block',
407 'inline-table',
408 'table-row-group',
409 'table-header-group',
410 'table-footer-group',
411 'table-row',
412 'table-column-group',
413 'table-column',
414 'table-cell',
415 'table-caption',
416 'none'
419 $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
420 array('visible', 'hidden', 'collapse')
422 $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
423 $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
427 * @param HTMLPurifier_Config $config
429 protected function doSetupTrusted($config)
431 $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
432 array('static', 'relative', 'absolute', 'fixed')
434 $this->info['top'] =
435 $this->info['left'] =
436 $this->info['right'] =
437 $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
438 array(
439 new HTMLPurifier_AttrDef_CSS_Length(),
440 new HTMLPurifier_AttrDef_CSS_Percentage(),
441 new HTMLPurifier_AttrDef_Enum(array('auto')),
444 $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
445 array(
446 new HTMLPurifier_AttrDef_Integer(),
447 new HTMLPurifier_AttrDef_Enum(array('auto')),
453 * Performs extra config-based processing. Based off of
454 * HTMLPurifier_HTMLDefinition.
455 * @param HTMLPurifier_Config $config
456 * @todo Refactor duplicate elements into common class (probably using
457 * composition, not inheritance).
459 protected function setupConfigStuff($config)
461 // setup allowed elements
462 $support = "(for information on implementing this, see the " .
463 "support forums) ";
464 $allowed_properties = $config->get('CSS.AllowedProperties');
465 if ($allowed_properties !== null) {
466 foreach ($this->info as $name => $d) {
467 if (!isset($allowed_properties[$name])) {
468 unset($this->info[$name]);
470 unset($allowed_properties[$name]);
472 // emit errors
473 foreach ($allowed_properties as $name => $d) {
474 // :TODO: Is this htmlspecialchars() call really necessary?
475 $name = htmlspecialchars($name);
476 trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
480 $forbidden_properties = $config->get('CSS.ForbiddenProperties');
481 if ($forbidden_properties !== null) {
482 foreach ($this->info as $name => $d) {
483 if (isset($forbidden_properties[$name])) {
484 unset($this->info[$name]);
491 // vim: et sw=4 sts=4