| [21] | 1 | <?php |
|---|
| 2 | |
|---|
| 3 | /** |
|---|
| 4 | * Defines allowed CSS attributes and what their values are. |
|---|
| 5 | * @see HTMLPurifier_HTMLDefinition |
|---|
| 6 | */ |
|---|
| 7 | class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition |
|---|
| 8 | { |
|---|
| 9 | |
|---|
| 10 | public $type = 'CSS'; |
|---|
| 11 | |
|---|
| 12 | /** |
|---|
| 13 | * Assoc array of attribute name to definition object. |
|---|
| 14 | */ |
|---|
| 15 | public $info = array(); |
|---|
| 16 | |
|---|
| 17 | /** |
|---|
| 18 | * Constructs the info array. The meat of this class. |
|---|
| 19 | */ |
|---|
| 20 | protected function doSetup($config) { |
|---|
| 21 | |
|---|
| 22 | $this->info['text-align'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 23 | array('left', 'right', 'center', 'justify'), false); |
|---|
| 24 | |
|---|
| 25 | $border_style = |
|---|
| 26 | $this->info['border-bottom-style'] = |
|---|
| 27 | $this->info['border-right-style'] = |
|---|
| 28 | $this->info['border-left-style'] = |
|---|
| 29 | $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 30 | array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', |
|---|
| 31 | 'groove', 'ridge', 'inset', 'outset'), false); |
|---|
| 32 | |
|---|
| 33 | $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); |
|---|
| 34 | |
|---|
| 35 | $this->info['clear'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 36 | array('none', 'left', 'right', 'both'), false); |
|---|
| 37 | $this->info['float'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 38 | array('none', 'left', 'right'), false); |
|---|
| 39 | $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 40 | array('normal', 'italic', 'oblique'), false); |
|---|
| 41 | $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 42 | array('normal', 'small-caps'), false); |
|---|
| 43 | |
|---|
| 44 | $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( |
|---|
| 45 | array( |
|---|
| 46 | new HTMLPurifier_AttrDef_Enum(array('none')), |
|---|
| 47 | new HTMLPurifier_AttrDef_CSS_URI() |
|---|
| 48 | ) |
|---|
| 49 | ); |
|---|
| 50 | |
|---|
| 51 | $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 52 | array('inside', 'outside'), false); |
|---|
| 53 | $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 54 | array('disc', 'circle', 'square', 'decimal', 'lower-roman', |
|---|
| 55 | 'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false); |
|---|
| 56 | $this->info['list-style-image'] = $uri_or_none; |
|---|
| 57 | |
|---|
| 58 | $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); |
|---|
| 59 | |
|---|
| 60 | $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 61 | array('capitalize', 'uppercase', 'lowercase', 'none'), false); |
|---|
| 62 | $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 63 | |
|---|
| 64 | $this->info['background-image'] = $uri_or_none; |
|---|
| 65 | $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 66 | array('repeat', 'repeat-x', 'repeat-y', 'no-repeat') |
|---|
| 67 | ); |
|---|
| 68 | $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 69 | array('scroll', 'fixed') |
|---|
| 70 | ); |
|---|
| 71 | $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); |
|---|
| 72 | |
|---|
| 73 | $border_color = |
|---|
| 74 | $this->info['border-top-color'] = |
|---|
| 75 | $this->info['border-bottom-color'] = |
|---|
| 76 | $this->info['border-left-color'] = |
|---|
| 77 | $this->info['border-right-color'] = |
|---|
| 78 | $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 79 | new HTMLPurifier_AttrDef_Enum(array('transparent')), |
|---|
| 80 | new HTMLPurifier_AttrDef_CSS_Color() |
|---|
| 81 | )); |
|---|
| 82 | |
|---|
| 83 | $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); |
|---|
| 84 | |
|---|
| 85 | $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); |
|---|
| 86 | |
|---|
| 87 | $border_width = |
|---|
| 88 | $this->info['border-top-width'] = |
|---|
| 89 | $this->info['border-bottom-width'] = |
|---|
| 90 | $this->info['border-left-width'] = |
|---|
| 91 | $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 92 | new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), |
|---|
| 93 | new HTMLPurifier_AttrDef_CSS_Length(true) //disallow negative |
|---|
| 94 | )); |
|---|
| 95 | |
|---|
| 96 | $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); |
|---|
| 97 | |
|---|
| 98 | $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 99 | new HTMLPurifier_AttrDef_Enum(array('normal')), |
|---|
| 100 | new HTMLPurifier_AttrDef_CSS_Length() |
|---|
| 101 | )); |
|---|
| 102 | |
|---|
| 103 | $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 104 | new HTMLPurifier_AttrDef_Enum(array('normal')), |
|---|
| 105 | new HTMLPurifier_AttrDef_CSS_Length() |
|---|
| 106 | )); |
|---|
| 107 | |
|---|
| 108 | $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 109 | new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small', |
|---|
| 110 | 'small', 'medium', 'large', 'x-large', 'xx-large', |
|---|
| 111 | 'larger', 'smaller')), |
|---|
| 112 | new HTMLPurifier_AttrDef_CSS_Percentage(), |
|---|
| 113 | new HTMLPurifier_AttrDef_CSS_Length() |
|---|
| 114 | )); |
|---|
| 115 | |
|---|
| 116 | $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 117 | new HTMLPurifier_AttrDef_Enum(array('normal')), |
|---|
| 118 | new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives |
|---|
| 119 | new HTMLPurifier_AttrDef_CSS_Length(true), |
|---|
| 120 | new HTMLPurifier_AttrDef_CSS_Percentage(true) |
|---|
| 121 | )); |
|---|
| 122 | |
|---|
| 123 | $margin = |
|---|
| 124 | $this->info['margin-top'] = |
|---|
| 125 | $this->info['margin-bottom'] = |
|---|
| 126 | $this->info['margin-left'] = |
|---|
| 127 | $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 128 | new HTMLPurifier_AttrDef_CSS_Length(), |
|---|
| 129 | new HTMLPurifier_AttrDef_CSS_Percentage(), |
|---|
| 130 | new HTMLPurifier_AttrDef_Enum(array('auto')) |
|---|
| 131 | )); |
|---|
| 132 | |
|---|
| 133 | $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); |
|---|
| 134 | |
|---|
| 135 | // non-negative |
|---|
| 136 | $padding = |
|---|
| 137 | $this->info['padding-top'] = |
|---|
| 138 | $this->info['padding-bottom'] = |
|---|
| 139 | $this->info['padding-left'] = |
|---|
| 140 | $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 141 | new HTMLPurifier_AttrDef_CSS_Length(true), |
|---|
| 142 | new HTMLPurifier_AttrDef_CSS_Percentage(true) |
|---|
| 143 | )); |
|---|
| 144 | |
|---|
| 145 | $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); |
|---|
| 146 | |
|---|
| 147 | $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 148 | new HTMLPurifier_AttrDef_CSS_Length(), |
|---|
| 149 | new HTMLPurifier_AttrDef_CSS_Percentage() |
|---|
| 150 | )); |
|---|
| 151 | |
|---|
| 152 | $this->info['width'] = |
|---|
| 153 | $this->info['height'] = |
|---|
| 154 | new HTMLPurifier_AttrDef_CSS_DenyElementDecorator( |
|---|
| 155 | new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 156 | new HTMLPurifier_AttrDef_CSS_Length(true), |
|---|
| 157 | new HTMLPurifier_AttrDef_CSS_Percentage(true), |
|---|
| 158 | new HTMLPurifier_AttrDef_Enum(array('auto')) |
|---|
| 159 | )), 'img'); |
|---|
| 160 | |
|---|
| 161 | $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); |
|---|
| 162 | |
|---|
| 163 | $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); |
|---|
| 164 | |
|---|
| 165 | // this could use specialized code |
|---|
| 166 | $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( |
|---|
| 167 | array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', |
|---|
| 168 | '400', '500', '600', '700', '800', '900'), false); |
|---|
| 169 | |
|---|
| 170 | // MUST be called after other font properties, as it references |
|---|
| 171 | // a CSSDefinition object |
|---|
| 172 | $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config); |
|---|
| 173 | |
|---|
| 174 | // same here |
|---|
| 175 | $this->info['border'] = |
|---|
| 176 | $this->info['border-bottom'] = |
|---|
| 177 | $this->info['border-top'] = |
|---|
| 178 | $this->info['border-left'] = |
|---|
| 179 | $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config); |
|---|
| 180 | |
|---|
| 181 | $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array( |
|---|
| 182 | 'collapse', 'separate')); |
|---|
| 183 | |
|---|
| 184 | $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array( |
|---|
| 185 | 'top', 'bottom')); |
|---|
| 186 | |
|---|
| 187 | $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array( |
|---|
| 188 | 'auto', 'fixed')); |
|---|
| 189 | |
|---|
| 190 | $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array( |
|---|
| 191 | new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super', |
|---|
| 192 | 'top', 'text-top', 'middle', 'bottom', 'text-bottom')), |
|---|
| 193 | new HTMLPurifier_AttrDef_CSS_Length(), |
|---|
| 194 | new HTMLPurifier_AttrDef_CSS_Percentage() |
|---|
| 195 | )); |
|---|
| 196 | |
|---|
| 197 | $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); |
|---|
| 198 | |
|---|
| 199 | // partial support |
|---|
| 200 | $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap')); |
|---|
| 201 | |
|---|
| 202 | if ($config->get('CSS', 'Proprietary')) { |
|---|
| 203 | $this->doSetupProprietary($config); |
|---|
| 204 | } |
|---|
| 205 | |
|---|
| 206 | if ($config->get('CSS', 'AllowTricky')) { |
|---|
| 207 | $this->doSetupTricky($config); |
|---|
| 208 | } |
|---|
| 209 | |
|---|
| 210 | $allow_important = $config->get('CSS', 'AllowImportant'); |
|---|
| 211 | // wrap all attr-defs with decorator that handles !important |
|---|
| 212 | foreach ($this->info as $k => $v) { |
|---|
| 213 | $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | $this->setupConfigStuff($config); |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | protected function doSetupProprietary($config) { |
|---|
| 220 | // Internet Explorer only scrollbar colors |
|---|
| 221 | $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 222 | $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 223 | $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 224 | $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 225 | $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 226 | $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); |
|---|
| 227 | |
|---|
| 228 | // technically not proprietary, but CSS3, and no one supports it |
|---|
| 229 | $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
|---|
| 230 | $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
|---|
| 231 | $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); |
|---|
| 232 | |
|---|
| 233 | // only opacity, for now |
|---|
| 234 | $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); |
|---|
| 235 | |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | protected function doSetupTricky($config) { |
|---|
| 239 | $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array( |
|---|
| 240 | 'inline', 'block', 'list-item', 'run-in', 'compact', |
|---|
| 241 | 'marker', 'table', 'inline-table', 'table-row-group', |
|---|
| 242 | 'table-header-group', 'table-footer-group', 'table-row', |
|---|
| 243 | 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none' |
|---|
| 244 | )); |
|---|
| 245 | $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array( |
|---|
| 246 | 'visible', 'hidden', 'collapse' |
|---|
| 247 | )); |
|---|
| 248 | } |
|---|
| 249 | |
|---|
| 250 | |
|---|
| 251 | /** |
|---|
| 252 | * Performs extra config-based processing. Based off of |
|---|
| 253 | * HTMLPurifier_HTMLDefinition. |
|---|
| 254 | * @todo Refactor duplicate elements into common class (probably using |
|---|
| 255 | * composition, not inheritance). |
|---|
| 256 | */ |
|---|
| 257 | protected function setupConfigStuff($config) { |
|---|
| 258 | |
|---|
| 259 | // setup allowed elements |
|---|
| 260 | $support = "(for information on implementing this, see the ". |
|---|
| 261 | "support forums) "; |
|---|
| 262 | $allowed_attributes = $config->get('CSS', 'AllowedProperties'); |
|---|
| 263 | if ($allowed_attributes !== null) { |
|---|
| 264 | foreach ($this->info as $name => $d) { |
|---|
| 265 | if(!isset($allowed_attributes[$name])) unset($this->info[$name]); |
|---|
| 266 | unset($allowed_attributes[$name]); |
|---|
| 267 | } |
|---|
| 268 | // emit errors |
|---|
| 269 | foreach ($allowed_attributes as $name => $d) { |
|---|
| 270 | // :TODO: Is this htmlspecialchars() call really necessary? |
|---|
| 271 | $name = htmlspecialchars($name); |
|---|
| 272 | trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); |
|---|
| 273 | } |
|---|
| 274 | } |
|---|
| 275 | |
|---|
| 276 | } |
|---|
| 277 | } |
|---|
| 278 | |
|---|