1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition |
8: | { |
9: | |
10: | public $type = 'CSS'; |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | public $info = array(); |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | protected function doSetup($config) |
23: | { |
24: | $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum( |
25: | array('left', 'right', 'center', 'justify'), |
26: | false |
27: | ); |
28: | |
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' |
45: | ), |
46: | false |
47: | ); |
48: | |
49: | $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); |
50: | |
51: | $this->info['clear'] = new HTMLPurifier_AttrDef_Enum( |
52: | array('none', 'left', 'right', 'both'), |
53: | false |
54: | ); |
55: | $this->info['float'] = new HTMLPurifier_AttrDef_Enum( |
56: | array('none', 'left', 'right'), |
57: | false |
58: | ); |
59: | $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( |
60: | array('normal', 'italic', 'oblique'), |
61: | false |
62: | ); |
63: | $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( |
64: | array('normal', 'small-caps'), |
65: | false |
66: | ); |
67: | |
68: | $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( |
69: | array( |
70: | new HTMLPurifier_AttrDef_Enum(array('none')), |
71: | new HTMLPurifier_AttrDef_CSS_URI() |
72: | ) |
73: | ); |
74: | |
75: | $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( |
76: | array('inside', 'outside'), |
77: | false |
78: | ); |
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' |
90: | ), |
91: | false |
92: | ); |
93: | $this->info['list-style-image'] = $uri_or_none; |
94: | |
95: | $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); |
96: | |
97: | $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( |
98: | array('capitalize', 'uppercase', 'lowercase', 'none'), |
99: | false |
100: | ); |
101: | $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
102: | |
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') |
106: | ); |
107: | $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( |
108: | array('scroll', 'fixed') |
109: | ); |
110: | $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); |
111: | |
112: | $this->info['background-size'] = new HTMLPurifier_AttrDef_CSS_Composite( |
113: | array( |
114: | new HTMLPurifier_AttrDef_Enum( |
115: | array( |
116: | 'auto', |
117: | 'cover', |
118: | 'contain', |
119: | 'initial', |
120: | 'inherit', |
121: | ) |
122: | ), |
123: | new HTMLPurifier_AttrDef_CSS_Percentage(), |
124: | new HTMLPurifier_AttrDef_CSS_Length() |
125: | ) |
126: | ); |
127: | |
128: | $border_color = |
129: | $this->info['border-top-color'] = |
130: | $this->info['border-bottom-color'] = |
131: | $this->info['border-left-color'] = |
132: | $this->info['border-right-color'] = |
133: | $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite( |
134: | array( |
135: | new HTMLPurifier_AttrDef_Enum(array('transparent')), |
136: | new HTMLPurifier_AttrDef_CSS_Color() |
137: | ) |
138: | ); |
139: | |
140: | $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); |
141: | |
142: | $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); |
143: | |
144: | $border_width = |
145: | $this->info['border-top-width'] = |
146: | $this->info['border-bottom-width'] = |
147: | $this->info['border-left-width'] = |
148: | $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite( |
149: | array( |
150: | new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), |
151: | new HTMLPurifier_AttrDef_CSS_Length('0') |
152: | ) |
153: | ); |
154: | |
155: | $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); |
156: | |
157: | $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite( |
158: | array( |
159: | new HTMLPurifier_AttrDef_Enum(array('normal')), |
160: | new HTMLPurifier_AttrDef_CSS_Length() |
161: | ) |
162: | ); |
163: | |
164: | $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite( |
165: | array( |
166: | new HTMLPurifier_AttrDef_Enum(array('normal')), |
167: | new HTMLPurifier_AttrDef_CSS_Length() |
168: | ) |
169: | ); |
170: | |
171: | $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite( |
172: | array( |
173: | new HTMLPurifier_AttrDef_Enum( |
174: | array( |
175: | 'xx-small', |
176: | 'x-small', |
177: | 'small', |
178: | 'medium', |
179: | 'large', |
180: | 'x-large', |
181: | 'xx-large', |
182: | 'larger', |
183: | 'smaller' |
184: | ) |
185: | ), |
186: | new HTMLPurifier_AttrDef_CSS_Percentage(), |
187: | new HTMLPurifier_AttrDef_CSS_Length() |
188: | ) |
189: | ); |
190: | |
191: | $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite( |
192: | array( |
193: | new HTMLPurifier_AttrDef_Enum(array('normal')), |
194: | new HTMLPurifier_AttrDef_CSS_Number(true), |
195: | new HTMLPurifier_AttrDef_CSS_Length('0'), |
196: | new HTMLPurifier_AttrDef_CSS_Percentage(true) |
197: | ) |
198: | ); |
199: | |
200: | $margin = |
201: | $this->info['margin-top'] = |
202: | $this->info['margin-bottom'] = |
203: | $this->info['margin-left'] = |
204: | $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite( |
205: | array( |
206: | new HTMLPurifier_AttrDef_CSS_Length(), |
207: | new HTMLPurifier_AttrDef_CSS_Percentage(), |
208: | new HTMLPurifier_AttrDef_Enum(array('auto')) |
209: | ) |
210: | ); |
211: | |
212: | $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); |
213: | |
214: | |
215: | $padding = |
216: | $this->info['padding-top'] = |
217: | $this->info['padding-bottom'] = |
218: | $this->info['padding-left'] = |
219: | $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite( |
220: | array( |
221: | new HTMLPurifier_AttrDef_CSS_Length('0'), |
222: | new HTMLPurifier_AttrDef_CSS_Percentage(true) |
223: | ) |
224: | ); |
225: | |
226: | $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); |
227: | |
228: | $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite( |
229: | array( |
230: | new HTMLPurifier_AttrDef_CSS_Length(), |
231: | new HTMLPurifier_AttrDef_CSS_Percentage() |
232: | ) |
233: | ); |
234: | |
235: | $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite( |
236: | array( |
237: | new HTMLPurifier_AttrDef_CSS_Length('0'), |
238: | new HTMLPurifier_AttrDef_CSS_Percentage(true), |
239: | new HTMLPurifier_AttrDef_Enum(array('auto', 'initial', 'inherit')) |
240: | ) |
241: | ); |
242: | $trusted_min_wh = new HTMLPurifier_AttrDef_CSS_Composite( |
243: | array( |
244: | new HTMLPurifier_AttrDef_CSS_Length('0'), |
245: | new HTMLPurifier_AttrDef_CSS_Percentage(true), |
246: | new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')) |
247: | ) |
248: | ); |
249: | $trusted_max_wh = new HTMLPurifier_AttrDef_CSS_Composite( |
250: | array( |
251: | new HTMLPurifier_AttrDef_CSS_Length('0'), |
252: | new HTMLPurifier_AttrDef_CSS_Percentage(true), |
253: | new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')) |
254: | ) |
255: | ); |
256: | $max = $config->get('CSS.MaxImgLength'); |
257: | |
258: | $this->info['width'] = |
259: | $this->info['height'] = |
260: | $max === null ? |
261: | $trusted_wh : |
262: | new HTMLPurifier_AttrDef_Switch( |
263: | 'img', |
264: | |
265: | new HTMLPurifier_AttrDef_CSS_Composite( |
266: | array( |
267: | new HTMLPurifier_AttrDef_CSS_Length('0', $max), |
268: | new HTMLPurifier_AttrDef_Enum(array('auto')) |
269: | ) |
270: | ), |
271: | |
272: | $trusted_wh |
273: | ); |
274: | $this->info['min-width'] = |
275: | $this->info['min-height'] = |
276: | $max === null ? |
277: | $trusted_min_wh : |
278: | new HTMLPurifier_AttrDef_Switch( |
279: | 'img', |
280: | |
281: | new HTMLPurifier_AttrDef_CSS_Composite( |
282: | array( |
283: | new HTMLPurifier_AttrDef_CSS_Length('0', $max), |
284: | new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit')) |
285: | ) |
286: | ), |
287: | |
288: | $trusted_min_wh |
289: | ); |
290: | $this->info['max-width'] = |
291: | $this->info['max-height'] = |
292: | $max === null ? |
293: | $trusted_max_wh : |
294: | new HTMLPurifier_AttrDef_Switch( |
295: | 'img', |
296: | |
297: | new HTMLPurifier_AttrDef_CSS_Composite( |
298: | array( |
299: | new HTMLPurifier_AttrDef_CSS_Length('0', $max), |
300: | new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit')) |
301: | ) |
302: | ), |
303: | |
304: | $trusted_max_wh |
305: | ); |
306: | |
307: | $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); |
308: | |
309: | $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); |
310: | |
311: | |
312: | $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( |
313: | array( |
314: | 'normal', |
315: | 'bold', |
316: | 'bolder', |
317: | 'lighter', |
318: | '100', |
319: | '200', |
320: | '300', |
321: | '400', |
322: | '500', |
323: | '600', |
324: | '700', |
325: | '800', |
326: | '900' |
327: | ), |
328: | false |
329: | ); |
330: | |
331: | |
332: | |
333: | $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config); |
334: | |
335: | |
336: | $this->info['border'] = |
337: | $this->info['border-bottom'] = |
338: | $this->info['border-top'] = |
339: | $this->info['border-left'] = |
340: | $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config); |
341: | |
342: | $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum( |
343: | array('collapse', 'separate') |
344: | ); |
345: | |
346: | $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum( |
347: | array('top', 'bottom') |
348: | ); |
349: | |
350: | $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum( |
351: | array('auto', 'fixed') |
352: | ); |
353: | |
354: | $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite( |
355: | array( |
356: | new HTMLPurifier_AttrDef_Enum( |
357: | array( |
358: | 'baseline', |
359: | 'sub', |
360: | 'super', |
361: | 'top', |
362: | 'text-top', |
363: | 'middle', |
364: | 'bottom', |
365: | 'text-bottom' |
366: | ) |
367: | ), |
368: | new HTMLPurifier_AttrDef_CSS_Length(), |
369: | new HTMLPurifier_AttrDef_CSS_Percentage() |
370: | ) |
371: | ); |
372: | |
373: | $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); |
374: | |
375: | |
376: | |
377: | $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum( |
378: | array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line') |
379: | ); |
380: | |
381: | if ($config->get('CSS.Proprietary')) { |
382: | $this->doSetupProprietary($config); |
383: | } |
384: | |
385: | if ($config->get('CSS.AllowTricky')) { |
386: | $this->doSetupTricky($config); |
387: | } |
388: | |
389: | if ($config->get('CSS.Trusted')) { |
390: | $this->doSetupTrusted($config); |
391: | } |
392: | |
393: | $allow_important = $config->get('CSS.AllowImportant'); |
394: | |
395: | foreach ($this->info as $k => $v) { |
396: | $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); |
397: | } |
398: | |
399: | $this->setupConfigStuff($config); |
400: | } |
401: | |
402: | |
403: | |
404: | |
405: | protected function doSetupProprietary($config) |
406: | { |
407: | |
408: | $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
409: | $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
410: | $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
411: | $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
412: | $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
413: | $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
414: | |
415: | |
416: | $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
417: | $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
418: | |
419: | |
420: | $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); |
421: | |
422: | |
423: | $this->info['page-break-after'] = |
424: | $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum( |
425: | array( |
426: | 'auto', |
427: | 'always', |
428: | 'avoid', |
429: | 'left', |
430: | 'right' |
431: | ) |
432: | ); |
433: | $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid')); |
434: | |
435: | $border_radius = new HTMLPurifier_AttrDef_CSS_Composite( |
436: | array( |
437: | new HTMLPurifier_AttrDef_CSS_Percentage(true), |
438: | new HTMLPurifier_AttrDef_CSS_Length('0') |
439: | )); |
440: | |
441: | $this->info['border-top-left-radius'] = |
442: | $this->info['border-top-right-radius'] = |
443: | $this->info['border-bottom-right-radius'] = |
444: | $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2); |
445: | |
446: | $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4); |
447: | |
448: | } |
449: | |
450: | |
451: | |
452: | |
453: | protected function doSetupTricky($config) |
454: | { |
455: | $this->info['display'] = new HTMLPurifier_AttrDef_Enum( |
456: | array( |
457: | 'inline', |
458: | 'block', |
459: | 'list-item', |
460: | 'run-in', |
461: | 'compact', |
462: | 'marker', |
463: | 'table', |
464: | 'inline-block', |
465: | 'inline-table', |
466: | 'table-row-group', |
467: | 'table-header-group', |
468: | 'table-footer-group', |
469: | 'table-row', |
470: | 'table-column-group', |
471: | 'table-column', |
472: | 'table-cell', |
473: | 'table-caption', |
474: | 'none' |
475: | ) |
476: | ); |
477: | $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum( |
478: | array('visible', 'hidden', 'collapse') |
479: | ); |
480: | $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); |
481: | $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
482: | } |
483: | |
484: | |
485: | |
486: | |
487: | protected function doSetupTrusted($config) |
488: | { |
489: | $this->info['position'] = new HTMLPurifier_AttrDef_Enum( |
490: | array('static', 'relative', 'absolute', 'fixed') |
491: | ); |
492: | $this->info['top'] = |
493: | $this->info['left'] = |
494: | $this->info['right'] = |
495: | $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite( |
496: | array( |
497: | new HTMLPurifier_AttrDef_CSS_Length(), |
498: | new HTMLPurifier_AttrDef_CSS_Percentage(), |
499: | new HTMLPurifier_AttrDef_Enum(array('auto')), |
500: | ) |
501: | ); |
502: | $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite( |
503: | array( |
504: | new HTMLPurifier_AttrDef_Integer(), |
505: | new HTMLPurifier_AttrDef_Enum(array('auto')), |
506: | ) |
507: | ); |
508: | } |
509: | |
510: | |
511: | |
512: | |
513: | |
514: | |
515: | |
516: | |
517: | protected function setupConfigStuff($config) |
518: | { |
519: | |
520: | $support = "(for information on implementing this, see the " . |
521: | "support forums) "; |
522: | $allowed_properties = $config->get('CSS.AllowedProperties'); |
523: | if ($allowed_properties !== null) { |
524: | foreach ($this->info as $name => $d) { |
525: | if (!isset($allowed_properties[$name])) { |
526: | unset($this->info[$name]); |
527: | } |
528: | unset($allowed_properties[$name]); |
529: | } |
530: | |
531: | foreach ($allowed_properties as $name => $d) { |
532: | |
533: | $name = htmlspecialchars($name); |
534: | trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); |
535: | } |
536: | } |
537: | |
538: | $forbidden_properties = $config->get('CSS.ForbiddenProperties'); |
539: | if ($forbidden_properties !== null) { |
540: | foreach ($this->info as $name => $d) { |
541: | if (isset($forbidden_properties[$name])) { |
542: | unset($this->info[$name]); |
543: | } |
544: | } |
545: | } |
546: | } |
547: | } |
548: | |
549: | |
550: | |