test_selectors.html (61861B)
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <title>Test for CSS Selectors</title> 5 <script src="/tests/SimpleTest/SimpleTest.js"></script> 6 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> 7 </head> 8 <body onload="run()"> 9 <p id="display"><iframe id="iframe" src="about:blank"></iframe><iframe id="cloneiframe" src="about:blank"></iframe></p> 10 <pre id="test"> 11 <script class="testbody" type="text/javascript"> 12 13 SimpleTest.waitForExplicitFinish(); 14 SimpleTest.requestLongerTimeout(2); 15 16 var cloneiframe; 17 18 function run() { 19 SpecialPowers.pushPrefEnv({ set: [["layout.css.xul-tree-pseudos.content.enabled", true]] }, runTests); 20 } 21 function runTests() { 22 var iframe = document.getElementById("iframe"); 23 var ifwin = iframe.contentWindow; 24 var ifdoc = iframe.contentDocument; 25 26 cloneiframe = document.getElementById("cloneiframe"); 27 28 var style_elem = ifdoc.createElement("style"); 29 style_elem.setAttribute("type", "text/css"); 30 ifdoc.getElementsByTagName("head")[0].appendChild(style_elem); 31 var style_text = ifdoc.createTextNode(""); 32 style_elem.appendChild(style_text); 33 34 var gCounter = 0; 35 36 /* 37 * selector: the selector to test 38 * body_contents: what to set the body's innerHTML to 39 * match_fn: a function that, given the document object into which 40 * body_contents has been inserted, produces an array of nodes that 41 * should match selector 42 * notmatch_fn: likewise, but for nodes that should not match 43 * namespaces (optional): @namespace rules to be included in the sheet 44 */ 45 function test_selector_in_html(selector, body_contents, match_fn, notmatch_fn, namespaces) 46 { 47 var zi = ++gCounter; 48 if (typeof(body_contents) == "string") { 49 ifdoc.body.innerHTML = body_contents; 50 } else { 51 // It's a function. 52 ifdoc.body.innerHTML = ""; 53 body_contents(ifdoc.body); 54 } 55 if (!namespaces) { 56 namespaces = ""; 57 } 58 style_text.data = namespaces + selector + "{ z-index: " + zi + " }"; 59 60 var idx = style_text.parentNode.sheet.cssRules.length - 1; 61 if (namespaces == "") { 62 is(idx, 0, "unexpected rule index for " + selector); 63 } 64 if (idx < 0 || 65 style_text.parentNode.sheet.cssRules[idx].type != 66 CSSRule.STYLE_RULE) 67 { 68 ok(false, "selector " + selector + " could not be parsed"); 69 return; 70 } 71 72 var should_match = match_fn(ifdoc); 73 var should_not_match = notmatch_fn(ifdoc); 74 if (should_match.length + should_not_match.length == 0) { 75 ok(false, "nothing to check"); 76 } 77 78 for (let i = 0; i < should_match.length; ++i) { 79 let e = should_match[i]; 80 is(ifwin.getComputedStyle(e).zIndex, String(zi), 81 "element in " + body_contents + " matched " + selector); 82 } 83 for (let i = 0; i < should_not_match.length; ++i) { 84 let e = should_not_match[i]; 85 is(ifwin.getComputedStyle(e).zIndex, "auto", 86 "element in " + body_contents + " did not match " + selector); 87 } 88 89 // Now, since we're here, may as well make sure serialization 90 // works correctly. It need not produce the exact same text, 91 // but it should produce a selector that matches the same 92 // elements. 93 zi = ++gCounter; 94 var ser1 = style_text.parentNode.sheet.cssRules[idx].selectorText; 95 style_text.data = namespaces + ser1 + "{ z-index: " + zi + " }"; 96 for (let i = 0; i < should_match.length; ++i) { 97 let e = should_match[i]; 98 is(ifwin.getComputedStyle(e).zIndex, String(zi), 99 "element in " + body_contents + " matched " + ser1 + 100 " which is the reserialization of " + selector); 101 } 102 for (let i = 0; i < should_not_match.length; ++i) { 103 let e = should_not_match[i]; 104 is(ifwin.getComputedStyle(e).zIndex, "auto", 105 "element in " + body_contents + " did not match " + ser1 + 106 " which is the reserialization of " + selector); 107 } 108 109 // But when we serialize the serialized result, we should get 110 // the same text. 111 isnot(style_text.parentNode.sheet.cssRules[idx], undefined, 112 "parse of selector \"" + ser1 + "\" failed"); 113 114 if (style_text.parentNode.sheet.cssRules[idx] !== undefined) { 115 var ser2 = style_text.parentNode.sheet.cssRules[idx].selectorText; 116 is(ser2, ser1, "parse+serialize of selector \"" + selector + 117 "\" is idempotent"); 118 } 119 120 ifdoc.body.innerHTML = ""; 121 style_text.data = ""; 122 123 // And now test that when we clone the style sheet, we end up 124 // with the same selector (serializes to same string, and 125 // matches the same things). 126 zi = ++gCounter; 127 var style_sheet = "data:text/css," + 128 escape(namespaces + selector + "{ z-index: " + zi + " }"); 129 var style_sheet_link = 130 "<link rel='stylesheet' href='" + style_sheet + "'>"; 131 var html_doc = "<!DOCTYPE HTML>" + 132 style_sheet_link + style_sheet_link + 133 "<body>"; 134 if (typeof(body_contents) == "string") { 135 html_doc += body_contents; 136 } 137 var docurl = "data:text/html," + escape(html_doc); 138 defer_clonedoc_tests(docurl, function() { 139 var wrappedCloneFrame = SpecialPowers.wrap(cloneiframe); 140 var clonedoc = wrappedCloneFrame.contentDocument; 141 var clonewin = wrappedCloneFrame.contentWindow; 142 143 if (typeof(body_contents) != "string") { 144 body_contents(clonedoc.body); 145 } 146 147 var links = clonedoc.getElementsByTagName("link"); 148 // cause a clone 149 links[1].sheet.insertRule("#nonexistent { color: purple}", idx + 1); 150 // remove the uncloned sheet 151 links[0].remove(); 152 153 var should_match1 = match_fn(clonedoc); 154 var should_not_match1 = notmatch_fn(clonedoc); 155 156 if (should_match1.length + should_not_match1.length == 0) { 157 ok(false, "nothing to check"); 158 } 159 160 for (let i = 0; i < should_match1.length; ++i) { 161 let e = should_match1[i]; 162 is(clonewin.getComputedStyle(e).zIndex, String(zi), 163 "element in " + body_contents + " matched clone of " + 164 selector); 165 } 166 for (let i = 0; i < should_not_match1.length; ++i) { 167 let e = should_not_match1[i]; 168 is(clonewin.getComputedStyle(e).zIndex, "auto", 169 "element in " + body_contents + " did not match clone of " + 170 selector); 171 } 172 173 var ser3 = links[0].sheet.cssRules[idx].selectorText; 174 is(ser3, ser1, 175 "selector " + selector + " serializes correctly after cloning"); 176 }); 177 } 178 179 function should_serialize_to(selector, serialization) 180 { 181 style_text.data = selector + "{ z-index: 0 }"; 182 is(style_text.parentNode.sheet.cssRules[0].selectorText, 183 serialization, 184 "selector '" + selector + "' should serialize to '" + 185 serialization + "'."); 186 } 187 188 function test_parseable(selector) 189 { 190 ifdoc.body.innerHTML = "<p></p>"; 191 192 var zi = ++gCounter; 193 style_text.data = "p, " + selector + "{ z-index: " + zi + " }"; 194 var should_match = ifdoc.getElementsByTagName("p")[0]; 195 var parsed = ifwin.getComputedStyle(should_match).zIndex == zi; 196 ok(parsed, "selector " + selector + " was parsed"); 197 if (!parsed) { 198 return; 199 } 200 201 // Test that it serializes to something that is also parseable. 202 var ser1 = style_elem.sheet.cssRules[0].selectorText; 203 zi = ++gCounter; 204 style_text.data = ser1 + "{ z-index: " + zi + " }"; 205 is(ifwin.getComputedStyle(should_match).zIndex, String(zi), 206 "serialization " + ser1 + " of selector p, " + selector + 207 " was parsed"); 208 var ser2 = style_elem.sheet.cssRules[0].selectorText; 209 is(ser2, ser1, 210 "parse+serialize of selector " + selector + " is idempotent"); 211 212 ifdoc.body.innerHTML = ""; 213 style_text.data = ""; 214 215 // Test that it clones to the same thing it serializes to. 216 zi = ++gCounter; 217 var style_sheet = "data:text/css," + 218 escape("p, " + selector + "{ z-index: " + zi + " }"); 219 var style_sheet_link = 220 "<link rel='stylesheet' href='" + style_sheet + "'>"; 221 var html_doc = "<!DOCTYPE HTML>" + 222 style_sheet_link + style_sheet_link + 223 "<p></p>"; 224 var docurl = "data:text/html," + escape(html_doc); 225 226 defer_clonedoc_tests(docurl, function() { 227 var wrappedCloneFrame = SpecialPowers.wrap(cloneiframe); 228 var clonedoc = wrappedCloneFrame.contentDocument; 229 var clonewin = wrappedCloneFrame.contentWindow; 230 var links = clonedoc.getElementsByTagName("link"); 231 // cause a clone 232 links[1].sheet.insertRule("#nonexistent { color: purple}", 0); 233 // remove the uncloned sheet 234 links[0].remove(); 235 236 should_match = clonedoc.getElementsByTagName("p")[0]; 237 is(clonewin.getComputedStyle(should_match).zIndex, String(zi), 238 "selector " + selector + " was cloned correctly"); 239 var ser3 = links[0].sheet.cssRules[1].selectorText; 240 is(ser3, ser1, 241 "selector " + selector + " serializes correctly after cloning"); 242 }); 243 } 244 245 function test_unparseable_via_api(selector) 246 { 247 try { 248 // Test that it is also unparseable when followed by EOF. 249 ifdoc.body.matches(selector); 250 ok(false, "selector '" + selector + "' plus EOF is parse error"); 251 } catch(ex) { 252 is(ex.name, "SyntaxError", 253 "selector '" + selector + "' plus EOF is parse error"); 254 is(ex.code, DOMException.SYNTAX_ERR, 255 "selector '" + selector + "' plus EOF is parse error"); 256 } 257 } 258 259 function test_parseable_via_api(selector) 260 { 261 var threw = false; 262 try { 263 // Test that a selector is parseable when followed by EOF. 264 ifdoc.body.matches(selector); 265 } catch(ex) { 266 threw = true; 267 } 268 ok(!threw, "selector '" + selector + "' was parsed"); 269 } 270 271 function test_balanced_unparseable(selector) 272 { 273 var zi1 = ++gCounter; 274 var zi2 = ++gCounter; 275 ifdoc.body.innerHTML = "<p></p><div></div>"; 276 style_text.data = "p, " + selector + "{ z-index: " + zi1 + " }" + 277 "div { z-index: " + zi2 + " }"; 278 var should_not_match = ifdoc.getElementsByTagName("p")[0]; 279 var should_match = ifdoc.getElementsByTagName("div")[0]; 280 is(ifwin.getComputedStyle(should_not_match).zIndex, "auto", 281 "selector " + selector + " was a parser error"); 282 is(ifwin.getComputedStyle(should_match).zIndex, String(zi2), 283 "selector " + selector + " error was recovered from"); 284 ifdoc.body.innerHTML = ""; 285 style_text.data = ""; 286 test_unparseable_via_api(selector); 287 } 288 289 function test_unbalanced_unparseable(selector) 290 { 291 var zi1 = ++gCounter; 292 var zi2 = ++gCounter; 293 ifdoc.body.innerHTML = "<p></p>"; 294 style_text.data = "p, " + selector + "{ z-index: " + zi1 + " }"; 295 var should_not_match = ifdoc.getElementsByTagName("p")[0]; 296 is(ifwin.getComputedStyle(should_not_match).zIndex, "auto", 297 "selector " + selector + " was a parser error"); 298 is(style_text.parentNode.sheet.cssRules.length, 0, 299 "sheet should have no rules since " + selector + " is parse error"); 300 ifdoc.body.innerHTML = ""; 301 style_text.data = ""; 302 test_unparseable_via_api(selector); 303 } 304 305 // [attr] selector 306 test_parseable("[attr]") 307 test_parseable_via_api("[attr"); 308 test_parseable("[ATTR]") 309 should_serialize_to("[attr]", "[attr]"); 310 should_serialize_to("[ATTR]", "[ATTR]"); 311 312 // Whether we should drop the bar is debatable. This matches Edge 313 // and Safari at the time of writing. 314 should_serialize_to("[|attr]", "[attr]"); 315 should_serialize_to("[|ATTR]", "[ATTR]"); 316 317 // [attr= ] selector 318 test_parseable("[attr=\"x\"]"); 319 test_parseable("[attr='x']"); 320 test_parseable("[attr=x]"); 321 test_parseable("[attr=\"\"]"); 322 test_parseable("[attr='']"); 323 test_parseable("[attr=\"foo bar\"]"); 324 test_parseable_via_api("[attr=x"); 325 326 test_balanced_unparseable("[attr=]"); 327 test_balanced_unparseable("[attr=foo bar]"); 328 329 test_selector_in_html( 330 '[title=""]', 331 '<p title=""></p>' 332 + '<div lang=" "></div><div lang="\t"></div><div lang="\n"></div>', 333 function(doc) { return doc.getElementsByTagName("p"); }, 334 function(doc) { return doc.getElementsByTagName("div"); } 335 ); 336 337 // [attr~= ] selector 338 test_parseable("[attr~=\"x\"]"); 339 test_parseable("[attr~='x']"); 340 test_parseable("[attr~=x]"); 341 test_parseable("[attr~=\"\"]"); 342 test_parseable("[attr~='']"); 343 test_parseable("[attr~=\"foo bar\"]"); 344 test_parseable_via_api("[attr~=x"); 345 346 test_balanced_unparseable("[attr~=]"); 347 test_balanced_unparseable("[attr~=foo bar]"); 348 349 test_selector_in_html( 350 '[class~="x x"]', 351 '<div class="x x"></div><div class="x"></div><div class="x\tx"></div>div class="x\nx"></div>', 352 function(doc) { return []; }, 353 function(doc) { return doc.getElementsByTagName("div"); } 354 ); 355 356 // [attr|="x"] 357 test_parseable('[attr|="x"]'); 358 test_parseable("[attr|='x']"); 359 test_parseable('[attr|=x]'); 360 test_parseable_via_api("[attr|=x"); 361 362 test_parseable('[attr|=""]'); 363 test_parseable("[attr|='']"); 364 test_balanced_unparseable('[attr|=]'); 365 366 test_selector_in_html( 367 '[lang|=""]', 368 '<p lang=""></p><p lang="-"></p><p lang="-GB"></p>' 369 + '<div lang="en-GB"></div><div lang="en-"></div>', 370 function(doc) { return doc.getElementsByTagName("p"); }, 371 function(doc) { return doc.getElementsByTagName("div"); } 372 ); 373 374 // [attr$= ] selector 375 test_parseable("[attr$=\"x\"]"); 376 test_parseable("[attr$='x']"); 377 test_parseable("[attr$=x]"); 378 test_parseable("[attr$=\"\"]"); 379 test_parseable("[attr$='']"); 380 test_parseable("[attr$=\"foo bar\"]"); 381 test_parseable_via_api("[attr$=x"); 382 383 test_balanced_unparseable("[attr$=]"); 384 test_balanced_unparseable("[attr$=foo bar]"); 385 386 // [attr^= ] selector 387 test_parseable("[attr^=\"x\"]"); 388 test_parseable("[attr^='x']"); 389 test_parseable("[attr^=x]"); 390 test_parseable("[attr^=\"\"]"); 391 test_parseable("[attr^='']"); 392 test_parseable("[attr^=\"foo bar\"]"); 393 test_parseable_via_api("[attr^=x"); 394 395 test_balanced_unparseable("[attr^=]"); 396 test_balanced_unparseable("[attr^=foo bar]"); 397 398 // attr[*= ] selector 399 test_parseable("[attr*=\"x\"]"); 400 test_parseable("[attr*='x']"); 401 test_parseable("[attr*=x]"); 402 test_parseable("[attr*=\"\"]"); 403 test_parseable("[attr*='']"); 404 test_parseable("[attr*=\"foo bar\"]"); 405 test_parseable_via_api("[attr^=x"); 406 407 test_balanced_unparseable("[attr*=]"); 408 test_balanced_unparseable("[attr*=foo bar]"); 409 410 // And now tests for correctness of matching of attr selectors. 411 var attrTestBody = 412 // Paragraphs 1-5 413 "<p attr></p> <p attr=''></p> <p attr='foo'></p> <p att></p> <p></p>" + 414 // Paragraphs 6-8 415 "<p attr='foo bar'></p> <p attr='foo-bar'></p> <p attr='foobar'></p>" + 416 // Paragraphs 9-10 417 "<p attr='foo bar baz'></p> <p attr='foo-bar-baz'></p>" + 418 // Paragraphs 11-12 419 "<p attr='foo-bar baz'></p> <p attr=' foo-bar '></p> " + 420 // Paragraph 13-15 421 "<p attr=' foo '></p> <p attr='fo'></p> <p attr='bar baz-foo'></p>"; 422 test_selector_in_html( 423 "[attr]", attrTestBody, 424 pset([1,2,3,6,7,8,9,10,11,12,13,14,15]), pset([4,5])); 425 test_selector_in_html( 426 "[attr=foo]", attrTestBody, 427 pset([3]), pset([1,2,4,5,6,7,8,9,10,11,12,13,14,15])); 428 test_selector_in_html( 429 "[attr~=foo]", attrTestBody, 430 pset([3,6,9,13]), pset([1,2,4,5,7,8,10,11,12,14,15])); 431 test_selector_in_html( 432 "[attr~=bar]", attrTestBody, 433 pset([6,9,15]), pset([1,2,3,4,5,7,8,10,11,12,13,14])); 434 test_selector_in_html( 435 "[attr~=baz]", attrTestBody, 436 pset([9,11]), pset([1,2,3,4,5,6,7,8,10,12,13,14,15])); 437 test_selector_in_html( 438 "[attr|=foo]", attrTestBody, 439 pset([3,7,10,11]), pset([1,2,4,5,6,8,9,12,13,14,15])); 440 test_selector_in_html( 441 "[attr|='bar baz']", attrTestBody, 442 pset([15]), pset([1,2,3,4,5,6,7,8,9,10,11,12,13,14])); 443 test_selector_in_html( 444 "[attr$=foo]", attrTestBody, 445 pset([3,15]), pset([1,2,4,5,6,7,8,9,10,11,12,13,14])); 446 test_selector_in_html( 447 "[attr$=bar]", attrTestBody, 448 pset([6,7,8]), pset([1,2,3,4,5,9,10,11,12,13,14,15])); 449 test_selector_in_html( 450 "[attr^=foo]", attrTestBody, 451 pset([3,6,7,8,9,10,11]), pset([1,2,4,5,12,13,14,15])); 452 test_selector_in_html( 453 "[attr*=foo]", attrTestBody, 454 pset([3,6,7,8,9,10,11,12,13,15]), pset([1,2,4,5,14])); 455 456 // Bug 420814 457 test_selector_in_html( 458 "div ~ div p", 459 "<div></div><div><div><p>match</p></div></div>", 460 function(doc) { return doc.getElementsByTagName("p"); }, 461 function(doc) { return []; } 462 ); 463 464 // Bug 420245 465 test_selector_in_html( 466 "p[attr$=\"\"]", 467 "<p attr=\"foo\">This should not match</p>", 468 function(doc) { return []; }, 469 function(doc) { return doc.getElementsByTagName("p"); } 470 ); 471 test_selector_in_html( 472 "div + p[attr~=\"\"]", 473 "<div>Dummy</div><p attr=\"foo\">This should not match</p>", 474 function(doc) { return []; }, 475 function(doc) { return doc.getElementsByTagName("p"); } 476 ); 477 test_selector_in_html( 478 "div[attr^=\"\"]", 479 "<div attr=\"dummy1\">Dummy</div><div attr=\"dummy2\">Dummy</div>", 480 function(doc) { return []; }, 481 function(doc) { return doc.getElementsByTagName("div"); } 482 ); 483 test_selector_in_html( 484 "div[attr*=\"\"]", 485 "<div attr=\"dummy1\">Dummy</div><div attr=\"dummy2\">Dummy</div>", 486 function(doc) { return []; }, 487 function(doc) { return doc.getElementsByTagName("div"); } 488 ); 489 490 // :nth-child(), etc. 491 // Follow the whitespace rules as proposed in 492 // http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html 493 test_balanced_unparseable(":nth-child()"); 494 test_balanced_unparseable(":nth-of-type( )"); 495 test_parseable(":nth-last-child( odd)"); 496 test_parseable(":nth-last-of-type(even )"); 497 test_parseable(":nth-child(n )"); 498 test_parseable(":nth-of-type( 2n)"); 499 test_parseable(":nth-last-child( -n)"); 500 test_parseable(":nth-last-of-type(-2n )"); 501 test_balanced_unparseable(":nth-child(- n)"); 502 test_balanced_unparseable(":nth-of-type(-2 n)"); 503 test_balanced_unparseable(":nth-last-of-type(2n1)"); 504 test_balanced_unparseable(":nth-child(2n++1)"); 505 test_balanced_unparseable(":nth-of-type(2n-+1)"); 506 test_balanced_unparseable(":nth-last-child(2n+-1)"); 507 test_balanced_unparseable(":nth-last-of-type(2n--1)"); 508 test_parseable(":nth-child( 3n + 1 )"); 509 test_parseable(":nth-child( +3n - 2 )"); 510 test_parseable(":nth-child( -n+ 6)"); 511 test_parseable(":nth-child( +6 )"); 512 test_balanced_unparseable(":nth-child(3 n)"); 513 test_balanced_unparseable(":nth-child(+ 2n)"); 514 test_balanced_unparseable(":nth-child(+ 2)"); 515 test_parseable(":nth-child(3)"); 516 test_parseable(":nth-of-type(-3)"); 517 test_parseable(":nth-last-child(+3)"); 518 test_parseable(":nth-last-of-type(0)"); 519 test_parseable(":nth-child(-0)"); 520 test_parseable(":nth-of-type(3n)"); 521 test_parseable(":nth-last-child(-3n)"); 522 test_parseable(":nth-last-of-type(+3n)"); 523 test_parseable(":nth-last-of-type(0n)"); 524 test_parseable(":nth-child(-0n)"); 525 test_parseable(":nth-of-type(n)"); 526 test_parseable(":nth-last-child(-n)"); 527 test_parseable(":nth-last-of-type(2n+1)"); 528 test_parseable(":nth-child(2n-1)"); 529 test_parseable(":nth-of-type(2n+0)"); 530 test_parseable(":nth-last-child(2n-0)"); 531 test_parseable(":nth-child(-0n+0)"); 532 test_parseable(":nth-of-type(n+1)"); 533 test_parseable(":nth-last-child(n-1)"); 534 test_parseable(":nth-last-of-type(-n+1)"); 535 test_parseable(":nth-child(-n-1)"); 536 test_balanced_unparseable(":nth-child(2-n)"); 537 test_balanced_unparseable(":nth-child(2-n-1)"); 538 test_balanced_unparseable(":nth-child(n-2-1)"); 539 // Bug 750388 540 test_parseable(":nth-child(+n)"); 541 test_balanced_unparseable(":nth-child(+ n)"); 542 test_parseable(":nth-child(+n+2)"); 543 test_parseable(":nth-child(+n-2)"); 544 test_parseable(":nth-child(+n + 2)"); 545 test_parseable(":nth-child(+n - 2)"); 546 test_balanced_unparseable(":nth-child(+ n+2)"); 547 test_balanced_unparseable(":nth-child(+ n-2)"); 548 test_balanced_unparseable(":nth-child(+ n + 2)"); 549 test_balanced_unparseable(":nth-child(+ n - 2)"); 550 test_parseable(":nth-child(+n-100)"); 551 test_parseable(":nth-child(+n - 100)"); 552 test_balanced_unparseable(":nth-child(+ n-100)"); 553 test_balanced_unparseable(":nth-child(+-n+2)"); 554 test_balanced_unparseable(":nth-child(+ -n+2)"); 555 test_balanced_unparseable(":nth-child(+-n-100)"); 556 test_balanced_unparseable(":nth-child(+ -n-100)"); 557 test_balanced_unparseable(":nth-child(++n-100)"); 558 test_balanced_unparseable(":nth-child(-+n-100)"); 559 test_balanced_unparseable(":nth-child(++2n - 100)"); 560 test_balanced_unparseable(":nth-child(+-2n - 100)"); 561 test_balanced_unparseable(":nth-child(-+2n - 100)"); 562 test_balanced_unparseable(":nth-child(--2n - 100)"); 563 test_balanced_unparseable(":nth-child(+/**/+2n - 100)"); 564 test_balanced_unparseable(":nth-child(+/**/-2n - 100)"); 565 test_balanced_unparseable(":nth-child(-/**/+2n - 100)"); 566 test_balanced_unparseable(":nth-child(-/**/-2n - 100)"); 567 test_balanced_unparseable(":nth-child(+/**/+/**/2n - 100)"); 568 test_balanced_unparseable(":nth-child(+/**/-/**/2n - 100)"); 569 test_balanced_unparseable(":nth-child(-/**/+/**/2n - 100)"); 570 test_balanced_unparseable(":nth-child(-/**/-/**/2n - 100)"); 571 test_balanced_unparseable(":nth-child(++/**/2n - 100)"); 572 test_balanced_unparseable(":nth-child(+-/**/2n - 100)"); 573 test_balanced_unparseable(":nth-child(-+/**/2n - 100)"); 574 test_balanced_unparseable(":nth-child(--/**/2n - 100)"); 575 test_balanced_unparseable(":nth-child(-even)"); 576 test_balanced_unparseable(":nth-child(-odd)"); 577 test_balanced_unparseable(":nth-child(+even)"); 578 test_balanced_unparseable(":nth-child(+odd)"); 579 test_balanced_unparseable(":nth-child(+ even)"); 580 test_balanced_unparseable(":nth-child(+ odd)"); 581 test_balanced_unparseable(":nth-child(+-n)"); 582 test_balanced_unparseable(":nth-child(+-n-)"); 583 test_balanced_unparseable(":nth-child(-+n)"); 584 test_balanced_unparseable(":nth-child(+n--)"); 585 test_parseable(":nth-child(n+2)"); 586 test_parseable(":nth-child(n/**/+/**/2)"); 587 test_parseable(":nth-child(n-2)"); 588 test_parseable(":nth-child(n/**/-/**/2)"); 589 test_balanced_unparseable(":nth-child(n++2)"); 590 test_balanced_unparseable(":nth-child(n+-2)"); 591 test_balanced_unparseable(":nth-child(n-+2)"); 592 test_balanced_unparseable(":nth-child(n--2)"); 593 test_balanced_unparseable(":nth-child(n/**/++2)"); 594 test_balanced_unparseable(":nth-child(n/**/+-2)"); 595 test_balanced_unparseable(":nth-child(n/**/-+2)"); 596 test_balanced_unparseable(":nth-child(n/**/--2)"); 597 test_balanced_unparseable(":nth-child(n/**/+/**/+2)"); 598 test_balanced_unparseable(":nth-child(n/**/+/**/-2)"); 599 test_balanced_unparseable(":nth-child(n/**/-/**/+2)"); 600 test_balanced_unparseable(":nth-child(n/**/-/**/-2)"); 601 test_balanced_unparseable(":nth-child(n+/**/+2)"); 602 test_balanced_unparseable(":nth-child(n+/**/-2)"); 603 test_balanced_unparseable(":nth-child(n-/**/+2)"); 604 test_balanced_unparseable(":nth-child(n-/**/-2)"); 605 test_balanced_unparseable(":nth-child(n++/**/2)"); 606 test_balanced_unparseable(":nth-child(n+-/**/2)"); 607 test_balanced_unparseable(":nth-child(n-+/**/2)"); 608 test_balanced_unparseable(":nth-child(n--/**/2)"); 609 test_balanced_unparseable(":nth-child(n/**/++/**/2)"); 610 test_balanced_unparseable(":nth-child(n/**/+-/**/2)"); 611 test_balanced_unparseable(":nth-child(n/**/-+/**/2)"); 612 test_balanced_unparseable(":nth-child(n/**/--/**/2)"); 613 test_balanced_unparseable(":nth-child(n/**/+/**/+/**/2)"); 614 test_balanced_unparseable(":nth-child(n/**/+/**/-/**/2)"); 615 test_balanced_unparseable(":nth-child(n/**/-/**/+/**/2)"); 616 test_balanced_unparseable(":nth-child(n/**/-/**/-/**/2)"); 617 test_balanced_unparseable(":nth-child(n+/**/+/**/2)"); 618 test_balanced_unparseable(":nth-child(n+/**/-/**/2)"); 619 test_balanced_unparseable(":nth-child(n-/**/+/**/2)"); 620 test_balanced_unparseable(":nth-child(n-/**/-/**/2)"); 621 test_parseable(":nth-child(2n+2)"); 622 test_parseable(":nth-child(2n/**/+/**/2)"); 623 test_parseable(":nth-child(2n-2)"); 624 test_parseable(":nth-child(2n/**/-/**/2)"); 625 test_balanced_unparseable(":nth-child(2n++2)"); 626 test_balanced_unparseable(":nth-child(2n+-2)"); 627 test_balanced_unparseable(":nth-child(2n-+2)"); 628 test_balanced_unparseable(":nth-child(2n--2)"); 629 test_balanced_unparseable(":nth-child(2n/**/++2)"); 630 test_balanced_unparseable(":nth-child(2n/**/+-2)"); 631 test_balanced_unparseable(":nth-child(2n/**/-+2)"); 632 test_balanced_unparseable(":nth-child(2n/**/--2)"); 633 test_balanced_unparseable(":nth-child(2n/**/+/**/+2)"); 634 test_balanced_unparseable(":nth-child(2n/**/+/**/-2)"); 635 test_balanced_unparseable(":nth-child(2n/**/-/**/+2)"); 636 test_balanced_unparseable(":nth-child(2n/**/-/**/-2)"); 637 test_balanced_unparseable(":nth-child(2n+/**/+2)"); 638 test_balanced_unparseable(":nth-child(2n+/**/-2)"); 639 test_balanced_unparseable(":nth-child(2n-/**/+2)"); 640 test_balanced_unparseable(":nth-child(2n-/**/-2)"); 641 test_balanced_unparseable(":nth-child(2n++/**/2)"); 642 test_balanced_unparseable(":nth-child(2n+-/**/2)"); 643 test_balanced_unparseable(":nth-child(2n-+/**/2)"); 644 test_balanced_unparseable(":nth-child(2n--/**/2)"); 645 test_balanced_unparseable(":nth-child(2n/**/++/**/2)"); 646 test_balanced_unparseable(":nth-child(2n/**/+-/**/2)"); 647 test_balanced_unparseable(":nth-child(2n/**/-+/**/2)"); 648 test_balanced_unparseable(":nth-child(2n/**/--/**/2)"); 649 test_balanced_unparseable(":nth-child(2n/**/+/**/+/**/2)"); 650 test_balanced_unparseable(":nth-child(2n/**/+/**/-/**/2)"); 651 test_balanced_unparseable(":nth-child(2n/**/-/**/+/**/2)"); 652 test_balanced_unparseable(":nth-child(2n/**/-/**/-/**/2)"); 653 test_balanced_unparseable(":nth-child(2n+/**/+/**/2)"); 654 test_balanced_unparseable(":nth-child(2n+/**/-/**/2)"); 655 test_balanced_unparseable(":nth-child(2n-/**/+/**/2)"); 656 test_balanced_unparseable(":nth-child(2n-/**/-/**/2)"); 657 test_parseable(":nth-child(+/**/n+2)"); 658 test_parseable(":nth-child(+n/**/+2)"); 659 test_parseable(":nth-child(+n/**/+2)"); 660 test_parseable(":nth-child(+n+/**/2)"); 661 test_parseable(":nth-child(+n+2/**/)"); 662 test_balanced_unparseable(":nth-child(+1/**/n+2)"); 663 test_parseable(":nth-child(+1n/**/+2)"); 664 test_parseable(":nth-child(+1n/**/+2)"); 665 test_parseable(":nth-child(+1n+/**/2)"); 666 test_parseable(":nth-child(+1n+2/**/)"); 667 test_balanced_unparseable(":nth-child(-/**/n+2)"); 668 test_parseable(":nth-child(-n/**/+2)"); 669 test_parseable(":nth-child(-n/**/+2)"); 670 test_parseable(":nth-child(-n+/**/2)"); 671 test_parseable(":nth-child(-n+2/**/)"); 672 test_balanced_unparseable(":nth-child(-1/**/n+2)"); 673 test_parseable(":nth-child(-1n/**/+2)"); 674 test_parseable(":nth-child(-1n/**/+2)"); 675 test_parseable(":nth-child(-1n+/**/2)"); 676 test_parseable(":nth-child(-1n+2/**/)"); 677 test_balanced_unparseable(":nth-child(-/**/ n+2)"); 678 test_balanced_unparseable(":nth-child(- /**/n+2)"); 679 test_balanced_unparseable(":nth-child(+/**/ n+2)"); 680 test_balanced_unparseable(":nth-child(+ /**/n+2)"); 681 test_parseable(":nth-child(+/**/n-2)"); 682 test_parseable(":nth-child(+n/**/-2)"); 683 test_parseable(":nth-child(+n/**/-2)"); 684 test_parseable(":nth-child(+n-/**/2)"); 685 test_parseable(":nth-child(+n-2/**/)"); 686 test_balanced_unparseable(":nth-child(+1/**/n-2)"); 687 test_parseable(":nth-child(+1n/**/-2)"); 688 test_parseable(":nth-child(+1n/**/-2)"); 689 test_parseable(":nth-child(+1n-/**/2)"); 690 test_parseable(":nth-child(+1n-2/**/)"); 691 test_balanced_unparseable(":nth-child(-/**/n-2)"); 692 test_parseable(":nth-child(-n/**/-2)"); 693 test_parseable(":nth-child(-n/**/-2)"); 694 test_parseable(":nth-child(-n-/**/2)"); 695 test_parseable(":nth-child(-n-2/**/)"); 696 test_balanced_unparseable(":nth-child(-1/**/n-2)"); 697 test_parseable(":nth-child(-1n/**/-2)"); 698 test_parseable(":nth-child(-1n/**/-2)"); 699 test_parseable(":nth-child(-1n-/**/2)"); 700 test_parseable(":nth-child(-1n-2/**/)"); 701 test_balanced_unparseable(":nth-child(-/**/ n-2)"); 702 test_balanced_unparseable(":nth-child(- /**/n-2)"); 703 test_balanced_unparseable(":nth-child(+/**/ n-2)"); 704 test_balanced_unparseable(":nth-child(+ /**/n-2)"); 705 test_parseable(":nth-child(+/**/N-2)"); 706 test_parseable(":nth-child(+N/**/-2)"); 707 test_parseable(":nth-child(+N/**/-2)"); 708 test_parseable(":nth-child(+N-/**/2)"); 709 test_parseable(":nth-child(+N-2/**/)"); 710 test_balanced_unparseable(":nth-child(+1/**/N-2)"); 711 test_parseable(":nth-child(+1N/**/-2)"); 712 test_parseable(":nth-child(+1N/**/-2)"); 713 test_parseable(":nth-child(+1N-/**/2)"); 714 test_parseable(":nth-child(+1N-2/**/)"); 715 test_balanced_unparseable(":nth-child(-/**/N-2)"); 716 test_parseable(":nth-child(-N/**/-2)"); 717 test_parseable(":nth-child(-N/**/-2)"); 718 test_parseable(":nth-child(-N-/**/2)"); 719 test_parseable(":nth-child(-N-2/**/)"); 720 test_balanced_unparseable(":nth-child(-1/**/N-2)"); 721 test_parseable(":nth-child(-1N/**/-2)"); 722 test_parseable(":nth-child(-1N/**/-2)"); 723 test_parseable(":nth-child(-1N-/**/2)"); 724 test_parseable(":nth-child(-1N-2/**/)"); 725 test_balanced_unparseable(":nth-child(-/**/ N-2)"); 726 test_balanced_unparseable(":nth-child(- /**/N-2)"); 727 test_balanced_unparseable(":nth-child(+/**/ N-2)"); 728 test_balanced_unparseable(":nth-child(+ /**/N-2)"); 729 test_parseable(":nth-child( +n + 1 )"); 730 test_parseable(":nth-child( +/**/n + 1 )"); 731 test_balanced_unparseable(":nth-child( -/**/2/**/n/**/+/**/4 )"); 732 test_parseable(":nth-child( -2n/**/ + /**/4 )"); 733 test_parseable(":nth-child( -2n/**/+/**/4 )"); 734 test_parseable(":nth-child( -2n /**/+/**/4 )"); 735 test_balanced_unparseable(":nth-child( -/**/n /**/+ /**/ 4 )"); 736 test_parseable(":nth-child( +/**/n /**/+ /**/ 4 )"); 737 test_balanced_unparseable(":nth-child(+1/**/n-1)"); 738 test_balanced_unparseable(":nth-child(1/**/n-1)"); 739 // bug 876570 740 test_balanced_unparseable(":nth-child(+2n-)"); 741 test_balanced_unparseable(":nth-child(+n-)"); 742 test_balanced_unparseable(":nth-child(-2n-)"); 743 test_balanced_unparseable(":nth-child(-n-)"); 744 test_balanced_unparseable(":nth-child(2n-)"); 745 test_balanced_unparseable(":nth-child(n-)"); 746 test_balanced_unparseable(":nth-child(+2n+)"); 747 test_balanced_unparseable(":nth-child(+n+)"); 748 test_balanced_unparseable(":nth-child(-2n+)"); 749 test_balanced_unparseable(":nth-child(-n+)"); 750 test_balanced_unparseable(":nth-child(2n+)"); 751 test_balanced_unparseable(":nth-child(n+)"); 752 753 // exercise the an+b matching logic particularly hard for 754 // :nth-child() (since we know we use the same code for all 4) 755 var seven_ps = "<p></p><p></p><p></p><p></p><p></p><p></p><p></p>"; 756 function pset(indices) { // takes an array of 1-based indices 757 return function pset_filter(doc) { 758 var a = doc.getElementsByTagName("p"); 759 var result = []; 760 for (var i in indices) 761 result.push(a[indices[i] - 1]); 762 return result; 763 } 764 } 765 test_selector_in_html(":nth-child(0)", seven_ps, 766 pset([]), pset([1, 2, 3, 4, 5, 6, 7])); 767 test_selector_in_html(":nth-child(-3)", seven_ps, 768 pset([]), pset([1, 2, 3, 4, 5, 6, 7])); 769 test_selector_in_html(":nth-child(3)", seven_ps, 770 pset([3]), pset([1, 2, 4, 5, 6, 7])); 771 test_selector_in_html(":nth-child(0n+3)", seven_ps, 772 pset([3]), pset([1, 2, 4, 5, 6, 7])); 773 test_selector_in_html(":nth-child(-0n+3)", seven_ps, 774 pset([3]), pset([1, 2, 4, 5, 6, 7])); 775 test_selector_in_html(":nth-child(8)", seven_ps, 776 pset([]), pset([1, 2, 3, 4, 5, 6, 7])); 777 test_selector_in_html(":nth-child(odd)", seven_ps, 778 pset([1, 3, 5, 7]), pset([2, 4, 6])); 779 test_selector_in_html(":nth-child(even)", seven_ps, 780 pset([2, 4, 6]), pset([1, 3, 5, 7])); 781 test_selector_in_html(":nth-child(2n-1)", seven_ps, 782 pset([1, 3, 5, 7]), pset([2, 4, 6])); 783 test_selector_in_html(":nth-child( 2n - 1 )", seven_ps, 784 pset([1, 3, 5, 7]), pset([2, 4, 6])); 785 test_selector_in_html(":nth-child(2n+1)", seven_ps, 786 pset([1, 3, 5, 7]), pset([2, 4, 6])); 787 test_selector_in_html(":nth-child( 2n + 1 )", seven_ps, 788 pset([1, 3, 5, 7]), pset([2, 4, 6])); 789 test_selector_in_html(":nth-child(2n+0)", seven_ps, 790 pset([2, 4, 6]), pset([1, 3, 5, 7])); 791 test_selector_in_html(":nth-child(2n-0)", seven_ps, 792 pset([2, 4, 6]), pset([1, 3, 5, 7])); 793 test_selector_in_html(":nth-child(-n+3)", seven_ps, 794 pset([1, 2, 3]), pset([4, 5, 6, 7])); 795 test_selector_in_html(":nth-child(-n-3)", seven_ps, 796 pset([]), pset([1, 2, 3, 4, 5, 6, 7])); 797 test_selector_in_html(":nth-child(n)", seven_ps, 798 pset([1, 2, 3, 4, 5, 6, 7]), pset([])); 799 test_selector_in_html(":nth-child(n-3)", seven_ps, 800 pset([1, 2, 3, 4, 5, 6, 7]), pset([])); 801 test_selector_in_html(":nth-child(n+3)", seven_ps, 802 pset([3, 4, 5, 6, 7]), pset([1, 2])); 803 test_selector_in_html(":nth-child(2n+3)", seven_ps, 804 pset([3, 5, 7]), pset([1, 2, 4, 6])); 805 test_selector_in_html(":nth-child(2n)", seven_ps, 806 pset([2, 4, 6]), pset([1, 3, 5, 7])); 807 test_selector_in_html(":nth-child(2n-3)", seven_ps, 808 pset([1, 3, 5, 7]), pset([2, 4, 6])); 809 test_selector_in_html(":nth-child(-1n+3)", seven_ps, 810 pset([1, 2, 3]), pset([4, 5, 6, 7])); 811 test_selector_in_html(":nth-child(-2n+3)", seven_ps, 812 pset([1, 3]), pset([2, 4, 5, 6, 7])); 813 // And a few spot-checks for the other :nth-* selectors 814 test_selector_in_html(":nth-child(4n+1)", seven_ps, 815 pset([1, 5]), pset([2, 3, 4, 6, 7])); 816 test_selector_in_html(":nth-last-child(4n+1)", seven_ps, 817 pset([3, 7]), pset([1, 2, 4, 5, 6])); 818 test_selector_in_html(":nth-of-type(4n+1)", seven_ps, 819 pset([1, 5]), pset([2, 3, 4, 6, 7])); 820 test_selector_in_html(":nth-last-of-type(4n+1)", seven_ps, 821 pset([3, 7]), pset([1, 2, 4, 5, 6])); 822 test_selector_in_html(":nth-child(6)", seven_ps, 823 pset([6]), pset([1, 2, 3, 4, 5, 7])); 824 test_selector_in_html(":nth-last-child(6)", seven_ps, 825 pset([2]), pset([1, 3, 4, 5, 6, 7])); 826 test_selector_in_html(":nth-of-type(6)", seven_ps, 827 pset([6]), pset([1, 2, 3, 4, 5, 7])); 828 test_selector_in_html(":nth-last-of-type(6)", seven_ps, 829 pset([2]), pset([1, 3, 4, 5, 6, 7])); 830 831 // Test [first|last|only]-[child|node|of-type] 832 var interesting_doc = "<!----> <div id='p1'> <!---->x<p id='s1'></p> <!----><p id='s2'></p> <!----></div> <!----><p id='p2'> <!----><span id='s3'></span> <!----><span id='s4'></span> <!---->x</p> <!----><div id='p3'> <!----><p id='s5'></p> <!----></div> <!---->"; 833 function idset(ids) { // takes an array of ids 834 return function idset_filter(doc) { 835 var result = []; 836 for (var id of ids) 837 result.push(doc.getElementById(id)); 838 return result; 839 } 840 } 841 function classset(classes) { // takes an array of classes 842 return function classset_filter(doc) { 843 var i, j, els; 844 var result = []; 845 for (i = 0; i < classes.length; i++) { 846 els = doc.getElementsByClassName(classes[i]); 847 for (j = 0; j < els.length; j++) { 848 result.push(els[j]); 849 } 850 } 851 return result; 852 } 853 } 854 function emptyset(doc) { return []; } 855 test_parseable(":first-child"); 856 test_parseable(":last-child"); 857 test_parseable(":only-child"); 858 test_parseable(":-moz-first-node"); 859 test_parseable(":-moz-last-node"); 860 test_parseable(":first-of-type"); 861 test_parseable(":last-of-type"); 862 test_parseable(":only-of-type"); 863 test_selector_in_html(":first-child", seven_ps, 864 pset([1]), pset([2, 3, 4, 5, 6, 7])); 865 test_selector_in_html(":first-child", interesting_doc, 866 idset(["p1", "s1", "s3", "s5"]), 867 idset(["s2", "p2", "s4", "p3"])); 868 test_selector_in_html(":-moz-first-node", interesting_doc, 869 idset(["p1", "s3", "s5"]), 870 idset(["s1", "s2", "p2", "s4", "p3"])); 871 test_selector_in_html(":last-child", seven_ps, 872 pset([7]), pset([1, 2, 3, 4, 5, 6])); 873 test_selector_in_html(":last-child", interesting_doc, 874 idset(["s2", "s4", "p3", "s5"]), 875 idset(["p1", "s1", "p2", "s3"])); 876 test_selector_in_html(":-moz-last-node", interesting_doc, 877 idset(["s2", "p3", "s5"]), 878 idset(["p1", "s1", "p2", "s3", "s4"])); 879 test_selector_in_html(":only-child", seven_ps, 880 pset([]), pset([1, 2, 3, 4, 5, 6, 7])); 881 test_selector_in_html(":only-child", interesting_doc, 882 idset(["s5"]), 883 idset(["p1", "s1", "s2", "p2", "s3", "s4", "p3"])); 884 test_selector_in_html(":first-of-type", seven_ps, 885 pset([1]), pset([2, 3, 4, 5, 6, 7])); 886 test_selector_in_html(":first-of-type", interesting_doc, 887 idset(["p1", "s1", "p2", "s3", "s5"]), 888 idset(["s2", "s4", "p3"])); 889 test_selector_in_html(":last-of-type", seven_ps, 890 pset([7]), pset([1, 2, 3, 4, 5, 6])); 891 test_selector_in_html(":last-of-type", interesting_doc, 892 idset(["s2", "p2", "s4", "p3", "s5"]), 893 idset(["p1", "s1", "s3"])); 894 test_selector_in_html(":only-of-type", seven_ps, 895 pset([]), pset([1, 2, 3, 4, 5, 6, 7])); 896 test_selector_in_html(":only-of-type", interesting_doc, 897 idset(["p2", "s5"]), 898 idset(["p1", "s1", "s2", "s3", "s4", "p3"])); 899 900 // And a bunch of tests for the of-type aspect of :nth-of-type() and 901 // :nth-last-of-type(). Note that the last div here contains two 902 // children. 903 var mixed_elements="<p></p><p></p><div></div><p></p><div><p></p><address></address></div><address></address>"; 904 function pdaset(ps, divs, addresses) { // takes an array of 1-based indices 905 var l = { p: ps, div: divs, address: addresses }; 906 return function pdaset_filter(doc) { 907 var result = []; 908 for (var tag in l) { 909 var a = doc.getElementsByTagName(tag); 910 var indices = l[tag]; 911 for (var i in indices) 912 result.push(a[indices[i] - 1]); 913 } 914 return result; 915 } 916 } 917 test_selector_in_html(":nth-of-type(odd)", mixed_elements, 918 pdaset([1, 3, 4], [1], [1, 2]), 919 pdaset([2], [2], [])); 920 test_selector_in_html(":nth-of-type(2n-0)", mixed_elements, 921 pdaset([2], [2], []), 922 pdaset([1, 3, 4], [1], [1, 2])); 923 test_selector_in_html(":nth-last-of-type(even)", mixed_elements, 924 pdaset([2], [1], []), 925 pdaset([1, 3, 4], [2], [1, 2])); 926 927 // Test greediness of descendant combinators. 928 var four_children="<div id='a'><div id='b'><div id='c'><div id='d'><\/div><\/div><\/div><\/div>"; 929 test_selector_in_html("#a > div div", four_children, 930 idset(["c", "d"]), idset(["a", "b"])); 931 test_selector_in_html("#a > #b div", four_children, 932 idset(["c", "d"]), idset(["a", "b"])); 933 test_selector_in_html("#a div > div", four_children, 934 idset(["c", "d"]), idset(["a", "b"])); 935 test_selector_in_html("#a #b > div", four_children, 936 idset(["c"]), idset(["a", "b", "d"])); 937 test_selector_in_html("#a > #b div", four_children, 938 idset(["c", "d"]), idset(["a", "b"])); 939 test_selector_in_html("#a #c > div", four_children, 940 idset(["d"]), idset(["a", "b", "c"])); 941 test_selector_in_html("#a > #c div", four_children, 942 idset([]), idset(["a", "b", "c", "d"])); 943 944 // More descendant combinator greediness (bug 511147) 945 test_selector_in_html(".a > .b ~ .match", '<div class="a"><div class="b"></div><div class="match"></div></div>', 946 classset(["match"]), classset(["a", "b"])); 947 test_selector_in_html(".a > .b ~ .match", '<div class="a"><div class="b"></div><div class="x"></div><div class="match"></div></div>', 948 classset(["match"]), classset(["a", "b", "x"])); 949 test_selector_in_html(".a > .b ~ .match", '<div class="a"><div class="b"><p>filler filler <i>filler</i> filler</p></div><div class="match"></div></div>', 950 classset(["match"]), classset(["a", "b", "x"])); 951 test_selector_in_html(".a > .b ~ .match", '<div class="a"><div class="x"><p>filler filler <i>filler</i> filler</p></div><div></div><div class="b"></div><div></div><div class="x"><p>filler filler <i>filler</i> filler</p></div><div class="match"></div></div>', 952 classset(["match"]), classset(["a", "b", "x"])); 953 test_selector_in_html(".a > .b ~ .match", '<div class="a"><div class="b"></div><div class="match"></div><div class="match"></div></div>', 954 classset(["match"]), classset(["a", "b"])); 955 956 test_selector_in_html(".a > .b ~ .nomatch", '<div class="a"><div><div class="b"></div><div class="nomatch"></div></div></div>', 957 emptyset, classset(["a", "b", "nomatch"])); 958 test_selector_in_html(".a > .b ~ .nomatch", '<div class="a"><div><div class="b"></div><div class="nomatch"></div></div><div class="nomatch"></div></div>', 959 emptyset, classset(["a", "b", "nomatch"])); 960 test_selector_in_html(".a > .b ~ .nomatch", '<div class="a"><div class="b"></div><div><div class="nomatch"></div></div><div></div></div>', 961 emptyset, classset(["a", "b", "nomatch"])); 962 test_selector_in_html(".a > .b ~ .nomatch", '<div class="a"><div class="b"></div></div><div class="nomatch"></div>', 963 emptyset, classset(["a", "b", "nomatch"])); 964 965 // Test serialization of pseudo-elements. 966 should_serialize_to("p::first-letter", "p::first-letter"); 967 should_serialize_to("p:first-letter", "p::first-letter"); 968 should_serialize_to("div>p:first-letter", "div > p::first-letter"); 969 should_serialize_to("span +div:first-line", "span + div::first-line"); 970 should_serialize_to("input::placeholder", "input::placeholder"); 971 should_serialize_to("input:placeholder-shown", "input:placeholder-shown"); 972 973 // Test serialization of ::-moz-placeholder. 974 should_serialize_to("input::-moz-placeholder", "input::placeholder"); 975 976 should_serialize_to(':lang("foo\\"bar")', ':lang(foo\\"bar)'); 977 978 // Test default namespaces, including inside :not(). 979 var html_default_ns = "@namespace url(http://www.w3.org/1999/xhtml);"; 980 var html_ns = "@namespace html url(http://www.w3.org/1999/xhtml);"; 981 var xul_default_ns = "@namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);"; 982 var single_a = "<a id='a' href='data:text/plain,this_better_be_unvisited'></a>"; 983 var set_single = idset(['a']); 984 var empty_set = idset([]); 985 test_selector_in_html("a", single_a, set_single, empty_set, 986 html_default_ns); 987 test_selector_in_html("a", single_a, empty_set, set_single, 988 xul_default_ns); 989 test_selector_in_html("*|a", single_a, set_single, empty_set, 990 xul_default_ns); 991 test_selector_in_html("html|a", single_a, set_single, empty_set, 992 xul_default_ns + html_ns); 993 // Type selectors inside :not() bring in default namespaces, but 994 // non-type selectors don't. 995 test_selector_in_html("*|a:not(*)", single_a, set_single, empty_set, 996 xul_default_ns); 997 test_selector_in_html("*|a:not(a)", single_a, set_single, empty_set, 998 xul_default_ns); 999 test_selector_in_html("*|a:not(*|*)", single_a, empty_set, set_single, 1000 xul_default_ns); 1001 test_selector_in_html("*|a:not(*|a)", single_a, empty_set, set_single, 1002 xul_default_ns); 1003 test_selector_in_html("*|a:not(:link)", single_a + "<a id='b'></a>", 1004 idset(["b"]), set_single, 1005 xul_default_ns); 1006 test_selector_in_html("*|a:not(:visited)", single_a + "<a id='b'></a>", 1007 idset(["a", "b"]), empty_set, 1008 xul_default_ns); 1009 test_selector_in_html("*|a:not(html|*)", single_a, empty_set, set_single, 1010 xul_default_ns + html_ns); 1011 test_selector_in_html("*|a:not(html|a)", single_a, empty_set, set_single, 1012 xul_default_ns + html_ns); 1013 test_selector_in_html("*|a:not(|*)", single_a, set_single, empty_set, 1014 xul_default_ns + html_ns); 1015 test_selector_in_html("*|a:not(|a)", single_a, set_single, empty_set, 1016 xul_default_ns + html_ns); 1017 test_selector_in_html("html|a:not(|*)", single_a, set_single, empty_set, 1018 xul_default_ns + html_ns); 1019 test_selector_in_html("html|a:not(|a)", single_a, set_single, empty_set, 1020 xul_default_ns + html_ns); 1021 test_selector_in_html("html|a:not(*|*)", single_a, empty_set, set_single, 1022 xul_default_ns + html_ns); 1023 test_selector_in_html("html|a:not(*|a)", single_a, empty_set, set_single, 1024 xul_default_ns + html_ns); 1025 1026 // Test -moz-locale-dir 1027 test_balanced_unparseable(":-moz-locale-dir(ltr)"); 1028 test_balanced_unparseable(":-moz-locale-dir(rtl)"); 1029 test_balanced_unparseable(":-moz-locale-dir(rTl)"); 1030 test_balanced_unparseable(":-moz-locale-dir(LTR)"); 1031 test_balanced_unparseable(":-moz-locale-dir(other)"); 1032 1033 test_balanced_unparseable(":-moz-locale-dir()"); 1034 test_balanced_unparseable(":-moz-locale-dir(())"); 1035 test_balanced_unparseable(":-moz-locale-dir(3())"); 1036 test_balanced_unparseable(":-moz-locale-dir(f{})"); 1037 test_balanced_unparseable(":-moz-locale-dir('ltr')"); 1038 test_balanced_unparseable(":-moz-locale-dir(ltr, other)"); 1039 test_balanced_unparseable(":-moz-locale-dir(ltr other)"); 1040 test_balanced_unparseable(":-moz-locale-dir"); 1041 1042 // Test :dir() 1043 test_parseable(":dir(ltr)"); 1044 test_parseable(":dir(rtl)"); 1045 test_parseable(":dir(rTl)"); 1046 test_parseable(":dir(LTR)"); 1047 test_parseable(":dir(other)"); 1048 if (document.body.matches(":dir(ltr)")) { 1049 test_selector_in_html("a:dir(LTr)", single_a, 1050 set_single, empty_set); 1051 test_selector_in_html("a:dir(ltR)", single_a, 1052 set_single, empty_set); 1053 test_selector_in_html("a:dir(LTR)", single_a, 1054 set_single, empty_set); 1055 test_selector_in_html("a:dir(RTl)", single_a, 1056 empty_set, set_single); 1057 } else { 1058 test_selector_in_html("a:dir(RTl)", single_a, 1059 set_single, empty_set); 1060 test_selector_in_html("a:dir(rtL)", single_a, 1061 set_single, empty_set); 1062 test_selector_in_html("a:dir(RTL)", single_a, 1063 set_single, empty_set); 1064 test_selector_in_html("a:dir(LTr)", single_a, 1065 empty_set, set_single); 1066 } 1067 test_selector_in_html("a:dir(other)", single_a, 1068 empty_set, set_single); 1069 1070 test_balanced_unparseable(":dir()"); 1071 test_balanced_unparseable(":dir(())"); 1072 test_balanced_unparseable(":dir(3())"); 1073 test_balanced_unparseable(":dir(f{})"); 1074 test_balanced_unparseable(":dir('ltr')"); 1075 test_balanced_unparseable(":dir(ltr, other)"); 1076 test_balanced_unparseable(":dir(ltr other)"); 1077 test_balanced_unparseable(":dir"); 1078 1079 // Test chrome-only -moz-lwtheme 1080 test_balanced_unparseable(":-moz-broken"); 1081 1082 test_balanced_unparseable(":-moz-tree-row(selected)"); 1083 test_balanced_unparseable("::-moz-tree-row(selected)"); 1084 test_balanced_unparseable("::-MoZ-trEE-RoW(sElEcTeD)"); 1085 test_balanced_unparseable(":-moz-tree-row(selected focus)"); 1086 test_balanced_unparseable(":-moz-tree-row(selected , focus)"); 1087 test_balanced_unparseable("::-moz-tree-row(selected ,focus)"); 1088 test_balanced_unparseable(":-moz-tree-row(selected, focus)"); 1089 test_balanced_unparseable("::-moz-tree-row(selected,focus)"); 1090 test_balanced_unparseable(":-moz-tree-row(selected focus)"); 1091 test_balanced_unparseable("::-moz-tree-row(selected , focus)"); 1092 test_balanced_unparseable("::-moz-tree-twisty( hover open )"); 1093 test_balanced_unparseable("::-moz-tree-row(selected {[]} )"); 1094 test_balanced_unparseable(":-moz-tree-twisty(open())"); 1095 test_balanced_unparseable("::-moz-tree-twisty(hover ())"); 1096 1097 test_parseable(":-moz-window-inactive"); 1098 test_parseable("div p:-moz-window-inactive:hover span"); 1099 1100 // Chrome-only 1101 test_unbalanced_unparseable(":-moz-browser-frame"); 1102 1103 // Plugin pseudoclasses are chrome-only: 1104 test_unbalanced_unparseable(":-moz-type-unsupported"); 1105 test_unbalanced_unparseable(":-moz-type-unsupported-platform"); 1106 test_unbalanced_unparseable(":-moz-handler-clicktoplay"); 1107 test_unbalanced_unparseable(":-moz-handler-vulnerable-updatable"); 1108 test_unbalanced_unparseable(":-moz-handler-vulnerable-no-update"); 1109 test_unbalanced_unparseable(":-moz-handler-disabled"); 1110 test_unbalanced_unparseable(":-moz-handler-blocked"); 1111 test_unbalanced_unparseable(":-moz-handler-crashed"); 1112 1113 // We're not in a UA sheet, so this should be invalid. 1114 test_balanced_unparseable(":-moz-inert"); 1115 test_balanced_unparseable(":-moz-native-anonymous"); 1116 test_balanced_unparseable(":-moz-table-border-nonzero"); 1117 1118 // Case sensitivity of tag selectors 1119 function setup_cased_spans(body) { 1120 var data = [ 1121 { tag: "span" }, 1122 { tag: "sPaN" }, 1123 { tag: "Span" }, 1124 { tag: "SPAN" }, 1125 { ns: "http://www.w3.org/1999/xhtml", tag: "span" }, 1126 { ns: "http://www.w3.org/1999/xhtml", tag: "sPaN" }, 1127 { ns: "http://www.w3.org/1999/xhtml", tag: "Span" }, 1128 { ns: "http://www.w3.org/1999/xhtml", tag: "SPAN" }, 1129 { ns: "http://example.com/useless", tag: "span" }, 1130 { ns: "http://example.com/useless", tag: "sPaN" }, 1131 { ns: "http://example.com/useless", tag: "Span" }, 1132 { ns: "http://example.com/useless", tag: "SPAN" }, 1133 ] 1134 for (var i in data) { 1135 var ent = data[i]; 1136 var elem; 1137 if ("ns" in ent) { 1138 elem = body.ownerDocument.createElementNS(ent.ns, ent.tag); 1139 } else { 1140 elem = body.ownerDocument.createElement(ent.tag); 1141 } 1142 body.appendChild(elem); 1143 } 1144 } 1145 function bodychildset(indices) { 1146 return function bodychildset_filter(doc) { 1147 var body = doc.body; 1148 var result = []; 1149 for (var i in indices) { 1150 result.push(body.childNodes[indices[i]]); 1151 } 1152 return result; 1153 } 1154 } 1155 test_selector_in_html("span", setup_cased_spans, 1156 bodychildset([0, 1, 2, 3, 4, 8]), 1157 bodychildset([5, 6, 7, 9, 10, 11])); 1158 test_selector_in_html("sPaN", setup_cased_spans, 1159 bodychildset([0, 1, 2, 3, 4, 9]), 1160 bodychildset([5, 6, 7, 8, 10, 11])); 1161 test_selector_in_html("Span", setup_cased_spans, 1162 bodychildset([0, 1, 2, 3, 4, 10]), 1163 bodychildset([5, 6, 7, 8, 9, 11])); 1164 test_selector_in_html("SPAN", setup_cased_spans, 1165 bodychildset([0, 1, 2, 3, 4, 11]), 1166 bodychildset([5, 6, 7, 8, 9, 10])); 1167 1168 // bug 528096 (tree pseudos) 1169 test_unbalanced_unparseable(":-moz-tree-column((){} a"); 1170 test_unbalanced_unparseable(":-moz-tree-column(x(){} a"); 1171 test_unbalanced_unparseable(":-moz-tree-column(a b (){} a"); 1172 test_unbalanced_unparseable(":-moz-tree-column(a, b (){} a"); 1173 1174 // Bug 543428 (escaping) 1175 test_selector_in_html("\\32|a", single_a, set_single, empty_set, 1176 "@namespace \\32 url(http://www.w3.org/1999/xhtml);"); 1177 test_selector_in_html("-\\32|a", single_a, set_single, empty_set, 1178 "@namespace -\\32 url(http://www.w3.org/1999/xhtml);"); 1179 test_selector_in_html("\\2|a", single_a, set_single, empty_set, 1180 "@namespace \\0002 url(http://www.w3.org/1999/xhtml);"); 1181 test_selector_in_html("-\\2|a", single_a, set_single, empty_set, 1182 "@namespace -\\000002 url(http://www.w3.org/1999/xhtml);"); 1183 var spans = "<span class='2'></span><span class=''></span>" + 1184 "<span id='2'></span><span id=''></span>" 1185 test_selector_in_html(".\\32", spans, 1186 bodychildset([0]), bodychildset([1, 2, 3])); 1187 test_selector_in_html("[class=\\32]", spans, 1188 bodychildset([0]), bodychildset([1, 2, 3])); 1189 test_selector_in_html(".\\2", spans, 1190 bodychildset([1]), bodychildset([0, 2, 3])); 1191 test_selector_in_html("[class=\\2]", spans, 1192 bodychildset([1]), bodychildset([0, 2, 3])); 1193 test_selector_in_html("#\\32", spans, 1194 bodychildset([2]), bodychildset([0, 1, 3])); 1195 test_selector_in_html("[id=\\32]", spans, 1196 bodychildset([2]), bodychildset([0, 1, 3])); 1197 test_selector_in_html("#\\2", spans, 1198 bodychildset([3]), bodychildset([0, 1, 2])); 1199 test_selector_in_html("[id=\\2]", spans, 1200 bodychildset([3]), bodychildset([0, 1, 2])); 1201 test_balanced_unparseable("#2"); 1202 1203 // Bug 553805: :not() containing nothing is forbidden 1204 test_balanced_unparseable(":not()"); 1205 test_balanced_unparseable(":not( )"); 1206 test_balanced_unparseable(":not( \t\n )"); 1207 test_balanced_unparseable(":not(/*comment*/)"); 1208 test_balanced_unparseable(":not( /*comment*/ /* comment */ )"); 1209 test_balanced_unparseable("p :not()"); 1210 test_balanced_unparseable("p :not( )"); 1211 test_balanced_unparseable("p :not( \t\n )"); 1212 test_balanced_unparseable("p :not(/*comment*/)"); 1213 test_balanced_unparseable("p :not( /*comment*/ /* comment */ )"); 1214 test_balanced_unparseable("p:not()"); 1215 test_balanced_unparseable("p:not( )"); 1216 test_balanced_unparseable("p:not( \t\n )"); 1217 test_balanced_unparseable("p:not(/*comment*/)"); 1218 test_balanced_unparseable("p:not( /*comment*/ /* comment */ )"); 1219 1220 test_balanced_unparseable(":not(:nth-child(2k))"); 1221 test_balanced_unparseable(":not(:nth-child(()))"); 1222 1223 // Bug 1685621 - Serialization of :not() 1224 should_serialize_to(":not([disabled][selected])", ":not([disabled][selected])"); 1225 should_serialize_to(":not([disabled],[selected])", ":not([disabled], [selected])"); 1226 1227 // :-moz-any() 1228 test_parseable(":-moz-any()"); 1229 test_parseable(":-moz-any('foo')"); 1230 test_parseable(":-moz-any(div p)"); 1231 test_parseable(":-moz-any(div ~ p)"); 1232 test_parseable(":-moz-any(div~p)"); 1233 test_parseable(":-moz-any(div + p)"); 1234 test_parseable(":-moz-any(div+p)"); 1235 test_parseable(":-moz-any(div > p)"); 1236 test_parseable(":-moz-any(div>p)"); 1237 test_parseable(":-moz-any(div, p)"); 1238 test_parseable(":-moz-any( div , p )"); 1239 test_parseable(":-moz-any(div,p)"); 1240 test_parseable(":-moz-any(div)"); 1241 test_parseable(":-moz-any(div,p,:link,span:focus)"); 1242 test_parseable(":-moz-any(:active,:focus)"); 1243 test_parseable(":-moz-any(:active,:link:focus)"); 1244 test_parseable(":-moz-any(div,:nonexistentpseudo)"); 1245 var any_elts = "<input type='text'><a href='http://www.example.com/'></a><div></div><a name='foo'>"; 1246 test_selector_in_html(":-moz-any(a,input)", any_elts, 1247 bodychildset([0, 1, 3]), bodychildset([2])); 1248 test_selector_in_html(":-moz-any(:link,:not(a))", any_elts, 1249 bodychildset([0, 1, 2]), bodychildset([3])); 1250 test_selector_in_html(":-moz-any([href],input[type],input[name])", any_elts, 1251 bodychildset([0, 1]), bodychildset([2, 3])); 1252 test_selector_in_html(":-moz-any(div,a):-moz-any([type],[href],[name])", 1253 any_elts, 1254 bodychildset([1, 3]), bodychildset([0, 2])); 1255 1256 // Test that we don't tokenize an empty HASH. 1257 test_balanced_unparseable("#"); 1258 test_balanced_unparseable("# "); 1259 test_balanced_unparseable("#, p"); 1260 test_balanced_unparseable("# , p"); 1261 test_balanced_unparseable("p #"); 1262 test_balanced_unparseable("p # "); 1263 test_balanced_unparseable("p #, p"); 1264 test_balanced_unparseable("p # , p"); 1265 1266 // Test that a backslash alone at EOF outside of a string is treated 1267 // as U+FFFD. 1268 test_parseable_via_api("#a\\"); 1269 test_parseable_via_api("#\\"); 1270 test_parseable_via_api("\\"); 1271 1272 // Test that newline escapes are only supported in strings. 1273 test_balanced_unparseable("di\\\nv"); 1274 test_balanced_unparseable("div \\\n p"); 1275 test_balanced_unparseable("div\\\n p"); 1276 test_balanced_unparseable("div \\\np"); 1277 test_balanced_unparseable("div\\\np"); 1278 1279 // Test that :-moz-placeholder is parsable. 1280 test_parseable(":-moz-placeholder"); 1281 1282 // Test that things other than user-action pseudo-classes are 1283 // rejected after pseudo-elements. Some of these tests rely on 1284 // using a pseudo-element that supports a user-action pseudo-class 1285 // after it, so we need to use the prefixed ::-moz-color-swatch, 1286 // which is one of the ones with 1287 // CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (none of which are 1288 // unprefixed). 1289 test_parseable("::-moz-color-swatch:hover"); 1290 test_parseable("::-moz-color-swatch:is(:hover)"); 1291 test_parseable("::-moz-color-swatch:not(:hover)"); 1292 test_parseable("::-moz-color-swatch:where(:hover)"); 1293 test_balanced_unparseable("::-moz-color-swatch:not(.foo)"); 1294 test_balanced_unparseable("::-moz-color-swatch:first-child"); 1295 test_balanced_unparseable("::-moz-color-swatch:host"); 1296 test_balanced_unparseable("::-moz-color-swatch:host(div)"); 1297 test_balanced_unparseable("::-moz-color-swatch:nth-child(1)"); 1298 test_balanced_unparseable("::-moz-color-swatch:hover#foo"); 1299 test_balanced_unparseable(".foo::after:not(.bar) ~ h3"); 1300 1301 for (let selector of [ 1302 "::-moz-color-swatch:where(.foo)", 1303 "::-moz-color-swatch:is(.foo)", 1304 "::-moz-color-swatch:where(p, :hover)", 1305 "::-moz-color-swatch:is(p, :hover)", 1306 ]) { 1307 test_parseable(selector); 1308 should_serialize_to(selector, selector); 1309 ok(!CSS.supports(`selector(${selector})`), "supports should report false for forging selector parse failure"); 1310 } 1311 1312 run_deferred_tests(); 1313 } 1314 1315 var deferred_tests = []; 1316 1317 function defer_clonedoc_tests(docurl, onloadfunc) 1318 { 1319 deferred_tests.push( { docurl: docurl, onloadfunc: onloadfunc } ); 1320 } 1321 1322 function run_deferred_tests() 1323 { 1324 if (deferred_tests.length == 0) { 1325 SimpleTest.finish(); 1326 return; 1327 } 1328 1329 cloneiframe.onload = deferred_tests_onload; 1330 cloneiframe.src = deferred_tests[0].docurl; 1331 } 1332 1333 function deferred_tests_onload(event) 1334 { 1335 if (event.target != cloneiframe) 1336 return; 1337 1338 deferred_tests[0].onloadfunc(); 1339 deferred_tests.shift(); 1340 1341 run_deferred_tests(); 1342 } 1343 1344 </script> 1345 </pre> 1346 </body> 1347 </html>