Skip to content

Commit 86fd609

Browse files
authored
Merge pull request #168 from raxbg/keyframe_selector_validation
Keyframe selector validation
2 parents e620ccf + d148f79 commit 86fd609

File tree

6 files changed

+61
-10
lines changed

6 files changed

+61
-10
lines changed

lib/Sabberworm/CSS/CSSList/CSSList.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ private static function parseListItem(ParserState $oParserState, CSSList $oList)
9797
}
9898
}
9999
} else {
100-
return DeclarationBlock::parse($oParserState);
100+
return DeclarationBlock::parse($oParserState, $oList);
101101
}
102102
}
103103

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Sabberworm\CSS\Property;
4+
5+
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
6+
7+
class KeyframeSelector extends Selector {
8+
9+
//Regexes for specificity calculations
10+
11+
const SELECTOR_VALIDATION_RX = '/
12+
^(
13+
(?:
14+
[a-zA-Z0-9\x{00A0}-\x{FFFF}_^$|*="\'~\[\]()\-\s\.:#+>]* # any sequence of valid unescaped characters
15+
(?:\\\\.)? # a single escaped character
16+
(?:([\'"]).*?(?<!\\\\)\2)? # a quoted text like [id="example"]
17+
)*
18+
)|
19+
(\d+%) # keyframe animation progress percentage (e.g. 50%)
20+
$
21+
/ux';
22+
23+
}

lib/Sabberworm/CSS/Property/Selector.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class Selector {
5151
private $iSpecificity;
5252

5353
public static function isValid($sSelector) {
54-
return preg_match(self::SELECTOR_VALIDATION_RX, $sSelector);
54+
return preg_match(static::SELECTOR_VALIDATION_RX, $sSelector);
5555
}
5656

5757
public function __construct($sSelector, $bCalculateSpecificity = false) {

lib/Sabberworm/CSS/RuleSet/DeclarationBlock.php

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
use Sabberworm\CSS\Parsing\OutputException;
77
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
88
use Sabberworm\CSS\Property\Selector;
9+
use Sabberworm\CSS\Property\KeyframeSelector;
910
use Sabberworm\CSS\Rule\Rule;
1011
use Sabberworm\CSS\Value\RuleValueList;
1112
use Sabberworm\CSS\Value\Value;
1213
use Sabberworm\CSS\Value\Size;
1314
use Sabberworm\CSS\Value\Color;
1415
use Sabberworm\CSS\Value\URL;
16+
use Sabberworm\CSS\CSSList\KeyFrame;
1517

1618
/**
1719
* Declaration blocks are the parts of a css file which denote the rules belonging to a selector.
@@ -26,7 +28,7 @@ public function __construct($iLineNo = 0) {
2628
$this->aSelectors = array();
2729
}
2830

29-
public static function parse(ParserState $oParserState) {
31+
public static function parse(ParserState $oParserState, $oList = NULL) {
3032
$aComments = array();
3133
$oResult = new DeclarationBlock($oParserState->currentLine());
3234
try {
@@ -42,7 +44,7 @@ public static function parse(ParserState $oParserState) {
4244
}
4345
}
4446
} while (!in_array($oParserState->peek(), array('{', '}')) || $sStringWrapperChar !== false);
45-
$oResult->setSelector(implode('', $aSelectorParts));
47+
$oResult->setSelector(implode('', $aSelectorParts), $oList);
4648
if ($oParserState->comes('{')) {
4749
$oParserState->consume(1);
4850
}
@@ -62,18 +64,25 @@ public static function parse(ParserState $oParserState) {
6264
}
6365

6466

65-
public function setSelectors($mSelector) {
67+
public function setSelectors($mSelector, $oList = NULL) {
6668
if (is_array($mSelector)) {
6769
$this->aSelectors = $mSelector;
6870
} else {
6971
$this->aSelectors = explode(',', $mSelector);
7072
}
7173
foreach ($this->aSelectors as $iKey => $mSelector) {
7274
if (!($mSelector instanceof Selector)) {
73-
if (!Selector::isValid($mSelector)) {
74-
throw new UnexpectedTokenException("Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", $mSelector, "custom");
75+
if ($oList === NULL || !($oList instanceof KeyFrame)) {
76+
if (!Selector::isValid($mSelector)) {
77+
throw new UnexpectedTokenException("Selector did not match '" . Selector::SELECTOR_VALIDATION_RX . "'.", $mSelector, "custom");
78+
}
79+
$this->aSelectors[$iKey] = new Selector($mSelector);
80+
} else {
81+
if (!KeyframeSelector::isValid($mSelector)) {
82+
throw new UnexpectedTokenException("Selector did not match '" . KeyframeSelector::SELECTOR_VALIDATION_RX . "'.", $mSelector, "custom");
83+
}
84+
$this->aSelectors[$iKey] = new KeyframeSelector($mSelector);
7585
}
76-
$this->aSelectors[$iKey] = new Selector($mSelector);
7786
}
7887
}
7988
}
@@ -102,8 +111,8 @@ public function getSelector() {
102111
/**
103112
* @deprecated use setSelectors()
104113
*/
105-
public function setSelector($mSelector) {
106-
$this->setSelectors($mSelector);
114+
public function setSelector($mSelector, $oList = NULL) {
115+
$this->setSelectors($mSelector, $oList);
107116
}
108117

109118
/**

tests/Sabberworm/CSS/ParserTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,14 @@ function testSelectorIgnoresInFile() {
474474
$this->assertSame($sExpected, $oDoc->render());
475475
}
476476

477+
function testKeyframeSelectors() {
478+
$oDoc = $this->parsedStructureForFile('keyframe-selector-validation', Settings::create()->withMultibyteSupport(true));
479+
$sExpected = '@-webkit-keyframes zoom {0% {-webkit-transform: scale(1,1);}
480+
50% {-webkit-transform: scale(1.2,1.2);}
481+
100% {-webkit-transform: scale(1,1);}}';
482+
$this->assertSame($sExpected, $oDoc->render());
483+
}
484+
477485
/**
478486
* @expectedException Sabberworm\CSS\Parsing\UnexpectedTokenException
479487
*/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@-webkit-keyframes zoom {
2+
0% {
3+
-webkit-transform: scale(1,1);
4+
}
5+
50% {
6+
-webkit-transform: scale(1.2,1.2);
7+
}
8+
100% {
9+
-webkit-transform: scale(1,1);
10+
}
11+
}

0 commit comments

Comments
 (0)