Home | History | Annotate | Download | only in html
      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 
     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