Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401] [Versions 400 and 401]

   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          $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') //disallow negative
 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), // no negatives
 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          // non-negative
 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                      // 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('auto'))
 269                          )
 270                      ),
 271                      // For everyone else:
 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                      // 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('initial', 'inherit'))
 285                          )
 286                      ),
 287                      // For everyone else:
 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                      // For img tags:
 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                      // For everyone else:
 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          // this could use specialized code
 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          // MUST be called after other font properties, as it references
 332          // a CSSDefinition object
 333          $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config);
 334  
 335          // same here
 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          // These CSS properties don't work on many browsers, but we live
 376          // in THE FUTURE!
 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          // wrap all attr-defs with decorator that handles !important
 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       * @param HTMLPurifier_Config $config
 404       */
 405      protected function doSetupProprietary($config)
 406      {
 407          // Internet Explorer only scrollbar colors
 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          // vendor specific prefixes of opacity
 416          $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
 417          $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue();
 418  
 419          // only opacity, for now
 420          $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter();
 421  
 422          // more CSS3
 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), // disallow negative
 438                  new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
 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          // TODO: support SLASH syntax
 446          $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
 447  
 448      }
 449  
 450      /**
 451       * @param HTMLPurifier_Config $config
 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       * @param HTMLPurifier_Config $config
 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       * Performs extra config-based processing. Based off of
 512       * HTMLPurifier_HTMLDefinition.
 513       * @param HTMLPurifier_Config $config
 514       * @todo Refactor duplicate elements into common class (probably using
 515       *       composition, not inheritance).
 516       */
 517      protected function setupConfigStuff($config)
 518      {
 519          // setup allowed elements
 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              // emit errors
 531              foreach ($allowed_properties as $name => $d) {
 532                  // :TODO: Is this htmlspecialchars() call really necessary?
 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  // vim: et sw=4 sts=4