1<?php 2/** 3 * @file 4 * @license https://opensource.org/licenses/Apache-2.0 Apache-2.0 5 */ 6 7namespace Wikimedia\CSS\Sanitizer; 8 9use Wikimedia\CSS\Grammar\Matcher; 10use Wikimedia\CSS\Objects\AtRule; 11use Wikimedia\CSS\Objects\CSSObject; 12use Wikimedia\CSS\Objects\DeclarationList; 13use Wikimedia\CSS\Objects\Rule; 14use Wikimedia\CSS\Parser\Parser; 15use Wikimedia\CSS\Util; 16 17/** 18 * Sanitizes a feature-value at-rule inside a CSS \@font-feature-values rule 19 * @see https://www.w3.org/TR/2018/CR-css-fonts-3-20180315/#at-font-feature-values-rule 20 */ 21class FontFeatureValueAtRuleSanitizer extends RuleSanitizer { 22 23 /** @var string */ 24 protected $name; 25 26 /** @var Matcher */ 27 protected $valueMatcher; 28 29 /** 30 * @param string $name 31 * @param Matcher $valueMatcher 32 */ 33 public function __construct( $name, Matcher $valueMatcher ) { 34 $this->name = $name; 35 $this->valueMatcher = $valueMatcher; 36 } 37 38 /** @inheritDoc */ 39 public function handlesRule( Rule $rule ) { 40 return $rule instanceof AtRule && !strcasecmp( $rule->getName(), $this->name ); 41 } 42 43 /** @inheritDoc */ 44 protected function doSanitize( CSSObject $object ) { 45 if ( !$object instanceof AtRule || !$this->handlesRule( $object ) ) { 46 $this->sanitizationError( 'expected-at-rule', $object, [ $this->name ] ); 47 return null; 48 } 49 50 if ( $object->getBlock() === null ) { 51 $this->sanitizationError( 'at-rule-block-required', $object, [ $this->name ] ); 52 return null; 53 } 54 55 // No non-whitespace prelude allowed 56 if ( Util::findFirstNonWhitespace( $object->getPrelude() ) ) { 57 $this->sanitizationError( 'invalid-font-feature-value', $object, [ $this->name ] ); 58 return null; 59 } 60 61 $ret = clone $object; 62 $this->fixPreludeWhitespace( $ret, false ); 63 64 // Parse the block's contents into a list of declarations, sanitize it, 65 // and put it back into the block. 66 $blockContents = $ret->getBlock()->getValue(); 67 $parser = Parser::newFromTokens( $blockContents->toTokenArray() ); 68 $oldDeclarations = $parser->parseDeclarationList(); 69 $this->sanitizationErrors = array_merge( $this->sanitizationErrors, $parser->getParseErrors() ); 70 $newDeclarations = new DeclarationList(); 71 foreach ( $oldDeclarations as $declaration ) { 72 if ( $this->valueMatcher->matchAgainst( $declaration->getValue(), [ 'mark-significance' => true ] ) ) { 73 $newDeclarations->add( $declaration ); 74 } else { 75 $this->sanitizationError( 'invalid-font-feature-value-declaration', $declaration, 76 [ $this->name ] ); 77 } 78 } 79 $blockContents->clear(); 80 $blockContents->add( $newDeclarations->toComponentValueArray() ); 81 82 return $ret; 83 } 84} 85