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 "\""/*\"", "'\\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