• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013, Mike Samuel
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
6 // are met:
7 //
8 // Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // Neither the name of the OWASP nor the names of its contributors may
14 // be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 
29 package org.owasp.html;
30 
31 import java.util.Arrays;
32 import java.util.List;
33 
34 import junit.framework.TestCase;
35 
36 import org.junit.Test;
37 import org.owasp.html.CssTokens.TokenType;
38 
39 import com.google.common.collect.Lists;
40 
41 import static org.owasp.html.CssTokens.TokenType.*;
42 
43 public class CssTokensTest extends TestCase {
44 
lex(String s)45   private static CssTokens lex(String s) {
46     CssTokens tokens = CssTokens.lex(s);
47     // Check that lexing is idempotent.
48     assertEquals(
49         "`" + s + "` not idempotent",
50         tokens.normalizedCss,
51         CssTokens.lex(tokens.normalizedCss).normalizedCss);
52     return tokens;
53   }
54 
55   @Test
testBracketIndices()56   public static final void testBracketIndices() {
57     CssTokens tokens = lex("([foo[[||]])");
58     assertEquals("([foo[[||]]])", tokens.normalizedCss);
59 
60     List<String> tokenTexts = Lists.newArrayList();
61     List<CssTokens.TokenType> types = Lists.newArrayList();
62     List<Integer> partners = Lists.newArrayList();
63     for (CssTokens.TokenIterator it = tokens.iterator(); it.hasNext();) {
64       types.add(it.type());
65       partners.add(tokens.brackets.partner(it.tokenIndex()));
66       tokenTexts.add(it.next());
67     }
68     assertEquals(
69         Arrays.asList("(", "[", "foo", "[", "[", "||", "]", "]", "]", ")"),
70         tokenTexts);
71     assertEquals(
72         Arrays.asList(
73             LEFT_PAREN, LEFT_SQUARE, IDENT, LEFT_SQUARE, LEFT_SQUARE, COLUMN,
74             RIGHT_SQUARE, RIGHT_SQUARE, RIGHT_SQUARE, RIGHT_PAREN),
75         types);
76     // ([foo[[||]]])
77     // 012  345 6789
78     assertEquals(
79         Arrays.asList(9, 8, -1, 7, 6, -1, 4, 3, 1, 0),
80         partners);
81   }
82 
83   @Test
testStringEscaping()84   public static final void testStringEscaping() throws Exception {
85     // input                         golden
86     String[] tests = {
87         "''",                          "''",
88         "\"\"",                        "''",
89         "\"\\a\"",                     "'\\a'",
90         "\"\\0d\\0a\"",                "'\\d\\a'",
91         "'\\000000d'",                 "'\\0 d'",   // too many hex digits
92         "'\\1fffff'",                  "'\ufffd'",  // exceeeds max codepoint
93         "\"'\"",                       "'\\27'",
94         "\"\\\"\"",                    "'\\22'",
95         "'\\\\'",                      "'\\\\'",
96         "'-->'",                       "'--\\3e'",
97         "'</style>'",                  "'\\3c/style\\3e'",
98         "\"<![CDATA[...]]>\"",         "'\\3c![CDATA[...]]\\3e'",
99         "\"&quot;/*\"",                "'\\26quot;/*'",
100         "\"\u0000AB\"",                "'\\0 AB'",
101         "\"\u0000 AB\"",               "'\\0  AB'",
102         "\"\u0000\\000020AB\"",        "'\\0  AB'",
103         "\"\u0000\\000009AB\"",        "'\\0 \tAB'",
104         "\"",                          null,
105         "'",                           null,
106         "\"\n",                        null,
107         "\"\r",                        null,
108         "'\f",                         null,
109         "'\\22",                       null,
110         "'foo\\\n",                    null,
111         "'foo\\\r\n",                  null,
112         "//\\a'foo'",                  null,
113         "/*'foo\\2a/'//*/",            null,
114     };
115     for (int i = 0, n = tests.length; i < n; i += 2) {
116       String input = tests[i],
117           golden = tests[i+1];
118       CssTokens tokens = lex(input);
119       assertEquals(input, golden != null ? golden : "", tokens.normalizedCss);
120       CssTokens.TokenIterator it = tokens.iterator();
121       assertEquals(input, it.hasNext(), golden != null);
122       if (golden != null) {
123         assertEquals(input, STRING, it.type());
124         assertEquals(input, golden, it.next());
125         assertFalse(input, it.hasNext());
126       }
127     }
128   }
129 
130   @Test
testComments()131   public static final void testComments() throws Exception {
132     assertEquals(
133         "a b c d e f g h",
134         lex(
135             "//\na/*z*/b//z*/z\\az\nc/*z/**/d//*/\f/**/e/***/f/*//*/g/*z**z*/h"
136             ).normalizedCss);
137   }
138 
139   @Test
testNonCommentSlash()140   public static final void testNonCommentSlash() throws Exception {
141     assertEquals("foo/ bar/", lex("foo/bar/").normalizedCss);
142   }
143 
144   @Test
testCdoCdc()145   public static final void testCdoCdc() throws Exception {
146     assertEquals(
147         "|| and are ignorable||",
148         lex("||<!-- and --> are ignorable||").normalizedCss);
149     assertEquals(
150         "< !-- and -- > are not ignorable",
151         lex("<!-\\- and -\\-> are not ignorable").normalizedCss);
152   }
153 
154   @Test
testIdentReencoding()155   public static final void testIdentReencoding() throws Exception {
156     // input                         golden
157     String[] tests = {
158         "\\",                        null,
159         "a",                         "a",
160         "\\61",                      "a",
161         "\\061",                     "a",
162         "\\0061",                    "a",
163         "\\00061",                   "a",
164         "\\000061",                  "a",
165         // First character is not an identifier part.
166         "\\0000061",                 "61:NUMBER",
167         "\\61 b",                    "ab",
168         "\\61\tb",                   "ab",
169         "\\61\nb",                   "ab",
170         "\\61\fb",                   "ab",
171         "\\61\rb",                   "ab",
172         "ab",                        "ab",
173         "_ab",                       "_ab",
174         "_42",                       "_42",
175         "foo-bar",                   "foo-bar",
176         "-foo-bar",                  "-foo-bar",
177         "\\2d foo-bar",              "-foo-bar",
178         "-\\66oo-bar",               "-foo-bar",
179         // \\5c66 is a single escape sequence, not \\5c66 -> \\66 -> f .
180         "\\5c66oo-bar",              "\u5c66" + "oo-bar",
181         "\\22foo-bar",               "\u022f" + "oo-bar",
182         // \\5c is not a valid identifier
183         "\\5c",                      "5c:BAD_DIMENSION",
184         "\\22oo-bar",                "22oo-bar:BAD_DIMENSION",
185         "\\27oo-bar",                "27oo-bar:BAD_DIMENSION",
186         // \\34 encodes a digit so slash is dropped.
187         "\\34mm",                    "34mm:DIMENSION",
188         // Number ambiguity can arise when - is escaped.
189         // We disallow such ambiguity even in the encoded output since it is
190         // of little value, and a possible source of confusion.
191         // In these cases, the \\ is just dropped.
192         "-42",                       "-42:NUMBER",
193         "\\-42",                     "-42:NUMBER",
194     };
195     for (int i = 0, n = tests.length; i < n; i += 2) {
196       String input = tests[i],
197           golden = tests[i+1];
198       // Invalid escape sequences can lead to things that are not identifiers
199       // once error recovery happens.
200       CssTokens.TokenType type = IDENT;
201       if (golden != null) {
202         int colon = golden.lastIndexOf(':');
203         if (colon >= 0) {  // Unambiguous since : not allowed in identifier.
204           type = TokenType.valueOf(golden.substring(colon + 1));
205           golden = golden.substring(0, colon);
206         }
207       }
208       CssTokens tokens = lex(input);
209       assertEquals(input, golden != null ? golden : "", tokens.normalizedCss);
210       CssTokens.TokenIterator it = tokens.iterator();
211       assertEquals(input, it.hasNext(), golden != null);
212       if (golden != null) {
213         assertEquals(input, type, it.type());
214         assertEquals(input, golden, it.next());
215         assertFalse(input, it.hasNext());
216       }
217     }
218     // More number ambiguity.
219     assertTokens("\\2d 42", "2d:BAD_DIMENSION", " ", "42:NUMBER");
220     assertTokens("\\2d\t42", "2d:BAD_DIMENSION", " ", "42:NUMBER");
221     assertTokens("\\2d\n42", "2d:BAD_DIMENSION", " ", "42:NUMBER");
222   }
223 
224   @Test
testOrphanedCloseBrackets()225   public static final void testOrphanedCloseBrackets() throws Exception {
226     assertEquals("{foo bar}", lex("{foo]bar").normalizedCss);
227   }
228 
229   @Test
testAtDirectives()230   public static final void testAtDirectives() throws Exception {
231     assertTokens(
232         "@import \"foo/bar\"; @ at, @34",
233         "@import:AT", " ", "'foo/bar':STRING", ";:SEMICOLON",
234         " ", "@:DELIM", " ", "at:IDENT", ",:COMMA", " ",
235         "@:DELIM", " ", "34:NUMBER");
236   }
237 
238   @Test
testHash()239   public static final void testHash() throws Exception {
240     assertTokens(
241         "#fff #foo #-moz-foo #abcd #abcdef #012f34 #888 #42foo # #",
242         "#fff:HASH_UNRESTRICTED", " ",
243         "#foo:HASH_ID", " ",
244         "#-moz-foo:HASH_ID", " ",
245         "#abcd:HASH_UNRESTRICTED", " ",
246         "#abcdef:HASH_UNRESTRICTED", " ",
247         "#012f34:HASH_UNRESTRICTED", " ",
248         "#888:HASH_UNRESTRICTED", " ",
249         "#42foo:HASH_ID", " ",
250         "#:DELIM", " ", "#:DELIM");
251   }
252 
253   @Test
testSignsAndDots()254   public static final void testSignsAndDots() throws Exception {
255     assertTokens(
256         "- . + +1 + 1 (1 + 1)--> .5 -.5 +.5 ++.5 .foo -",
257         "-:IDENT", " ", ".:DELIM", " ", "+:DELIM", " ", "1:NUMBER", " ",
258         "+:DELIM", " ", "1:NUMBER", " ", "(:LEFT_PAREN", "1:NUMBER", " ",
259         "+:DELIM", " ", "1:NUMBER", "):RIGHT_PAREN", " ", "0.5:NUMBER", " ",
260         "-0.5:NUMBER", " ", "0.5:NUMBER", " ", "+:DELIM", " ", "0.5:NUMBER",
261         " ", ".foo:DOT_IDENT", " ", "-:IDENT");
262     // TODO: is a single "-" an IDENT or a DELIM?  "--"?  "---"?
263   }
264 
testMultiCharPunctuation()265   public static final void testMultiCharPunctuation() throws Exception {
266     assertTokens(
267         "|| ~= === |= =^= $= *= = : % & ~",
268         "||:COLUMN", " ", "~=:MATCH", " ", "=:DELIM", "=:DELIM", "=:DELIM", " ",
269         "|=:MATCH", " ", "=:DELIM", "^=:MATCH", " ", "$=:MATCH", " ",
270         "*=:MATCH", " ", "=:DELIM", " ", "::COLON", " ", "%:DELIM", " ",
271         "&:DELIM", " ", "~:DELIM");
272   }
273 
274   @Test
testNul()275   public static final void testNul() throws Exception {
276     assertTokens("\u0000");
277     assertTokens("\u0000x\u0000", "x:IDENT");
278   }
279 
280   @Test
testNumbers()281   public static final void testNumbers() throws Exception {
282     assertTokens(
283         "0 -0 +0 0.0 -0.0 -.0 0e12 0e-12 0e+12",
284         "0:NUMBER", " ",
285         "0:NUMBER", " ",
286         "0:NUMBER", " ",
287         "0:NUMBER", " ",
288         "0:NUMBER", " ",
289         "0:NUMBER", " ",
290         "0:NUMBER", " ",
291         "0:NUMBER", " ",
292         "0:NUMBER");
293     assertTokens(
294         "1 -1 +1 1.0 -1.0 -.1e1 10e-1 .1e+1",
295         "1:NUMBER", " ",
296         "-1:NUMBER", " ",
297         "1:NUMBER", " ",
298         "1:NUMBER", " ",
299         "-1:NUMBER", " ",
300         "-0.1e1:NUMBER", " ",
301         "10e-1:NUMBER", " ",
302         "0.1e1:NUMBER");
303     assertTokens(
304         ".1 -.1 +.1 0.1 -0.100 -.1e0 10e-2% .01e+01 IN",
305         "0.1:NUMBER", " ",
306         "-0.1:NUMBER", " ",
307         "0.1:NUMBER", " ",
308         "0.1:NUMBER", " ",
309         "-0.1:NUMBER", " ",
310         "-0.1:NUMBER", " ",
311         "10e-2%:PERCENTAGE", " ",
312         "0.01e1in:DIMENSION");
313     assertTokens("01234.567890", "1234.56789:NUMBER");
314   }
315 
316   @Test
testUrls()317   public static final void testUrls() throws Exception {
318     assertTokens(
319         "url() url('..')url( \"foo\" ) URL( f\"/(bar'\\\\baz ) url('foo \\a b')"
320         + "Url( \u0080\u1234\ud801\udc02\\110000)",
321         "url(''):URL", " ",
322         "url('..'):URL",
323         "url('foo'):URL", " ",
324         "url('f%22/%28bar%27%5cbaz'):URL", " ",
325         "url('foo%20%0ab'):URL",
326         "url('%c2%80%e1%88%b4%f0%90%90%82%ef%bf%bd'):URL"
327         );
328   }
329 
330   @Test
testFunctions()331   public static final void testFunctions() throws Exception {
332     assertTokens("( rgb(0,0,0) rgba(0,50%,0,100%)",
333         "(:LEFT_PAREN",
334         " ",
335         "rgb(:FUNCTION",
336         "0:NUMBER",
337         ",:COMMA",
338         "0:NUMBER",
339         ",:COMMA",
340         "0:NUMBER",
341         "):RIGHT_PAREN",
342         " ",
343         "rgba(:FUNCTION",
344         "0:NUMBER",
345         ",:COMMA",
346         "50%:PERCENTAGE",
347         ",:COMMA",
348         "0:NUMBER",
349         ",:COMMA",
350         "100%:PERCENTAGE",
351         "):RIGHT_PAREN",
352         "):RIGHT_PAREN");
353   }
354 
355   @Test
testUnicodeRanges()356   public static final void testUnicodeRanges() {
357     assertTokens(
358         "U+2028 U+000-49F U+2000-27FF U+2900-2BFF U+1D400-1D7FF"
359         + " u+ff?? u+d8??-dc??",
360         "U+2028:UNICODE_RANGE", " ",
361         "U+000-49f:UNICODE_RANGE", " ",
362         "U+2000-27ff:UNICODE_RANGE", " ",
363         "U+2900-2bff:UNICODE_RANGE", " ",
364         "U+1d400-1d7ff:UNICODE_RANGE", " ",
365         "U+ff??:UNICODE_RANGE", " ",
366         // Question-marked ranges cannot be dashed.
367         "U+d8??:UNICODE_RANGE", " ",
368         "-dc:IDENT",
369         "?:DELIM", "?:DELIM");
370     assertTokens(
371         "U+?",
372         "U:IDENT", "+:DELIM", " ", "?:DELIM");
373     // TODO: invalid code-units in unicode ranges, and out of order values.
374   }
375 
testTokenMerging()376   public static final void testTokenMerging() {
377     assertTokens(
378         "/\\* */", "/:DELIM", " ", "*:DELIM", " ", "*:DELIM", "/:DELIM");
379     assertTokens(
380         "/\\/", "/:DELIM", " ", "/:DELIM");
381     assertTokens(
382         "url\\('evil:magic()') uRl\\('.')",
383         // url is not an allowable identifier.
384         "(:LEFT_PAREN", "'evil:magic()':STRING", "):RIGHT_PAREN", " ",
385         "(:LEFT_PAREN", "'.':STRING", "):RIGHT_PAREN");
386     assertTokens(
387         "foo\\(1,2)",
388         "foo:IDENT",
389         " ",
390         // TODO: Should we be more aggressive with functions than just making
391         // sure there is a space between the name and a parenthesis?
392         "(:LEFT_PAREN", "1:NUMBER", ",:COMMA", "2:NUMBER",
393         "):RIGHT_PAREN");
394   }
395 
assertTokens(String css, String... goldens)396   private static final void assertTokens(String css, String... goldens) {
397     List<String> expected = Lists.newArrayList();
398     for (String golden : goldens) {
399       if (" ".equals(golden)) {
400         expected.add(" :" + WHITESPACE.name());
401       } else {
402         int colon = golden.lastIndexOf(':');
403         expected.add(
404             golden.substring(0, colon) + ":"
405             + CssTokens.TokenType.valueOf(golden.substring(colon+1)).name());
406       }
407     }
408     List<String> actual = Lists.newArrayList();
409     for (CssTokens.TokenIterator it = lex(css).iterator();
410          it.hasNext(); it.advance()) {
411       actual.add(it.token() + ":" + it.type());
412     }
413 
414     // Slightly better debugging output
415     assertEquals(css, expected.toString(), actual.toString());
416     // The real assertions
417     assertEquals(css, expected, actual);
418   }
419 
assertLexedCss(String input, String... goldens)420   private static void assertLexedCss(String input, String... goldens) {
421     List<String> actual = Lists.newArrayList();
422     for (String token : lex(input)) {
423       actual.add(token);
424     }
425     List<String> goldensList = Arrays.asList(goldens);
426     assertEquals(input, goldensList.toString(), actual.toString());
427     assertEquals(input, goldensList, actual);
428   }
429 
430   @Test
testLex01()431   public static final void testLex01() {
432     assertLexedCss(
433       "body {\n"
434       + "	color:green;\n"
435       + "}\r\n"
436       + "\n"
437       + "div#foo { content:\"bar{foo}\"; }",
438       "body", " ", "{", " ",
439       "color", ":", "green", ";", " ",
440       "}", " ",
441       "div", "#foo", " ", "{", " ", "content", ":", "'bar{foo}'", ";", " ", "}");
442   }
443 
444   @Test
testLex02()445   public static final void testLex02() {
446     assertLexedCss(
447       "body div {\n"
448       + "\tcolor:red;\n"
449       + "}\n",
450       "body", " ", "div", " ", "{", " ",
451       "color", ":", "red", ";", " ",
452       "}");
453   }
454 
455   @Test
testLex03()456   public static final void testLex03() {
457     assertLexedCss(
458       "div#foo { background:url(img/blubb.png) top left repeat-y; }\n"
459       + "\n"
460       + "body { font-family:Verdana, Geneva, Arial, Helvetica, sans-serif; font-size:12px; }\n"
461       + "\n"
462       + "@import url(\"foo.css\");\n"
463       + "\n"
464       + "@import \"bar.css\" screen;",
465       "div", "#foo", " ", "{",
466       " ", "background", ":", "url('img/blubb.png')",
467       " ", "top", " ", "left", " ", "repeat-y", ";", " ", "}", " ",
468       "body", " ", "{", " ", "font-family", ":", "Verdana", ",",
469       " ", "Geneva", ",", " ", "Arial", ",", " ", "Helvetica", ",",
470       " ", "sans-serif", ";",
471       " ", "font-size", ":", "12px", ";", " ", "}", " ",
472       "@import", " ", "url('foo.css')", ";", " ",
473       "@import", " ", "'bar.css'", " ", "screen", ";");
474   }
475 
476   @Test
testLex04()477   public static final void testLex04() {
478     assertLexedCss(
479       "\n"
480       + "\n"
481       + "/* Komentar! */\n"
482       + "@media projection {\n"
483       + "\t#blubb {\n"
484       + "\t\tfont-weight: /* Komentar! */ bold;\n"
485       + "\t\tcontent:\';{!\"\"())!\"\';\n"
486       + "\t}\n"
487       + "}\n"
488       + "#gnnf{\n"
489       + "\tbackground:green url(\'img/beispiel.png\') top left no-repeat;\n"
490       + "\ttext-align:left\n"
491       + "}",
492       "@media", " ", "projection", " ", "{", " ",
493       "#blubb", " ", "{", " ",
494       "font-weight", ":", " ", "bold", ";", " ",
495       "content", ":", "';{!\\22\\22())!\\22'", ";", " ",
496       "}", " ",
497       "}", " ",
498       "#gnnf", "{", " ",
499       "background", ":", "green", " ", "url('img/beispiel.png')",
500       " ", "top", " ", "left", " ", "no-repeat", ";", " ",
501       "text-align", ":", "left", " ",
502       "}");
503   }
504 
505   @Test
testLex05()506   public static final void testLex05() {
507     assertLexedCss(
508       "/**\n"
509       + " * FETTER Komentar!\n"
510       + " * \n"
511       + " * Bla bla bla\n"
512       + " */\n"
513       + "@media screen {\n"
514       + "\t#test[foo] {\n"
515       + "\t\tcolor:red !important;\n"
516       + "\t}\n"
517       + "\t#test[foo] {\n"
518       + "\t\tcolor:blue;\n"
519       + "\t}\n"
520       + "}",
521       "@media", " ", "screen", " ", "{", " ",
522       "#test", "[", "foo", "]", " ", "{", " ",
523       "color", ":", "red", " ", "!", "important", ";", " ",
524       "}", " ",
525       "#test", "[", "foo", "]", " ", "{", " ",
526       "color", ":", "blue", ";", " ",
527       "}", " ",
528       "}");
529   }
530 
531   @Test
testLex06()532   public static final void testLex06() {
533     assertLexedCss(
534       "#blah[rel=\"/{_-;!\"] div > #blargh span.narf {\n"
535       + "\tbackground:green;\n"
536       + "\ttext-align:left;\n"
537       + "}",
538       "#blah", "[", "rel", "=", "'/{_-;!'", "]", " ", "div",
539       " ", ">", " ", "#blargh", " ", "span", ".narf", " ", "{", " ",
540       "background", ":", "green", ";", " ",
541       "text-align", ":", "left", ";", " ",
542       "}");
543   }
544 
545   @Test
testLex07()546   public static final void testLex07() {
547     assertLexedCss(
548       "/* Komentar! */\n"
549       + "@media print {\n"
550       + "\t#gnarf {\n"
551       + "\t\tfont-weight:normal;\n"
552       + "\t\tfont-size:2em\n"
553       + "\t}\n"
554       + "}",
555       "@media", " ", "print", " ", "{", " ",
556       "#gnarf", " ", "{", " ",
557       "font-weight", ":", "normal", ";", " ",
558       "font-size", ":", "2em", " ",
559       "}", " ",
560       "}");
561   }
562 
563   @Test
testLex08()564   public static final void testLex08() {
565     assertLexedCss(
566       "#foobar {\n"
567       + "\tfont-family:\"Trebuchet MS\", Verdana, Arial, sans-serif;\n"
568       + "}",
569       "#foobar", " ", "{", " ",
570       "font-family", ":", "'Trebuchet MS'", ",", " ", "Verdana", ",",
571       " ", "Arial", ",", " ", "sans-serif", ";", " ",
572       "}");
573   }
574 
575   @Test
testLex09()576   public static final void testLex09() {
577     assertLexedCss(
578       "p { color:red !important; }\n"
579       + ".foo { color:green; }",
580       "p", " ", "{", " ", "color", ":", "red", " ", "!", "important", ";",
581       " ", "}", " ",
582       ".foo", " ", "{", " ", "color", ":", "green", ";", " ", "}");
583   }
584 
585   @Test
testLex10()586   public static final void testLex10() {
587     assertLexedCss(
588       "@media screen{\n"
589       + "\t#wrapper {\n"
590       + "\t\tcolor:blue;\n"
591       + "\t\tfont-weight:bold !important;\n"
592       + "\t\ttext-decoration:underline;\n"
593       + "\t}\n"
594       + "\t#wrapper {\n"
595       + "\t\tcolor:red;\n"
596       + "\t\tfont-weight:normal;\n"
597       + "\t\tfont-style:italic;\n"
598       + "\t}\n"
599       + "}\n"
600       + "\n"
601       + "@media print {\n"
602       + "\t#wrapper {\n"
603       + "\t\tcolor:green;\n"
604       + "\t}\n"
605       + "}",
606       "@media", " ", "screen", "{", " ",
607       "#wrapper", " ", "{", " ",
608       "color", ":", "blue", ";", " ",
609       "font-weight", ":", "bold", " ", "!", "important", ";", " ",
610       "text-decoration", ":", "underline", ";", " ",
611       "}", " ",
612       "#wrapper", " ", "{", " ",
613       "color", ":", "red", ";", " ",
614       "font-weight", ":", "normal", ";", " ",
615       "font-style", ":", "italic", ";", " ",
616       "}", " ",
617       "}", " ",
618       "@media", " ", "print", " ", "{", " ",
619       "#wrapper", " ", "{", " ",
620       "color", ":", "green", ";", " ",
621       "}", " ",
622       "}");
623   }
624 
625   @Test
testLex11()626   public static final void testLex11() {
627     assertLexedCss(
628       "\n"
629       + "ADDRESS,\n"
630       + "BLOCKQUOTE, \n"
631       + "BODY, DD, DIV, \n"
632       + "DL, DT, \n"
633       + "FIELDSET, FORM,\n"
634       + "FRAME, FRAMESET,\n"
635       + "H1, H2, H3, H4, \n"
636       + "H5, H6, IFRAME, \n"
637       + "NOFRAMES, \n"
638       + "OBJECT, OL, P, \n"
639       + "UL, APPLET, \n"
640       + "CENTER, DIR, \n"
641       + "HR, MENU, PRE   { display: block }\n"
642       + "LI              { display: list-item }\n"
643       + "HEAD            { display: none }\n"
644       + "TABLE           { display: table }\n"
645       + "TR              { display: table-row }\n"
646       + "THEAD           { display: table-header-group }\n"
647       + "TBODY           { display: table-row-group }\n"
648       + "TFOOT           { display: table-footer-group }\n"
649       + "COL             { display: table-column }\n"
650       + "COLGROUP        { display: table-column-group }\n"
651       + "TD, TH          { display: table-cell }\n"
652       + "CAPTION         { display: table-caption }\n"
653       + "TH              { font-weight: bolder; text-align: center }\n"
654       + "CAPTION         { text-align: center }\n"
655       + "BODY            { padding: 8px; line-height: 1.33 }\n"
656       + "H1              { font-size: 2em; margin: .67em 0 }\n"
657       + "H2              { font-size: 1.5em; margin: .83em 0 }\n"
658       + "H3              { font-size: 1.17em; margin: 1em 0 }\n"
659       + "H4, P,\n"
660       + "BLOCKQUOTE, UL,\n"
661       + "FIELDSET, FORM,\n"
662       + "OL, DL, DIR,\n"
663       + "MENU            { margin: 1.33em 0 }\n"
664       + "H5              { font-size: .83em; line-height: 1.17em; margin: 1.67em 0 }\n"
665       + "H6              { font-size: .67em; margin: 2.33em 0 }\n"
666       + "H1, H2, H3, H4,\n"
667       + "H5, H6, B,\n"
668       + "STRONG          { font-weight: bolder }\n"
669       + "BLOCKQUOTE      { margin-left: 40px; margin-right: 40px }\n"
670       + "I, CITE, EM,\n"
671       + "VAR, ADDRESS    { font-style: italic }\n"
672       + "PRE, TT, CODE,\n"
673       + "KBD, SAMP       { font-family: monospace }\n"
674       + "PRE             { white-space: pre }\n"
675       + "BIG             { font-size: 1.17em }\n"
676       + "SMALL, SUB, SUP { font-size: .83em }\n"
677       + "SUB             { vertical-align: sub }\n"
678       + "SUP             { vertical-align: super }\n"
679       + "S, STRIKE, DEL  { text-decoration: line-through }\n"
680       + "HR              { border: 1px inset }\n"
681       + "OL, UL, DIR,\n"
682       + "MENU, DD        { margin-left: 40px }\n"
683       + "OL              { list-style-type: decimal }\n"
684       + "OL UL, UL OL,\n"
685       + "UL UL, OL OL    { margin-top: 0; margin-bottom: 0 }\n"
686       + "U, INS          { text-decoration: underline }\n"
687       + "CENTER          { text-align: center }\n"
688       + "BR:before       { content: \"\\A\" }\n"
689       + "COLOR_NOHASH\t{ color:987E81 }",
690       "ADDRESS", ",", " ",
691       "BLOCKQUOTE", ",", " ",
692       "BODY", ",", " ", "DD", ",", " ", "DIV", ",", " ",
693       "DL", ",", " ", "DT", ",", " ",
694       "FIELDSET", ",", " ", "FORM", ",", " ",
695       "FRAME", ",", " ", "FRAMESET", ",", " ",
696       "H1", ",", " ", "H2", ",", " ", "H3", ",", " ", "H4", ",", " ",
697       "H5", ",", " ", "H6", ",", " ", "IFRAME", ",", " ",
698       "NOFRAMES", ",", " ",
699       "OBJECT", ",", " ", "OL", ",", " ", "P", ",", " ",
700       "UL", ",", " ", "APPLET", ",", " ",
701       "CENTER", ",", " ", "DIR", ",", " ",
702       "HR", ",", " ", "MENU", ",", " ", "PRE", " ", "{",
703       " ", "display", ":", " ", "block", " ", "}", " ",
704       "LI", " ", "{", " ", "display", ":", " ", "list-item", " ", "}", " ",
705       "HEAD", " ", "{", " ", "display", ":", " ", "none", " ", "}", " ",
706       "TABLE", " ", "{", " ", "display", ":", " ", "table", " ", "}", " ",
707       "TR", " ", "{", " ", "display", ":", " ", "table-row", " ", "}", " ",
708       "THEAD", " ", "{",
709       " ", "display", ":", " ", "table-header-group", " ", "}", " ",
710       "TBODY", " ", "{",
711       " ", "display", ":", " ", "table-row-group", " ", "}", " ",
712       "TFOOT", " ", "{",
713       " ", "display", ":", " ", "table-footer-group", " ", "}", " ",
714       "COL", " ", "{", " ", "display", ":", " ", "table-column", " ", "}", " ",
715       "COLGROUP", " ", "{",
716       " ", "display", ":", " ", "table-column-group", " ", "}", " ",
717       "TD", ",", " ", "TH", " ", "{",
718       " ", "display", ":", " ", "table-cell", " ", "}", " ",
719       "CAPTION", " ", "{",
720       " ", "display", ":", " ", "table-caption", " ", "}", " ",
721       "TH", " ", "{",
722       " ", "font-weight", ":", " ", "bolder", ";",
723       " ", "text-align", ":", " ", "center", " ", "}", " ",
724       "CAPTION", " ", "{",
725       " ", "text-align", ":", " ", "center", " ", "}", " ",
726       "BODY", " ", "{",
727       " ", "padding", ":", " ", "8px", ";",
728       " ", "line-height", ":", " ", "1.33", " ", "}", " ",
729       "H1", " ", "{",
730       " ", "font-size", ":", " ", "2em", ";",
731       " ", "margin", ":", " ", "0.67em", " ", "0", " ", "}", " ",
732       "H2", " ", "{",
733       " ", "font-size", ":", " ", "1.5em", ";",
734       " ", "margin", ":", " ", "0.83em", " ", "0", " ", "}", " ",
735       "H3", " ", "{",
736       " ", "font-size", ":", " ", "1.17em", ";",
737       " ", "margin", ":", " ", "1em", " ", "0", " ", "}", " ",
738       "H4", ",", " ", "P", ",", " ",
739       "BLOCKQUOTE", ",", " ", "UL", ",", " ",
740       "FIELDSET", ",", " ", "FORM", ",", " ",
741       "OL", ",", " ", "DL", ",", " ", "DIR", ",", " ",
742       "MENU", " ", "{", " ", "margin", ":", " ", "1.33em", " ", "0", " ", "}", " ",
743       "H5", " ", "{", " ", "font-size", ":", " ", "0.83em", ";",
744       " ", "line-height", ":", " ", "1.17em",  ";",
745       " ", "margin", ":", " ", "1.67em", " ", "0", " ", "}", " ",
746       "H6", " ", "{", " ", "font-size", ":", " ", "0.67em", ";",
747       " ", "margin", ":", " ", "2.33em", " ", "0", " ", "}", " ",
748       "H1", ",", " ", "H2", ",", " ", "H3", ",", " ", "H4", ",", " ",
749       "H5", ",", " ", "H6", ",", " ", "B", ",", " ",
750       "STRONG", " ", "{", " ", "font-weight", ":", " ", "bolder", " ", "}", " ",
751       "BLOCKQUOTE", " ", "{", " ", "margin-left", ":", " ", "40px", ";",
752       " ", "margin-right", ":", " ", "40px", " ", "}", " ",
753       "I", ",", " ", "CITE", ",", " ", "EM", ",", " ",
754       "VAR", ",", " ", "ADDRESS", " ", "{",
755       " ", "font-style", ":", " ", "italic", " ", "}", " ",
756       "PRE", ",", " ", "TT", ",", " ", "CODE", ",", " ",
757       "KBD", ",", " ", "SAMP", " ", "{",
758       " ", "font-family", ":", " ", "monospace", " ", "}", " ",
759       "PRE", " ", "{", " ", "white-space", ":", " ", "pre", " ", "}", " ",
760       "BIG", " ", "{", " ", "font-size", ":", " ", "1.17em", " ", "}", " ",
761       "SMALL", ",", " ", "SUB", ",", " ", "SUP", " ", "{", " ", "font-size",
762       ":", " ", "0.83em", " ", "}", " ",
763       "SUB", " ", "{", " ", "vertical-align", ":", " ", "sub", " ", "}", " ",
764       "SUP", " ", "{", " ", "vertical-align", ":", " ", "super", " ", "}", " ",
765       "S", ",", " ", "STRIKE", ",", " ", "DEL", " ", "{",
766       " ", "text-decoration", ":", " ", "line-through", " ", "}", " ",
767       "HR", " ", "{", " ", "border", ":", " ", "1px", " ", "inset", " ", "}", " ",
768       "OL", ",", " ", "UL", ",", " ", "DIR", ",", " ",
769       "MENU", ",", " ", "DD", " ", "{",
770       " ", "margin-left", ":", " ", "40px", " ", "}", " ",
771       "OL", " ", "{", " ", "list-style-type", ":", " ", "decimal", " ", "}", " ",
772       "OL", " ", "UL", ",", " ", "UL", " ", "OL", ",", " ",
773       "UL", " ", "UL", ",", " ", "OL", " ", "OL", " ", "{",
774       " ", "margin-top", ":", " ", "0", ";",
775       " ", "margin-bottom", ":", " ", "0", " ", "}", " ",
776       "U", ",", " ", "INS", " ", "{",
777       " ", "text-decoration", ":", " ", "underline", " ", "}", " ",
778       "CENTER", " ", "{", " ", "text-align", ":", " ", "center", " ", "}", " ",
779       "BR", ":", "before", " ", "{",
780       " ", "content", ":", " ", "'\\a'", " ", "}", " ",
781       "COLOR_NOHASH", " ", "{", " ", "color", ":", "987e81", " ", "}");
782   }
783 
784   @Test
testLex12()785   public static final void testLex12() {
786     assertLexedCss(
787       "/* An example of style for HTML 4.0\'s ABBR/ACRONYM elements */\n"
788       + "\n"
789       + "ABBR, ACRONYM   { font-variant: small-caps; letter-spacing: 0.1em }\n"
790       + "A[href]         { text-decoration: underline }\n"
791       + ":focus          { outline: thin dotted invert }",
792       "ABBR", ",", " ", "ACRONYM", " ", "{",
793       " ", "font-variant", ":", " ", "small-caps", ";",
794       " ", "letter-spacing", ":", " ", "0.1em", " ", "}", " ",
795       "A", "[", "href", "]", " ", "{",
796       " ", "text-decoration", ":", " ", "underline", " ", "}", " ",
797       ":", "focus", " ", "{",
798       " ", "outline", ":", " ", "thin", " ", "dotted", " ", "invert", " ", "}");
799   }
800 
801   @Test
testLex13()802   public static final void testLex13() {
803     assertLexedCss(
804       "/* Begin bidirectionality settings (do not change) */\n"
805       + "BDO[DIR=\"ltr\"]  { direction: ltr; unicode-bidi: bidi-override }\n"
806       + "BDO[DIR=\"rtl\"]  { direction: rtl; unicode-bidi: bidi-override }\n"
807       + "\n"
808       + "*[DIR=\"ltr\"]    { direction: ltr; unicode-bidi: embed }\n"
809       + "*[DIR=\"rtl\"]    { direction: rtl; unicode-bidi: embed }\n"
810       + "\n"
811       + "/* Elements that are block-level in HTML4 */\n"
812       + "ADDRESS, BLOCKQUOTE, BODY, DD, DIV, DL, DT, FIELDSET, \n"
813       + "FORM, FRAME, FRAMESET, H1, H2, H3, H4, H5, H6, IFRAME,\n"
814       + "NOSCRIPT, NOFRAMES, OBJECT, OL, P, UL, APPLET, CENTER, \n"
815       + "DIR, HR, MENU, PRE, LI, TABLE, TR, THEAD, TBODY, TFOOT, \n"
816       + "COL, COLGROUP, TD, TH, CAPTION \n"
817       + "                { unicode-bidi: embed }\n"
818       + "/* End bidi settings */",
819       "BDO", "[", "DIR", "=", "'ltr'", "]", " ", "{",
820       " ", "direction", ":", " ", "ltr", ";",
821       " ", "unicode-bidi", ":", " ", "bidi-override", " ", "}", " ",
822       "BDO", "[", "DIR", "=", "'rtl'", "]", " ", "{",
823       " ", "direction", ":", " ", "rtl", ";",
824       " ", "unicode-bidi", ":", " ", "bidi-override", " ", "}", " ",
825       "*", "[", "DIR", "=", "'ltr'", "]", " ", "{",
826       " ", "direction", ":", " ", "ltr", ";",
827       " ", "unicode-bidi", ":", " ", "embed", " ", "}", " ",
828       "*", "[", "DIR", "=", "'rtl'", "]", " ", "{",
829       " ", "direction", ":", " ", "rtl", ";",
830       " ", "unicode-bidi", ":", " ", "embed", " ", "}", " ",
831       "ADDRESS", ",", " ", "BLOCKQUOTE", ",", " ", "BODY", ",",
832       " ", "DD", ",", " ", "DIV", ",", " ", "DL", ",", " ", "DT", ",",
833       " ", "FIELDSET", ",", " ",
834       "FORM", ",", " ", "FRAME", ",", " ", "FRAMESET", ",", " ", "H1", ",",
835       " ", "H2", ",", " ", "H3", ",", " ", "H4", ",", " ", "H5", ",",
836       " ", "H6", ",", " ", "IFRAME", ",", " ",
837       "NOSCRIPT", ",", " ", "NOFRAMES", ",", " ", "OBJECT", ",",
838       " ", "OL", ",", " ", "P", ",", " ", "UL", ",", " ", "APPLET", ",",
839       " ", "CENTER", ",", " ",
840       "DIR", ",", " ", "HR", ",", " ", "MENU", ",", " ", "PRE", ",",
841       " ", "LI", ",", " ", "TABLE", ",", " ", "TR", ",", " ", "THEAD", ",",
842       " ", "TBODY", ",", " ", "TFOOT", ",", " ",
843       "COL", ",", " ", "COLGROUP", ",", " ", "TD", ",", " ", "TH", ",",
844       " ", "CAPTION", " ",
845       "{", " ", "unicode-bidi", ":", " ", "embed", " ", "}");
846   }
847 
848   @Test
testLex14()849   public static final void testLex14() {
850     assertLexedCss(
851       "\n"
852       + "@media print {\n"
853       + "  /* @page         { margin: 10% }  */ /* not allowed according to spec */\n"
854       + "  H1, H2, H3,\n"
855       + "  H4, H5, H6    { page-break-after: avoid; page-break-inside: avoid }\n"
856       + "  BLOCKQUOTE, \n"
857       + "  PRE           { page-break-inside: avoid }\n"
858       + "  UL, OL, DL    { page-break-before: avoid }\n"
859       + "}",
860       "@media", " ", "print", " ", "{", " ",
861       "H1", ",", " ", "H2", ",", " ", "H3", ",", " ",
862       "H4", ",", " ", "H5", ",", " ", "H6", " ", "{",
863       " ", "page-break-after", ":", " ", "avoid", ";",
864       " ", "page-break-inside", ":", " ", "avoid", " ", "}", " ",
865       "BLOCKQUOTE", ",", " ",
866       "PRE", " ", "{", " ", "page-break-inside", ":", " ", "avoid", " ", "}", " ",
867       "UL", ",", " ", "OL", ",", " ", "DL", " ", "{",
868       " ", "page-break-before", ":", " ", "avoid", " ", "}", " ",
869       "}");
870   }
871 
872   @Test
testLex15()873   public static final void testLex15() {
874     assertLexedCss(
875       "@media speech {\n"
876       + "  H1, H2, H3, \n"
877       + "  H4, H5, H6    { voice-family: paul, male; stress: 20; richness: 90 }\n"
878       + "  H1            { pitch: x-low; pitch-range: 90 }\n"
879       + "  H2            { pitch: x-low; pitch-range: 80 }\n"
880       + "  H3            { pitch: low; pitch-range: 70 }\n"
881       + "  H4            { pitch: medium; pitch-range: 60 }\n"
882       + "  H5            { pitch: medium; pitch-range: 50 }\n"
883       + "  H6            { pitch: medium; pitch-range: 40 }\n"
884       + "  LI, DT, DD    { pitch: medium; richness: 60 }\n"
885       + "  DT            { stress: 80 }\n"
886       + "  PRE, CODE, TT { pitch: medium; pitch-range: 0; stress: 0; richness: 80 }\n"
887       + "  EM            { pitch: medium; pitch-range: 60; stress: 60; richness: 50 }\n"
888       + "  STRONG        { pitch: medium; pitch-range: 60; stress: 90; richness: 90 }\n"
889       + "  DFN           { pitch: high; pitch-range: 60; stress: 60 }\n"
890       + "  S, STRIKE     { richness: 0 }\n"
891       + "  I             { pitch: medium; pitch-range: 60; stress: 60; richness: 50 }\n"
892       + "  B             { pitch: medium; pitch-range: 60; stress: 90; richness: 90 }\n"
893       + "  U             { richness: 0 }\n"
894       + "  A:link        { voice-family: harry, male }\n"
895       + "  A:visited     { voice-family: betty, female }\n"
896       + "  A:active      { voice-family: betty, female; pitch-range: 80; pitch: x-high }\n"
897       + "}",
898       "@media", " ", "speech", " ", "{", " ",
899       "H1", ",", " ", "H2", ",", " ", "H3", ",", " ",
900       "H4", ",", " ", "H5", ",", " ", "H6",
901       " ", "{", " ", "voice-family", ":", " ", "paul", ",", " ", "male", ";",
902       " ", "stress", ":", " ", "20", ";", " ", "richness", ":", " ", "90",
903       " ", "}", " ",
904       "H1", " ", "{", " ", "pitch", ":", " ", "x-low", ";",
905       " ", "pitch-range", ":", " ", "90", " ", "}", " ",
906       "H2", " ", "{", " ", "pitch", ":", " ", "x-low", ";",
907       " ", "pitch-range", ":", " ", "80", " ", "}", " ",
908       "H3", " ", "{", " ", "pitch", ":", " ", "low", ";",
909       " ", "pitch-range", ":", " ", "70", " ", "}", " ",
910       "H4", " ", "{", " ", "pitch", ":", " ", "medium", ";",
911       " ", "pitch-range", ":", " ", "60", " ", "}", " ",
912       "H5", " ", "{", " ", "pitch", ":", " ", "medium", ";",
913       " ", "pitch-range", ":", " ", "50", " ", "}", " ",
914       "H6", " ", "{", " ", "pitch", ":", " ", "medium", ";",
915       " ", "pitch-range", ":", " ", "40", " ", "}", " ",
916       "LI", ",", " ", "DT", ",", " ", "DD", " ", "{",
917       " ", "pitch", ":", " ", "medium", ";",
918       " ", "richness", ":", " ", "60", " ", "}", " ",
919       "DT", " ", "{", " ", "stress", ":", " ", "80", " ", "}", " ",
920       "PRE", ",", " ", "CODE", ",", " ", "TT", " ", "{",
921       " ", "pitch", ":", " ", "medium", ";",
922       " ", "pitch-range", ":", " ", "0", ";",
923       " ", "stress", ":", " ", "0", ";",
924       " ", "richness", ":", " ", "80", " ", "}", " ",
925       "EM", " ", "{", " ", "pitch", ":", " ", "medium", ";",
926       " ", "pitch-range", ":", " ", "60", ";",
927       " ", "stress", ":", " ", "60", ";",
928       " ", "richness", ":", " ", "50", " ", "}", " ",
929       "STRONG", " ", "{", " ", "pitch", ":", " ", "medium", ";",
930       " ", "pitch-range", ":", " ", "60", ";",
931       " ", "stress", ":", " ", "90", ";",
932       " ", "richness", ":", " ", "90", " ", "}", " ",
933       "DFN", " ", "{", " ", "pitch", ":", " ", "high", ";",
934       " ", "pitch-range", ":", " ", "60", ";", " ",
935       "stress", ":", " ", "60", " ", "}", " ",
936       "S", ",", " ", "STRIKE", " ", "{",
937       " ", "richness", ":", " ", "0", " ", "}", " ",
938       "I", " ", "{", " ", "pitch", ":", " ", "medium", ";",
939       " ", "pitch-range", ":", " ", "60", ";",
940       " ", "stress", ":", " ", "60", ";",
941       " ", "richness", ":", " ", "50", " ", "}", " ",
942       "B", " ", "{", " ", "pitch", ":", " ", "medium", ";",
943       " ", "pitch-range", ":", " ", "60", ";",
944       " ", "stress", ":", " ", "90", ";",
945       " ", "richness", ":", " ", "90", " ", "}", " ",
946       "U", " ", "{", " ", "richness", ":", " ", "0", " ", "}", " ",
947       "A", ":", "link", " ", "{",
948       " ", "voice-family", ":", " ", "harry", ",", " ", "male", " ", "}", " ",
949       "A", ":", "visited", " ", "{",
950       " ", "voice-family", ":", " ", "betty", ",", " ", "female", " ", "}", " ",
951       "A", ":", "active", " ", "{",
952       " ", "voice-family", ":", " ", "betty", ",", " ", "female", ";",
953       " ", "pitch-range", ":", " ", "80", ";",
954       " ", "pitch", ":", " ", "x-high", " ", "}", " ",
955       "}");
956   }
957 
958   @Test
testLex16()959   public static final void testLex16() {
960     assertLexedCss(
961       "FOO > BAR + BAZ {  }",
962       "FOO", " ", ">", " ", "BAR", " ", "+", " ", "BAZ", " ", "{", " ", "}");
963   }
964 
965   @Test
testLex17()966   public static final void testLex17() {
967     assertLexedCss(
968       "A[href] BOO[zwop |= \"hello\"]:blinky {\n"
969       + "  color: #fff;\n"
970       + "  background: +#000000 ! important\n"
971       + "}",
972       "A", "[", "href", "]",
973       " ", "BOO", "[", "zwop", " ", "|=", " ", "'hello'", "]", ":", "blinky",
974       " ", "{", " ",
975       "color", ":", " ", "#fff", ";", " ",
976       "background", ":", " ", "+", " ", "#000000", " ", "!", " ", "important",
977       " ",
978       "}");
979   }
980 
981   @Test
testLex18()982   public static final void testLex18() {
983     assertLexedCss(
984       ".myclass[attr ~= almost] #id:hover(languidly) {\n"
985       + "  font-weight: super(bold / italic)\n"
986       + "}",
987       ".myclass", "[", "attr", " ", "~=", " ", "almost", "]",
988       " ", "#id", ":", "hover(", "languidly", ")", " ", "{", " ",
989       "font-weight", ":", " ", "super(", "bold", " ", "/", " ", "italic", ")",
990       " ", "}");
991   }
992 
993   @Test
testLex19()994   public static final void testLex19() {
995     assertLexedCss(
996       "/* The RHS of the attribute comparison operators parse to quoted\n"
997       + " * parse to quoted strings since they are surrounded by quotes. */\n"
998       + "foo[attr = \'bar\'] {}\n"
999       + "foo[attr = \"bar\"] {}\n"
1000       + "foo[attr ~= \'bar baz\'] {}\n"
1001       + "foo[attr |= \'bar-baz\'] {}",
1002       "foo", "[", "attr", " ", "=", " ", "'bar'", "]", " ", "{", "}", " ",
1003       "foo", "[", "attr", " ", "=", " ", "'bar'", "]", " ", "{", "}", " ",
1004       "foo", "[", "attr", " ", "~=", " ", "'bar baz'", "]", " ", "{", "}", " ",
1005       "foo", "[", "attr", " ", "|=", " ", "'bar-baz'", "]", " ", "{", "}");
1006   }
1007 
1008   @Test
testLex20()1009   public static final void testLex20() {
1010     assertLexedCss(
1011       "/* The RHS of the attribute comparison operator in the following cases\n"
1012       + " * will parse to an IdentLiteral since it is unquoted. */\n"
1013       + "foo[attr = bar] {}\n"
1014       + "foo[attr |= bar-baz] {}",
1015       "foo", "[", "attr", " ", "=", " ", "bar", "]", " ", "{", "}", " ",
1016       "foo", "[", "attr", " ", "|=", " ", "bar-baz", "]", " ", "{", "}");
1017   }
1018 
1019   @Test
testLex21()1020   public static final void testLex21() {
1021     assertLexedCss(
1022       "foo.bar { }",
1023       "foo", ".bar", " ", "{", " ", "}");
1024   }
1025 
1026   @Test
testLex22()1027   public static final void testLex22() {
1028     assertLexedCss(
1029       "foo .bar { }",
1030       "foo", " ", ".bar", " ", "{", " ", "}");
1031   }
1032 
1033   @Test
testLex23()1034   public static final void testLex23() {
1035     assertLexedCss(
1036       "foo .quoted { content: \'contains \\\'quotes\\\'\' }",
1037       "foo", " ", ".quoted", " ", "{", " ", "content", ":", " ",
1038       "'contains \\27quotes\\27\'", " ", "}");
1039   }
1040 
1041   @Test
testLex24()1042   public static final void testLex24() {
1043     assertLexedCss(
1044       "foo .dquoted { content: \"\'contains\'\\\\\\\"double quotes\\\"\" }",
1045       "foo", " ", ".dquoted", " ", "{", " ", "content", ":", " ",
1046       "'\\27 contains\\27\\\\\\22 double quotes\\22'", " ", "}");
1047   }
1048 
1049   @Test
testLex25()1050   public static final void testLex25() {
1051     assertLexedCss(
1052       "foo .long { content: \'spans \\\n"
1053       + "multiple \\\n"
1054       + "lines\' }\n",
1055       "foo", " ", ".long", " ", "{", " ", "content", ":", " ",
1056       "'spans multiple lines'", " ", "}");
1057   }
1058 
1059   @Test
testLex26()1060   public static final void testLex26() {
1061     assertLexedCss(
1062       "foo .extended-unicode { content: \'a1 \\61\\31  \\0000611 \\000061 1 \\0061\\0031\' }",
1063       "foo", " ", ".extended-unicode", " ", "{", " ", "content", ":", " ",
1064       "'a1 a1 a1 a1 a1'", " ", "}");
1065   }
1066 
1067   @Test
testLex27()1068   public static final void testLex27() {
1069     assertLexedCss(
1070       "/* CSS 2.1 allows _ in identifiers */\n"
1071       + "#a_b {}\n"
1072       + ".a_b {}",
1073       "#a_b", " ", "{", "}", " ", ".a_b", " ", "{", "}");
1074   }
1075 
1076   @Test
testLex28()1077   public static final void testLex28() {
1078     assertLexedCss(
1079       "#xxx {\n"
1080       + "  filter:alpha(opacity=50);\n"
1081       + "}",
1082       "#xxx", " ", "{", " ",
1083       "filter", ":", "alpha(", "opacity", "=", "50", ")", ";", " ",
1084       "}");
1085   }
1086 
1087   @Test
testLex29()1088   public static final void testLex29() {
1089     assertLexedCss(
1090       "p { margin: -3px -3px }\n"
1091       + "p { margin: -3px 3px }",
1092       "p", " ", "{", " ", "margin", ":", " ", "-3px", " ", "-3px", " ", "}", " ",
1093       "p", " ", "{", " ", "margin", ":", " ", "-3px", " ", "3px", " ", "}");
1094   }
1095 
1096   @Test
testLex30()1097   public static final void testLex30() {
1098     assertLexedCss(
1099       "<!-- \n"
1100       + "p { content: \'-->foo<!--\' }  /* - -> bar <!--- */\n"
1101       + "-->",
1102       "p", " ", "{", " ", "content", ":",
1103       " ", "'--\\3e foo\\3c!--'", " ", "}");
1104   }
1105 
1106   @Test
testLex31()1107   public static final void testLex31() {
1108     assertLexedCss(
1109       "@bogus hello {\n"
1110       + "  balanced { curly \"brackets\" };\n"
1111       + "}",
1112       "@bogus", " ", "hello", " ", "{", " ",
1113       "balanced", " ", "{", " ", "curly", " ", "'brackets'", " ", "}", ";",
1114       " ", "}");
1115   }
1116 
1117   @Test
testLex32()1118   public static final void testLex32() {
1119     assertLexedCss(
1120       "/* Not treated as part of the bogus symbol block */\n"
1121       + "* { color: red }",
1122       "*", " ", "{", " ", "color", ":", " ", "red", " ", "}");
1123   }
1124 
1125   @Test
testLex33()1126   public static final void testLex33() {
1127     assertLexedCss(
1128       "@unknown(\'hi\');",
1129       "@unknown", "(", "'hi'", ")", ";");
1130   }
1131 
1132   @Test
testLex34()1133   public static final void testLex34() {
1134     assertLexedCss(
1135       "/* list applies to body, input, and td.  Extraneous , skip. */\n"
1136       + "body, input, , td {\n"
1137       + "  /* missing property name causes skip until ; */\n"
1138       + "  Arial, sans-serif;\n"
1139       + "  color: blue;\n"
1140       + "  /* missing value.  skipped. */\n"
1141       + "  background-color:\n"
1142       + "}",
1143       "body", ",", " ", "input", ",", " ", ",", " ", "td",
1144       " ", "{", " ", "Arial", ",", " ", "sans-serif", ";",
1145       " ", "color", ":", " ", "blue", ";",
1146       " ", "background-color", ":", " ", "}");
1147   }
1148 
1149   @Test
testLex35()1150   public static final void testLex35() {
1151     assertLexedCss(
1152       "/* not thrown out, but 2 digit color is discarded */\n"
1153       + "@media print {\n"
1154       + "  * { color: black !important; background-color: #ff }\n"
1155       + "}",
1156       "@media", " ", "print", " ", "{",
1157       " ", "*", " ", "{", " ", "color", ":", " ", "black", " ", "!", "important", ";", " ", "background-color", ":", " ", "#ff", " ", "}", " ", "}");
1158   }
1159 
1160   @Test
testLex36()1161   public static final void testLex36() {
1162     assertLexedCss(
1163       "@page :{broken { margin-left: 4cm; }  /* extra { */",
1164       "@page", " ", ":", "{", "broken", " ", "{",
1165       " ", "margin-left", ":", " ", "4cm", ";", " ", "}", " ", "}");
1166   }
1167 
1168   @Test
testLex37()1169   public static final void testLex37() {
1170     assertLexedCss(
1171       "@page .broken {}  /* no colon */",
1172       "@page", " ", ".broken", " ", "{", "}");
1173   }
1174 
1175   @Test
testLex38()1176   public static final void testLex38() {
1177     assertLexedCss(
1178       "@page :{}  /* no pseudo-page */",
1179       "@page", " ", ":", "{", "}");
1180   }
1181 
1182   @Test
testLex39()1183   public static final void testLex39() {
1184     assertLexedCss(
1185       "@page :broken {  /* missing \'}\' */",
1186       "@page", " ", ":", "broken", " ", "{", " ", "}");
1187   }
1188 
1189   @Test
testLex40()1190   public static final void testLex40() {
1191     assertLexedCss(
1192       "@page :left { margin-left: 4cm;; size: 8.5in 11in; }  /* ok */",
1193       "@page", " ", ":", "left", " ", "{",
1194       " ", "margin-left", ":", " ", "4cm", ";", ";",
1195       " ", "size", ":", " ", "8.5in", " ", "11in", ";", " ", "}");
1196   }
1197 
1198   @Test
testLex41()1199   public static final void testLex41() {
1200     assertLexedCss(
1201       "/* missing property */\n"
1202       + "body { : blue }",
1203       "body", " ", "{", " ", ":", " ", "blue", " ", "}");
1204   }
1205 
1206   @Test
testLex42()1207   public static final void testLex42() {
1208     assertLexedCss(
1209       "color: blue;",
1210       "color", ":", " ", "blue", ";");
1211   }
1212 
1213   @Test
testLex43()1214   public static final void testLex43() {
1215     assertLexedCss(
1216       "a:visited, :unvisited, a::before { color: blue }",
1217       "a", ":", "visited", ",",
1218       " ", ":", "unvisited", ",",
1219       " ", "a", ":", ":", "before",
1220       " ", "{", " ", "color", ":", " ", "blue", " ", "}");
1221   }
1222 
1223   @Test
testLex44()1224   public static final void testLex44() {
1225     assertLexedCss(
1226       "/* not a valid wildcard wiseguy */\n"
1227       + "? { color: blue }",
1228       "?", " ", "{", " ", "color", ":", " ", "blue", " ", "}");
1229   }
1230 
1231   @Test
testLex45()1232   public static final void testLex45() {
1233     assertLexedCss(
1234       "/* lots of invalid selectors */\n"
1235       + ".3, #333, a[href=\'foo\', a[href=], a[=\'foo\'], body:, ok {}",
1236       "0.3", ",",
1237       " ", "#333", ",",
1238       " ", "a", "[", "href", "=", "'foo'", ",",
1239       " ", "a", "[", "href", "=", "]", ",",
1240       " ", "a", "[", "=", "'foo'", "]", ",",
1241       " ", "body", ":", ",",
1242       " ", "ok", " ", "{", "}",
1243       "]");
1244   }
1245 
1246   @Test
testLex46()1247   public static final void testLex46() {
1248     assertLexedCss(
1249       "/* all invalid selectors */\n"
1250       + "#333, .3, .,  {}",
1251       "#333", ",", " ", "0.3", ",", " ", ".", " ", ",", " ", "{", "}");
1252   }
1253 
1254   @Test
testLex47()1255   public static final void testLex47() {
1256     assertLexedCss(
1257       "/* valid selectors missing a body */\n"
1258       + "a, b, i, p, q, s, u, ;",
1259       "a", ",", " ", "b", ",", " ", "i", ",", " ", "p", ",", " ", "q", ",",
1260       " ", "s", ",", " ", "u", ",", " ", ";");
1261   }
1262 
1263   @Test
testLex48()1264   public static final void testLex48() {
1265     assertLexedCss(
1266       "/* expression cruft. Make sure parsing before and after ok. */\n"
1267       + "a1 { a: ok;  color: red:;              a: ok }  /* cruft after : */\n"
1268       + "a2 { a: ok;  width: 0 !import;         a: ok }  /* !important misspelled */\n"
1269       + "a3 { a: ok;  unicode-range: U+0-FFFF;  a: ok }  /* ok */ \n"
1270       + "a4 { a: ok;  color: #g00;              a: ok }  /* bad hex digit */\n"
1271       + "a5 { a: ok;  image: url(\'::\');       a: ok }  /* malformed URI */\n"
1272       + "a6 { a: ok;  image: url(::);           a: ok }  /* malformed URI */",
1273       "a1", " ", "{", " ", "a", ":", " ", "ok", ";",
1274       " ", "color", ":", " ", "red", ":", ";",
1275       " ", "a", ":", " ", "ok", " ", "}", " ",
1276       "a2", " ", "{", " ", "a", ":", " ", "ok", ";",
1277       " ", "width", ":", " ", "0", " ", "!", "import", ";",
1278       " ", "a", ":", " ", "ok", " ", "}", " ",
1279       "a3", " ", "{", " ", "a", ":", " ", "ok", ";",
1280       " ", "unicode-range", ":", " ", "U+0-ffff", ";",
1281       " ", "a", ":", " ", "ok", " ", "}", " ",
1282       "a4", " ", "{", " ", "a", ":", " ", "ok", ";",
1283       " ", "color", ":", " ", "#g00",
1284       ";", " ", "a", ":", " ", "ok", " ", "}", " ",
1285       "a5", " ", "{", " ", "a", ":", " ", "ok", ";",
1286       " ", "image", ":", " ", "url('::')", ";",
1287       " ", "a", ":", " ", "ok", " ", "}", " ",
1288       "a6", " ", "{", " ", "a", ":", " ", "ok", ";",
1289       " ", "image", ":", " ", "url('::')", ";",
1290       " ", "a", ":", " ", "ok", " ", "}");
1291   }
1292 
1293   @Test
testLex49()1294   public static final void testLex49() {
1295     assertLexedCss(
1296       "/* functions allow for lots of mischief */\n"
1297       + "a7 { a: ok;  font-size: expression(Math.random());  a: ok }  /* ok.  TODO */\n"
1298       + "a8 { a: ok;  font-size: expression(Math.random();   a: ok }  /* missing paren */\n"
1299       + "a9 { a: ok;  font-size: expression();               a: ok }  /* missing param */\n"
1300       + "aa { a: ok;  font-size: expression({});             a: ok }  /* bad param */",
1301       "a7", " ", "{", " ", "a", ":", " ", "ok", ";", " ", "font-size", ":",
1302       " ", "expression(", "Math", ".random", " ", "(", ")", ")", ";",
1303       " ", "a", ":", " ", "ok", " ", "}", " ",
1304       "a8", " ", "{", " ", "a", ":", " ", "ok", ";", " ", "font-size", ":",
1305       " ", "expression(", "Math", ".random", " ", "(", ")", ";",
1306       " ", "a", ":", " ", "ok", " ", ")", "}", " ",
1307       "a9", " ", "{", " ", "a", ":", " ", "ok", ";", " ", "font-size", ":",
1308       " ", "expression(", ")", ";", " ", "a", ":", " ", "ok", " ", "}", " ",
1309       "aa", " ", "{", " ", "a", ":", " ", "ok", ";", " ", "font-size", ":",
1310       " ", "expression(", "{", "}", ")", ";",
1311       " ", "a", ":", " ", "ok", " ", "}");
1312   }
1313 
1314   @Test
testLex50()1315   public static final void testLex50() {
1316     assertLexedCss(
1317       "@font-face; @font-face {}\n"
1318       + "@font-face @font-face"
1319       + " { font-family: Letters; src: url(\'Letters.ttf\') }",
1320       "@font-face", ";", " ", "@font-face", " ", "{", "}", " ",
1321       "@font-face", " ", "@font-face", " ", "{",
1322       " ", "font-family", ":", " ", "Letters", ";",
1323       " ", "src", ":", " ", "url('Letters.ttf')", " ", "}");
1324   }
1325 
1326   @Test
testLex51()1327   public static final void testLex51() {
1328     assertLexedCss(
1329       "@charset \"utf-8\";",
1330       "@charset", " ", "'utf-8'", ";");
1331   }
1332 
1333   @Test
testLex52()1334   public static final void testLex52() {
1335     assertLexedCss(
1336       "@import url(\'nonsense.css\') mumbling, blather;",
1337       "@import", " ", "url('nonsense.css')",
1338       " ", "mumbling", ",", " ", "blather", ";");
1339   }
1340 
1341   @Test
testLex53()1342   public static final void testLex53() {
1343     assertLexedCss(
1344       "@page { background: url(\'sparkley.jpg\'); }",
1345       "@page", " ", "{", " ", "background", ":",
1346       " ", "url('sparkley.jpg')", ";", " ", "}");
1347   }
1348 
1349   @Test
testLex54()1350   public static final void testLex54() {
1351     assertLexedCss(
1352       "@charset \"non-utf-8\";",
1353       "@charset", " ", "'non-utf-8'", ";");
1354   }
1355 
1356   @Test
testLex55()1357   public static final void testLex55() {
1358     assertLexedCss(
1359       "/* non utf-8 */\n"
1360       + "@import \'foo.css\';\n"
1361       + "@unknown(\'hi\');",
1362       "@import", " ", "'foo.css'", ";", " ",
1363       "@unknown", "(", "'hi'", ")", ";");
1364   }
1365 
1366   @Test
testLex56()1367   public static final void testLex56() {
1368     assertLexedCss(
1369       "\ufeff"
1370       + "values: 100% -12.5% \'\' \"\" .5em 0 12 url() url(\'\') url(\"\");",
1371       // Do not treat a BOM as a part of an identifier.
1372       "values", ":", " ", "100%", " ", "-12.5%", " ", "''", " ", "''",
1373       " ", "0.5em", " ", "0", " ", "12",
1374       " ", "url('')", " ", "url('')", " ", "url('')", ";");
1375   }
1376 
1377   @Test
testLex57()1378   public static final void testLex57() {
1379     assertLexedCss(
1380       "// line comment 1\nline2\n//line comment 3\r\nline4//line comment 4\f",
1381       "line2", " ", "line4");
1382   }
1383 
1384   @Test
testLex58()1385   public static final void testLex58() {
1386     assertLexedCss(
1387       "\"\\\r\n\"",
1388       "''");
1389   }
1390 
1391   @Test
testLex59()1392   public static final void testLex59() {
1393     assertLexedCss(
1394       "url()",
1395       "url('')");
1396   }
1397 
1398 
1399   @Test
testLex60()1400   public static final void testLex60() {
1401     assertLexedCss(
1402       "\t\ufeff x",
1403       "x");
1404   }
1405 
1406   @Test
testLex61()1407   public static final void testLex61() {
1408     assertTokens(
1409       "x.1",
1410       "x:IDENT", " ", "0.1:NUMBER");
1411   }
1412 
1413   @Test
testLex62()1414   public static final void testLex62() {
1415     assertTokens(
1416       "0.. 1. . 0e1. 0e1 .",
1417       "0:NUMBER", " ", ".:DELIM", " ", "1:NUMBER", " ", ".:DELIM", " ",
1418       "0:NUMBER", " ", ".:DELIM", " ", "0:NUMBER", " ", ".:DELIM");
1419   }
1420 
1421   @Test
testLex63()1422   public static final void testLex63() {
1423     assertTokens(
1424         "[[ ]]>",
1425         "[:LEFT_SQUARE",
1426         "[:LEFT_SQUARE",
1427         " ",
1428         "]:RIGHT_SQUARE",
1429         "]:RIGHT_SQUARE",
1430         " ",  // Inserted
1431         ">:DELIM");
1432     assertTokens(
1433         "[[ ]] >",
1434         "[:LEFT_SQUARE",
1435         "[:LEFT_SQUARE",
1436         " ",
1437         "]:RIGHT_SQUARE",
1438         "]:RIGHT_SQUARE",
1439         " ",
1440         ">:DELIM");
1441   }
1442 
1443   @Test
testLex64()1444   public static final void testLex64() {
1445     assertTokens(
1446         "<![CDATA[",
1447         "<:DELIM",
1448         " ", // inserted
1449         "!:DELIM",
1450         "[:LEFT_SQUARE",
1451         "CDATA:IDENT",
1452         "[:LEFT_SQUARE",
1453         "]:RIGHT_SQUARE",
1454         "]:RIGHT_SQUARE");
1455     assertTokens(
1456         "<\\![\\43 DATA[",
1457         "<:DELIM",
1458         " ", // inserted
1459         "!:DELIM",
1460         "[:LEFT_SQUARE",
1461         "CDATA:IDENT",
1462         "[:LEFT_SQUARE",
1463         "]:RIGHT_SQUARE",
1464         "]:RIGHT_SQUARE");
1465   }
1466 
1467   @Test
testLex65()1468   public static final void testLex65() {
1469     assertTokens(
1470         "<\\/St\\79le",
1471         "<:DELIM",
1472         " ", // inserted
1473         "/:DELIM",
1474         " ", // inserted
1475         "Style:IDENT");
1476   }
1477 
1478   @Test
testLex66()1479   public static final void testLex66() {
1480     assertTokens(
1481         "/\\/foo\n/\\*bar*/",
1482         "/:DELIM",
1483         " ",
1484         "/:DELIM",
1485         " ",
1486         "foo:IDENT",
1487         " ",
1488         "/:DELIM",
1489         " ",
1490         "*:DELIM",
1491         "bar:IDENT",
1492         "*:DELIM",
1493         "/:DELIM");
1494   }
1495 
1496   @Test
testLex67()1497   public static final void testLex67() {
1498     assertTokens(
1499         "0 .-42",
1500         "0:NUMBER",
1501         " ",
1502         ".:DELIM",
1503         " ",
1504         "-42:NUMBER");
1505   }
1506 
1507   @Test
testLex68()1508   public static final void testLex68() {
1509     assertTokens(
1510         "#.42",
1511         "#:DELIM",
1512         " ",
1513         "0.42:NUMBER"
1514         );
1515     assertTokens(
1516         "#-.42",
1517         "#-:HASH_ID",
1518         " ",
1519         "0.42:NUMBER"
1520         );
1521   }
1522 
1523   @Test
testLex69()1524   public static final void testLex69() {
1525     assertTokens(
1526         "font: 24ex\0pression",
1527         "font:IDENT",
1528         "::COLON",
1529         " ",
1530         "24ex:DIMENSION",
1531         " ",
1532         "pression:IDENT");
1533   }
1534 }
1535