Skip to content

Commit 870d080

Browse files
committed
Merge pull request #181 from cscott/escapes
Strictly parse whitespace and escapes.
2 parents 31c3aac + 3f4fba4 commit 870d080

File tree

3 files changed

+54
-12
lines changed

3 files changed

+54
-12
lines changed

src/css/TokenStream.js

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/*global Tokens, TokenStreamBase*/
22

33
var h = /^[0-9a-fA-F]$/,
4-
nonascii = /^[\u0080-\uFFFF]$/,
5-
nl = /\n|\r\n|\r|\f/;
4+
nonascii = /^[\u00A0-\uFFFF]$/,
5+
nl = /\n|\r\n|\r|\f/,
6+
whitespace = /\u0009|\u000a|\u000c|\u000d|\u0020/;
67

78
//-----------------------------------------------------------------------------
89
// Helper functions
@@ -18,15 +19,15 @@ function isDigit(c){
1819
}
1920

2021
function isWhitespace(c){
21-
return c !== null && /\s/.test(c);
22+
return c !== null && whitespace.test(c);
2223
}
2324

2425
function isNewLine(c){
2526
return c !== null && nl.test(c);
2627
}
2728

2829
function isNameStart(c){
29-
return c !== null && (/[a-z_\u0080-\uFFFF\\]/i.test(c));
30+
return c !== null && (/[a-z_\u00A0-\uFFFF\\]/i.test(c));
3031
}
3132

3233
function isNameChar(c){
@@ -213,6 +214,19 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
213214
token = this.htmlCommentStartToken(c, startLine, startCol);
214215
break;
215216

217+
/*
218+
* Potential tokens:
219+
* - IDENT
220+
* - CHAR
221+
*/
222+
case "\\":
223+
if (/[^\r\n\f]/.test(reader.peek())) {
224+
token = this.identOrFunctionToken(c, startLine, startCol);
225+
} else {
226+
token = this.charToken(c, startLine, startCol);
227+
}
228+
break;
229+
216230
/*
217231
* Potential tokens:
218232
* - UNICODE_RANGE
@@ -941,8 +955,13 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
941955

942956
while(true){
943957
if (c == "\\"){
944-
ident += this.readEscape(reader.read());
945-
c = reader.peek();
958+
if (/^[^\r\n\f]$/.test(reader.peek(2))) {
959+
ident += this.readEscape(reader.read(), true);
960+
c = reader.peek();
961+
} else {
962+
// Bad escape sequence.
963+
break;
964+
}
946965
} else if(c && isNameChar(c)){
947966
ident += reader.read();
948967
c = reader.peek();
@@ -954,7 +973,7 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
954973
return ident;
955974
},
956975

957-
readEscape: function(first){
976+
readEscape: function(first, unescape){
958977
var reader = this._reader,
959978
cssEscape = first || "",
960979
i = 0,
@@ -967,13 +986,31 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
967986
} while(c && isHexDigit(c) && ++i < 6);
968987
}
969988

970-
if (cssEscape.length == 3 && /\s/.test(c) ||
971-
cssEscape.length == 7 || cssEscape.length == 1){
989+
if (cssEscape.length === 1) {
990+
if (/^[^\r\n\f0-9a-f]$/.test(c)) {
972991
reader.read();
992+
if (unescape) { return c; }
993+
} else {
994+
// We should never get here (readName won't call readEscape
995+
// if the escape sequence is bad).
996+
throw new Error("Bad escape sequence.");
997+
}
998+
} else if (c === '\r') {
999+
reader.read();
1000+
if (reader.peek() === '\n') {
1001+
c += reader.read();
1002+
}
1003+
} else if (/^[ \t\n\f]$/.test(c)) {
1004+
reader.read();
9731005
} else {
9741006
c = "";
9751007
}
9761008

1009+
if (unescape) {
1010+
var cp = parseInt(cssEscape.slice(first.length), 16);
1011+
return String.fromCodePoint ? String.fromCodePoint(cp) :
1012+
String.fromCharCode(cp);
1013+
}
9771014
return cssEscape + c;
9781015
},
9791016

tests/css/Parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@
18361836

18371837
Assert.isInstanceOf(Selector, result, "Result should be an instance of Selector.");
18381838
Assert.isInstanceOf(SelectorPart, result.parts[0], "First part should be a SelectorPart.");
1839-
Assert.areEqual("#\\31 a2b3c", result.parts[0].toString(), "Selector should be correct.");
1839+
Assert.areEqual("#1a2b3c", result.parts[0].toString(), "Selector should be correct.");
18401840
Assert.areEqual(1, result.parts.length, "Should be one part.");
18411841
}
18421842

tests/css/TokenStream.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@
6161
" " : [CSSTokens.S],
6262
"\n" : [CSSTokens.S],
6363
"\n \t" : [CSSTokens.S],
64-
"\f \n" : [CSSTokens.S]
64+
"\f \n" : [CSSTokens.S],
65+
// Not legal whitespace (PR#16)
66+
"\v\u00A0\u1680": [CSSTokens.CHAR, CSSTokens.IDENT]
6567
}
6668
}));
6769

@@ -136,7 +138,9 @@
136138
"#h\\0fllo" : [CSSTokens.HASH],
137139
"#ffeeff" : [CSSTokens.HASH],
138140
"#\\31 a2b3c" : [CSSTokens.HASH],
139-
"#r0\\.5" : [CSSTokens.HASH]
141+
"#r0\\.5" : [CSSTokens.HASH],
142+
// Invalid escape sequence
143+
"#a\\\r" : [CSSTokens.HASH, CSSTokens.CHAR, CSSTokens.S]
140144
}
141145
}));
142146

@@ -148,6 +152,7 @@
148152

149153
var atRules = {
150154
"@charset" : CSSTokens.CHARSET_SYM,
155+
"@ch\\041 rset" : CSSTokens.CHARSET_SYM,
151156
"@import" : CSSTokens.IMPORT_SYM,
152157
"@page" : CSSTokens.PAGE_SYM,
153158
"@media" : CSSTokens.MEDIA_SYM,

0 commit comments

Comments
 (0)