Skip to content

Commit 64f397c

Browse files
committed
Improve parsing of calc and url by handling escapes and preserving them as non-function keywords
1 parent c7a03a3 commit 64f397c

File tree

5 files changed

+50
-20
lines changed

5 files changed

+50
-20
lines changed

lib/Sabberworm/CSS/Parsing/ParserState.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ class ParserState {
1818
private $sCharset;
1919
private $iLength;
2020
private $iLineNo;
21+
private $iAnchor;
2122

2223
public function __construct($sText, Settings $oParserSettings, $iLineNo = 1) {
2324
$this->oParserSettings = $oParserSettings;
2425
$this->sText = $sText;
2526
$this->iCurrentPosition = 0;
2627
$this->iLineNo = $iLineNo;
28+
$this->iAnchor = null;
2729
$this->setCharset($this->oParserSettings->sDefaultCharset);
2830
}
2931

@@ -48,13 +50,26 @@ public function getSettings() {
4850
return $this->oParserSettings;
4951
}
5052

53+
public function setAnchor() {
54+
$this->iAnchor = $this->iCurrentPosition;
55+
}
56+
57+
public function backtrackToAnchor() {
58+
if ($this->iAnchor !== null) {
59+
$this->iCurrentPosition = $this->iAnchor;
60+
}
61+
}
62+
5163
public function parseIdentifier($bIgnoreCase = true) {
64+
if ($this->isEnd()) {
65+
throw new UnexpectedEOFException('', '', 'identifier', $this->iLineNo);
66+
}
5267
$sResult = $this->parseCharacter(true);
5368
if ($sResult === null) {
5469
throw new UnexpectedTokenException($sResult, $this->peek(5), 'identifier', $this->iLineNo);
5570
}
5671
$sCharacter = null;
57-
while (($sCharacter = $this->parseCharacter(true)) !== null) {
72+
while (!$this->isEnd() && ($sCharacter = $this->parseCharacter(true)) !== null) {
5873
$sResult .= $sCharacter;
5974
}
6075
if ($bIgnoreCase) {

lib/Sabberworm/CSS/Value/CalcFunction.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ class CalcFunction extends CSSFunction {
1111

1212
public static function parse(ParserState $oParserState) {
1313
$aOperators = array('+', '-', '*', '/');
14-
$aTerminators = array('(', ';', "\n", "\r", ParserState::EOF);
15-
$sFunction = $oParserState->consumeUntil($aTerminators, false, true);
16-
if ($oParserState->peek(1, -1) != '(') {
14+
$sFunction = $oParserState->parseIdentifier();
15+
if ($oParserState->peek() != '(') {
1716
// Found ; or end of line before an opening bracket
18-
throw new UnexpectedTokenException('(', $oParserState->peek(1, -1), 'literal', $oParserState->currentLine());
17+
throw new UnexpectedTokenException('(', $oParserState->peek(), 'literal', $oParserState->currentLine());
1918
} else if (!in_array($sFunction, array('calc', '-moz-calc', '-webkit-calc'))) {
2019
// Found invalid calc definition. Example calc (...
2120
throw new UnexpectedTokenException('calc', $sFunction, 'literal', $oParserState->currentLine());
2221
}
22+
$oParserState->consume('(');
2323
$oCalcList = new CalcRuleValueList($oParserState->currentLine());
2424
$oList = new RuleValueList(',', $oParserState->currentLine());
2525
$iNestingLevel = 0;

lib/Sabberworm/CSS/Value/URL.php

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,21 @@ public function __construct(CSSString $oURL, $iLineNo = 0) {
1414
}
1515

1616
public static function parse(ParserState $oParserState) {
17-
$bUseUrl = $oParserState->comes('url', true);
17+
$oParserState->setAnchor();
18+
$sIdentifier = '';
19+
for ($i = 0; $i < 3; $i++) {
20+
$sChar = $oParserState->parseCharacter(true);
21+
if ($sChar === null) {
22+
break;
23+
}
24+
$sIdentifier .= $sChar;
25+
}
26+
$bUseUrl = $oParserState->streql($sIdentifier, 'url');
1827
if ($bUseUrl) {
19-
$oParserState->consume('url');
2028
$oParserState->consumeWhiteSpace();
2129
$oParserState->consume('(');
30+
} else {
31+
$oParserState->backtrackToAnchor();
2232
}
2333
$oParserState->consumeWhiteSpace();
2434
$oResult = new URL(CSSString::parse($oParserState), $oParserState->currentLine());
@@ -46,4 +56,4 @@ public function render(\Sabberworm\CSS\OutputFormat $oOutputFormat) {
4656
return "url({$this->oURL->render($oOutputFormat)})";
4757
}
4858

49-
}
59+
}

lib/Sabberworm/CSS/Value/Value.php

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,25 @@ public static function parseValue(ParserState $oParserState, $aListDelimiters =
6363
}
6464

6565
public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false) {
66-
$sResult = $oParserState->parseIdentifier($bIgnoreCase);
66+
$oParserState->setAnchor();
67+
$mResult = $oParserState->parseIdentifier($bIgnoreCase);
6768

6869
if ($oParserState->comes('(')) {
69-
$oParserState->consume('(');
70-
$aArguments = Value::parseValue($oParserState, array('=', ' ', ','));
71-
$sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine());
72-
$oParserState->consume(')');
70+
if ($oParserState->streql('url', $mResult)) {
71+
$oParserState->backtrackToAnchor();
72+
$mResult = URL::parse($oParserState);
73+
} else if ($oParserState->streql('calc', $mResult) || $oParserState->streql('-webkit-calc', $mResult) || $oParserState->streql('-moz-calc', $mResult)) {
74+
$oParserState->backtrackToAnchor();
75+
$mResult = CalcFunction::parse($oParserState);
76+
} else {
77+
$oParserState->consume('(');
78+
$aArguments = Value::parseValue($oParserState, array('=', ' ', ','));
79+
$mResult = new CSSFunction($mResult, $aArguments, ',', $oParserState->currentLine());
80+
$oParserState->consume(')');
81+
}
7382
}
7483

75-
return $sResult;
84+
return $mResult;
7685
}
7786

7887
public static function parsePrimitiveValue(ParserState $oParserState) {
@@ -82,10 +91,6 @@ public static function parsePrimitiveValue(ParserState $oParserState) {
8291
$oValue = Size::parse($oParserState);
8392
} else if ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) {
8493
$oValue = Color::parse($oParserState);
85-
} else if ($oParserState->comes('url', true)) {
86-
$oValue = URL::parse($oParserState);
87-
} else if ($oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true) || $oParserState->comes('-moz-calc', true)) {
88-
$oValue = CalcFunction::parse($oParserState);
8994
} else if ($oParserState->comes("'") || $oParserState->comes('"')) {
9095
$oValue = CSSString::parse($oParserState);
9196
} else if ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) {

tests/Sabberworm/CSS/ParserTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,8 @@ function testInvalidCalcInFile() {
420420
$sExpected = 'div {}
421421
div {}
422422
div {}
423-
div {}
424-
div {}';
423+
div {height: -moz-calc;}
424+
div {height: calc;}';
425425
$this->assertSame($sExpected, $oDoc->render());
426426
}
427427

0 commit comments

Comments
 (0)