Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
KeyframesAtRuleSanitizer
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
3 / 3
9
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 handlesRule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 doSanitize
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
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\Alternative;
10use Wikimedia\CSS\Grammar\KeywordMatcher;
11use Wikimedia\CSS\Grammar\Matcher;
12use Wikimedia\CSS\Grammar\MatcherFactory;
13use Wikimedia\CSS\Grammar\Quantifier;
14use Wikimedia\CSS\Objects\AtRule;
15use Wikimedia\CSS\Objects\CSSObject;
16use Wikimedia\CSS\Objects\Rule;
17use Wikimedia\CSS\Util;
18
19/**
20 * Sanitizes a CSS \@keyframes rule
21 * @see https://www.w3.org/TR/2018/WD-css-animations-1-20181011/#keyframes
22 */
23class KeyframesAtRuleSanitizer extends RuleSanitizer {
24
25    /** @var Matcher */
26    protected $nameMatcher;
27
28    /** @var Sanitizer */
29    protected $ruleSanitizer;
30
31    /**
32     * @param MatcherFactory $matcherFactory
33     * @param PropertySanitizer $propertySanitizer Sanitizer for declarations
34     */
35    public function __construct(
36        MatcherFactory $matcherFactory, PropertySanitizer $propertySanitizer
37    ) {
38        $this->nameMatcher = new Alternative( [
39            $matcherFactory->customIdent( [ 'none' ] ),
40            $matcherFactory->string(),
41        ] );
42        $this->ruleSanitizer = new StyleRuleSanitizer(
43            Quantifier::hash( new Alternative( [
44                new KeywordMatcher( [ 'from', 'to' ] ), $matcherFactory->rawPercentage()
45            ] ) ),
46            $propertySanitizer
47        );
48    }
49
50    /** @inheritDoc */
51    public function handlesRule( Rule $rule ) {
52        return $rule instanceof AtRule && !strcasecmp( $rule->getName(), 'keyframes' );
53    }
54
55    /** @inheritDoc */
56    protected function doSanitize( CSSObject $object ) {
57        if ( !$object instanceof AtRule || !$this->handlesRule( $object ) ) {
58            $this->sanitizationError( 'expected-at-rule', $object, [ 'keyframes' ] );
59            return null;
60        }
61
62        if ( $object->getBlock() === null ) {
63            $this->sanitizationError( 'at-rule-block-required', $object, [ 'keyframes' ] );
64            return null;
65        }
66
67        // Test the keyframe name
68        if ( !$this->nameMatcher->matchAgainst( $object->getPrelude(), [ 'mark-significance' => true ] ) ) {
69            $cv = Util::findFirstNonWhitespace( $object->getPrelude() );
70            if ( $cv ) {
71                $this->sanitizationError( 'invalid-keyframe-name', $cv );
72            } else {
73                $this->sanitizationError( 'missing-keyframe-name', $object );
74            }
75            return null;
76        }
77
78        $ret = clone $object;
79        $this->fixPreludeWhitespace( $ret, false );
80        $this->sanitizeRuleBlock( $ret->getBlock(), [ $this->ruleSanitizer ] );
81
82        return $ret;
83    }
84}