Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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       * @type HTMLPurifier_AttrDef[]

  15       */
  16      public $info = array();
  17  
  18      /**

  19       * Constructs the info array.  The meat of this class.

  20       * @param HTMLPurifier_Config $config

  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          $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()
 121                  )
 122              );
 123  
 124          $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config);
 125  
 126          $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color);
 127  
 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
 136                  )
 137              );
 138  
 139          $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width);
 140  
 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()
 145              )
 146          );
 147  
 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()
 152              )
 153          );
 154  
 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'
 168                      )
 169                  ),
 170                  new HTMLPurifier_AttrDef_CSS_Percentage(),
 171                  new HTMLPurifier_AttrDef_CSS_Length()
 172              )
 173          );
 174  
 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)
 181              )
 182          );
 183  
 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'))
 193                  )
 194              );
 195  
 196          $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin);
 197  
 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)
 207                  )
 208              );
 209  
 210          $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding);
 211  
 212          $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(
 213              array(
 214                  new HTMLPurifier_AttrDef_CSS_Length(),
 215                  new HTMLPurifier_AttrDef_CSS_Percentage()
 216              )
 217          );
 218  
 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', 'initial', 'inherit'))
 224              )
 225          );
 226          $trusted_min_wh = new HTMLPurifier_AttrDef_CSS_Composite(
 227              array(
 228                  new HTMLPurifier_AttrDef_CSS_Length('0'),
 229                  new HTMLPurifier_AttrDef_CSS_Percentage(true),
 230                  new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit'))
 231              )
 232          );
 233          $trusted_max_wh = new HTMLPurifier_AttrDef_CSS_Composite(
 234              array(
 235                  new HTMLPurifier_AttrDef_CSS_Length('0'),
 236                  new HTMLPurifier_AttrDef_CSS_Percentage(true),
 237                  new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit'))
 238              )
 239          );
 240          $max = $config->get('CSS.MaxImgLength');
 241  
 242          $this->info['width'] =
 243          $this->info['height'] =
 244              $max === null ?
 245                  $trusted_wh :
 246                  new HTMLPurifier_AttrDef_Switch(
 247                      'img',
 248                      // For img tags:

 249                      new HTMLPurifier_AttrDef_CSS_Composite(
 250                          array(
 251                              new HTMLPurifier_AttrDef_CSS_Length('0', $max),
 252                              new HTMLPurifier_AttrDef_Enum(array('auto'))
 253                          )
 254                      ),
 255                      // For everyone else:

 256                      $trusted_wh
 257                  );
 258          $this->info['min-width'] =
 259          $this->info['min-height'] =
 260              $max === null ?
 261                  $trusted_min_wh :
 262                  new HTMLPurifier_AttrDef_Switch(
 263                      'img',
 264                      // For img tags:

 265                      new HTMLPurifier_AttrDef_CSS_Composite(
 266                          array(
 267                              new HTMLPurifier_AttrDef_CSS_Length('0', $max),
 268                              new HTMLPurifier_AttrDef_Enum(array('initial', 'inherit'))
 269                          )
 270                      ),
 271                      // For everyone else:

 272                      $trusted_min_wh
 273                  );
 274          $this->info['max-width'] =
 275          $this->info['max-height'] =
 276              $max === null ?
 277                  $trusted_max_wh :
 278                  new HTMLPurifier_AttrDef_Switch(
 279                      'img',
 280                      // For img tags:

 281                      new HTMLPurifier_AttrDef_CSS_Composite(
 282                          array(
 283                              new HTMLPurifier_AttrDef_CSS_Length('0', $max),
 284                              new HTMLPurifier_AttrDef_Enum(array('none', 'initial', 'inherit'))
 285                          )
 286                      ),
 287                      // For everyone else:

 288                      $trusted_max_wh
 289                  );
 290  
 291          $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration();
 292  
 293          $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily();
 294  
 295          // this could use specialized code

 296          $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum(
 297              array(
 298                  'normal',
 299                  'bold',
 300                  'bolder',
 301                  'lighter',
 302                  '100',
 303                  '200',
 304                  '300',
 305                  '400',
 306                  '500',
 307                  '600',
 308                  '700',
 309                  '800',
 310                  '900'
 311              ),
 312              false
 313          );
 314  
 315          // MUST be called after other font properties, as it references

 316          // a CSSDefinition object

 317          $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
 318  
 319          // same here

 320          $this->info['border'] =
 321          $this->info['border-bottom'] =
 322          $this->info['border-top'] =
 323          $this->info['border-left'] =
 324          $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config);
 325  
 326          $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(
 327              array('collapse', 'separate')
 328          );
 329  
 330          $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(
 331              array('top', 'bottom')
 332          );
 333  
 334          $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(
 335              array('auto', 'fixed')
 336          );
 337  
 338          $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(
 339              array(
 340                  new HTMLPurifier_AttrDef_Enum(
 341                      array(
 342                          'baseline',
 343                          'sub',
 344                          'super',
 345                          'top',
 346                          'text-top',
 347                          'middle',
 348                          'bottom',
 349                          'text-bottom'
 350                      )
 351                  ),
 352                  new HTMLPurifier_AttrDef_CSS_Length(),
 353                  new HTMLPurifier_AttrDef_CSS_Percentage()
 354              )
 355          );
 356  
 357          $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2);
 358  
 359          // These CSS properties don't work on many browsers, but we live

 360          // in THE FUTURE!

 361          $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(
 362              array('nowrap', 'normal', 'pre', 'pre-wrap', 'pre-line')
 363          );
 364  
 365          if ($config->get('CSS.Proprietary')) {
 366              $this->doSetupProprietary($config);
 367          }
 368  
 369          if ($config->get('CSS.AllowTricky')) {
 370              $this->doSetupTricky($config);
 371          }
 372  
 373          if ($config->get('CSS.Trusted')) {
 374              $this->doSetupTrusted($config);
 375          }
 376  
 377          $allow_important = $config->get('CSS.AllowImportant');
 378          // wrap all attr-defs with decorator that handles !important

 379          foreach ($this->info as $k => $v) {
 380              $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important);
 381          }
 382  
 383          $this->setupConfigStuff($config);
 384      }
 385  
 386      /**

 387       * @param HTMLPurifier_Config $config

 388       */
 389      protected function doSetupProprietary($config)
 390      {
 391          // Internet Explorer only scrollbar colors

 392          $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
 393          $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color();
 394          $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
 395          $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color();
 396          $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color();
 397          $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color();
 398  
 399          // vendor specific prefixes of opacity

 400          $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
 401          $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
 402  
 403          // only opacity, for now

 404          $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
 405  
 406          // more CSS3

 407          $this->info['page-break-after'] =
 408          $this->info['page-break-before'] = new HTMLPurifier_AttrDef_Enum(
 409              array(
 410                  'auto',
 411                  'always',
 412                  'avoid',
 413                  'left',
 414                  'right'
 415              )
 416          );
 417          $this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
 418  
 419          $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
 420              array(
 421                  new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
 422                  new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
 423              ));
 424  
 425          $this->info['border-top-left-radius'] =
 426          $this->info['border-top-right-radius'] =
 427          $this->info['border-bottom-right-radius'] =
 428          $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
 429          // TODO: support SLASH syntax

 430          $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
 431  
 432      }
 433  
 434      /**

 435       * @param HTMLPurifier_Config $config

 436       */
 437      protected function doSetupTricky($config)
 438      {
 439          $this->info['display'] = new HTMLPurifier_AttrDef_Enum(
 440              array(
 441                  'inline',
 442                  'block',
 443                  'list-item',
 444                  'run-in',
 445                  'compact',
 446                  'marker',
 447                  'table',
 448                  'inline-block',
 449                  'inline-table',
 450                  'table-row-group',
 451                  'table-header-group',
 452                  'table-footer-group',
 453                  'table-row',
 454                  'table-column-group',
 455                  'table-column',
 456                  'table-cell',
 457                  'table-caption',
 458                  'none'
 459              )
 460          );
 461          $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(
 462              array('visible', 'hidden', 'collapse')
 463          );
 464          $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll'));
 465          $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
 466      }
 467  
 468      /**

 469       * @param HTMLPurifier_Config $config

 470       */
 471      protected function doSetupTrusted($config)
 472      {
 473          $this->info['position'] = new HTMLPurifier_AttrDef_Enum(
 474              array('static', 'relative', 'absolute', 'fixed')
 475          );
 476          $this->info['top'] =
 477          $this->info['left'] =
 478          $this->info['right'] =
 479          $this->info['bottom'] = new HTMLPurifier_AttrDef_CSS_Composite(
 480              array(
 481                  new HTMLPurifier_AttrDef_CSS_Length(),
 482                  new HTMLPurifier_AttrDef_CSS_Percentage(),
 483                  new HTMLPurifier_AttrDef_Enum(array('auto')),
 484              )
 485          );
 486          $this->info['z-index'] = new HTMLPurifier_AttrDef_CSS_Composite(
 487              array(
 488                  new HTMLPurifier_AttrDef_Integer(),
 489                  new HTMLPurifier_AttrDef_Enum(array('auto')),
 490              )
 491          );
 492      }
 493  
 494      /**

 495       * Performs extra config-based processing. Based off of

 496       * HTMLPurifier_HTMLDefinition.

 497       * @param HTMLPurifier_Config $config

 498       * @todo Refactor duplicate elements into common class (probably using

 499       *       composition, not inheritance).

 500       */
 501      protected function setupConfigStuff($config)
 502      {
 503          // setup allowed elements

 504          $support = "(for information on implementing this, see the " .
 505              "support forums) ";
 506          $allowed_properties = $config->get('CSS.AllowedProperties');
 507          if ($allowed_properties !== null) {
 508              foreach ($this->info as $name => $d) {
 509                  if (!isset($allowed_properties[$name])) {
 510                      unset($this->info[$name]);
 511                  }
 512                  unset($allowed_properties[$name]);
 513              }
 514              // emit errors

 515              foreach ($allowed_properties as $name => $d) {
 516                  // :TODO: Is this htmlspecialchars() call really necessary?

 517                  $name = htmlspecialchars($name);
 518                  trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING);
 519              }
 520          }
 521  
 522          $forbidden_properties = $config->get('CSS.ForbiddenProperties');
 523          if ($forbidden_properties !== null) {
 524              foreach ($this->info as $name => $d) {
 525                  if (isset($forbidden_properties[$name])) {
 526                      unset($this->info[$name]);
 527                  }
 528              }
 529          }
 530      }
 531  }
 532  
 533  // vim: et sw=4 sts=4