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
29package org.owasp.html;
30
31import java.util.Arrays;
32import java.util.List;
33
34import junit.framework.TestCase;
35
36import org.junit.Test;
37import org.owasp.html.CssTokens.TokenType;
38
39import com.google.common.collect.Lists;
40
41import static org.owasp.html.CssTokens.TokenType.*;
42
43public class CssTokensTest extends TestCase {
44
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
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
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
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
140  public static final void testNonCommentSlash() throws Exception {
141    assertEquals("foo/ bar/", lex("foo/bar/").normalizedCss);
142  }
143
144  @Test
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
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
225  public static final void testOrphanedCloseBrackets() throws Exception {
226    assertEquals("{foo bar}", lex("{foo]bar").normalizedCss);
227  }
228
229  @Test
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
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
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
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
275  public static final void testNul() throws Exception {
276    assertTokens("\u0000");
277    assertTokens("\u0000x\u0000", "x:IDENT");
278  }
279
280  @Test
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
959  public static final void testLex16() {
960    assertLexedCss(
961      "FOO > BAR + BAZ {  }",
962      "FOO", " ", ">", " ", "BAR", " ", "+", " ", "BAZ", " ", "{", " ", "}");
963  }
964
965  @Test
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
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
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
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
1020  public static final void testLex21() {
1021    assertLexedCss(
1022      "foo.bar { }",
1023      "foo", ".bar", " ", "{", " ", "}");
1024  }
1025
1026  @Test
1027  public static final void testLex22() {
1028    assertLexedCss(
1029      "foo .bar { }",
1030      "foo", " ", ".bar", " ", "{", " ", "}");
1031  }
1032
1033  @Test
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
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
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
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
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
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
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
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
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
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
1126  public static final void testLex33() {
1127    assertLexedCss(
1128      "@unknown(\'hi\');",
1129      "@unknown", "(", "'hi'", ")", ";");
1130  }
1131
1132  @Test
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
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
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
1169  public static final void testLex37() {
1170    assertLexedCss(
1171      "@page .broken {}  /* no colon */",
1172      "@page", " ", ".broken", " ", "{", "}");
1173  }
1174
1175  @Test
1176  public static final void testLex38() {
1177    assertLexedCss(
1178      "@page :{}  /* no pseudo-page */",
1179      "@page", " ", ":", "{", "}");
1180  }
1181
1182  @Test
1183  public static final void testLex39() {
1184    assertLexedCss(
1185      "@page :broken {  /* missing \'}\' */",
1186      "@page", " ", ":", "broken", " ", "{", " ", "}");
1187  }
1188
1189  @Test
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
1199  public static final void testLex41() {
1200    assertLexedCss(
1201      "/* missing property */\n"
1202      + "body { : blue }",
1203      "body", " ", "{", " ", ":", " ", "blue", " ", "}");
1204  }
1205
1206  @Test
1207  public static final void testLex42() {
1208    assertLexedCss(
1209      "color: blue;",
1210      "color", ":", " ", "blue", ";");
1211  }
1212
1213  @Test
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
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
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
1247  public static final void testLex46() {
1248    assertLexedCss(
1249      "/* all invalid selectors */\n"
1250      + "#333, .3, .,  {}",
1251      "#333", ",", " ", "0.3", ",", " ", ".", " ", ",", " ", "{", "}");
1252  }
1253
1254  @Test
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
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
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
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
1327  public static final void testLex51() {
1328    assertLexedCss(
1329      "@charset \"utf-8\";",
1330      "@charset", " ", "'utf-8'", ";");
1331  }
1332
1333  @Test
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
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
1350  public static final void testLex54() {
1351    assertLexedCss(
1352      "@charset \"non-utf-8\";",
1353      "@charset", " ", "'non-utf-8'", ";");
1354  }
1355
1356  @Test
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
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
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
1385  public static final void testLex58() {
1386    assertLexedCss(
1387      "\"\\\r\n\"",
1388      "''");
1389  }
1390
1391  @Test
1392  public static final void testLex59() {
1393    assertLexedCss(
1394      "url()",
1395      "url('')");
1396  }
1397
1398
1399  @Test
1400  public static final void testLex60() {
1401    assertLexedCss(
1402      "\t\ufeff x",
1403      "x");
1404  }
1405
1406  @Test
1407  public static final void testLex61() {
1408    assertTokens(
1409      "x.1",
1410      "x:IDENT", " ", "0.1:NUMBER");
1411  }
1412
1413  @Test
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
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
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
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
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
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
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
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