2008-03-05 16:06:15 -08:00
|
|
|
<!DOCTYPE HTML>
|
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Test for CSS Selectors</title>
|
|
|
|
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
|
|
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
|
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
|
|
|
</head>
|
|
|
|
<body onload="run()">
|
|
|
|
<p id="display"><iframe id="iframe" src="about:blank"></iframe></p>
|
|
|
|
<pre id="test">
|
|
|
|
<script class="testbody" type="text/javascript">
|
|
|
|
|
|
|
|
SimpleTest.waitForExplicitFinish();
|
|
|
|
|
|
|
|
function run() {
|
|
|
|
|
|
|
|
var iframe = document.getElementById("iframe");
|
|
|
|
var ifwin = iframe.contentWindow;
|
|
|
|
var ifdoc = iframe.contentDocument;
|
|
|
|
|
|
|
|
function setup_style() {
|
|
|
|
var style_elem = ifdoc.createElement("style");
|
|
|
|
style_elem.setAttribute("type", "text/css");
|
|
|
|
ifdoc.getElementsByTagName("head")[0].appendChild(style_elem);
|
|
|
|
var style_text = ifdoc.createTextNode("");
|
|
|
|
style_elem.appendChild(style_text);
|
|
|
|
return style_text;
|
|
|
|
}
|
|
|
|
|
|
|
|
var style_text = setup_style();
|
|
|
|
|
|
|
|
var gCounter = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* selector: the selector to test
|
|
|
|
* body_contents: what to set the body's innerHTML to
|
|
|
|
* match_fn: a function that, given the document object into which
|
|
|
|
* body_contents has been inserted, produces an array of nodes that
|
|
|
|
* should match selector
|
|
|
|
* notmatch_fn: likewise, but for nodes that should not match
|
|
|
|
*/
|
|
|
|
function test_selector_in_html(selector, body_contents, match_fn, notmatch_fn)
|
|
|
|
{
|
|
|
|
var zi = ++gCounter;
|
2008-06-02 20:17:35 -07:00
|
|
|
if (typeof(body_contents) == "string") {
|
|
|
|
ifdoc.body.innerHTML = body_contents;
|
|
|
|
} else {
|
|
|
|
// It's a function.
|
|
|
|
ifdoc.body.innerHTML = "";
|
|
|
|
body_contents(ifdoc.body);
|
|
|
|
}
|
2008-03-05 16:06:15 -08:00
|
|
|
style_text.data = selector + "{ z-index: " + zi + " }";
|
|
|
|
var should_match = match_fn(ifdoc);
|
|
|
|
var should_not_match = notmatch_fn(ifdoc);
|
|
|
|
if (should_match.length + should_not_match.length == 0) {
|
|
|
|
ok(false, "nothing to check");
|
|
|
|
}
|
2008-03-09 18:10:03 -07:00
|
|
|
|
2008-03-05 16:06:15 -08:00
|
|
|
for (var i = 0; i < should_match.length; ++i) {
|
|
|
|
var e = should_match[i];
|
|
|
|
is(ifwin.getComputedStyle(e, "").zIndex, zi,
|
|
|
|
"element in " + body_contents + " matched " + selector);
|
|
|
|
}
|
|
|
|
for (var i = 0; i < should_not_match.length; ++i) {
|
|
|
|
var e = should_not_match[i];
|
|
|
|
is(ifwin.getComputedStyle(e, "").zIndex, "auto",
|
|
|
|
"element in " + body_contents + " did not match " + selector);
|
|
|
|
}
|
2008-03-09 18:10:03 -07:00
|
|
|
|
|
|
|
// Now, since we're here, may as well make sure serialization
|
|
|
|
// works correctly. It need not produce the exact same text,
|
|
|
|
// but it should produce a selector that matches the same
|
|
|
|
// elements.
|
|
|
|
zi = ++gCounter;
|
|
|
|
var ser1 = style_text.parentNode.sheet.cssRules[0].selectorText;
|
|
|
|
style_text.data = ser1 + "{ z-index: " + zi + " }";
|
|
|
|
for (var i = 0; i < should_match.length; ++i) {
|
|
|
|
var e = should_match[i];
|
|
|
|
is(ifwin.getComputedStyle(e, "").zIndex, zi,
|
|
|
|
"element in " + body_contents + " matched " + ser1 +
|
|
|
|
" which is the reserialization of " + selector);
|
|
|
|
}
|
|
|
|
for (var i = 0; i < should_not_match.length; ++i) {
|
|
|
|
var e = should_not_match[i];
|
|
|
|
is(ifwin.getComputedStyle(e, "").zIndex, "auto",
|
|
|
|
"element in " + body_contents + " did not match " + ser1 +
|
|
|
|
" which is the reserialization of " + selector);
|
|
|
|
}
|
|
|
|
|
|
|
|
// But when we serialize the serialized result, we should get
|
|
|
|
// the same text.
|
|
|
|
var ser2 = style_text.parentNode.sheet.cssRules[0].selectorText;
|
|
|
|
is(ser2, ser1, "parse+serialize of selector \"" + selector +
|
|
|
|
"\" is idempotent");
|
|
|
|
|
2008-03-05 16:06:15 -08:00
|
|
|
ifdoc.body.innerHTML = "";
|
|
|
|
style_text.data = "";
|
|
|
|
}
|
|
|
|
|
2008-06-02 20:17:35 -07:00
|
|
|
function test_parseable(selector)
|
|
|
|
{
|
|
|
|
var zi = ++gCounter;
|
|
|
|
ifdoc.body.innerHTML = "<p></p>";
|
|
|
|
style_text.data = "p, " + selector + "{ z-index: " + zi + " }";
|
|
|
|
var should_match = ifdoc.getElementsByTagName("p")[0];
|
|
|
|
is(ifwin.getComputedStyle(should_match, "").zIndex, zi,
|
|
|
|
"selector " + selector + " was parsed");
|
|
|
|
ifdoc.body.innerHTML = "";
|
|
|
|
style_text.data = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_balanced_unparseable(selector)
|
|
|
|
{
|
|
|
|
var zi1 = ++gCounter;
|
|
|
|
var zi2 = ++gCounter;
|
|
|
|
ifdoc.body.innerHTML = "<p></p><div></div>";
|
|
|
|
style_text.data = "p, " + selector + "{ z-index: " + zi1 + " }" +
|
|
|
|
"div { z-index: " + zi2 + " }";
|
|
|
|
var should_not_match = ifdoc.getElementsByTagName("p")[0];
|
|
|
|
var should_match = ifdoc.getElementsByTagName("div")[0];
|
|
|
|
is(ifwin.getComputedStyle(should_not_match, "").zIndex, "auto",
|
|
|
|
"selector " + selector + " was a parser error");
|
|
|
|
is(ifwin.getComputedStyle(should_match, "").zIndex, zi2,
|
|
|
|
"selector " + selector + " error was recovered from");
|
|
|
|
ifdoc.body.innerHTML = "";
|
|
|
|
style_text.data = "";
|
|
|
|
}
|
|
|
|
|
2008-03-05 16:06:15 -08:00
|
|
|
// Bug 420814
|
|
|
|
test_selector_in_html(
|
|
|
|
"div ~ div p",
|
|
|
|
"<div></div><div><div><p>match</p></div></div>",
|
|
|
|
function(doc) { return doc.getElementsByTagName("p"); },
|
|
|
|
function(doc) { return []; }
|
|
|
|
);
|
|
|
|
|
2008-06-02 20:17:35 -07:00
|
|
|
// :nth-child(), etc.
|
|
|
|
// Follow the whitespace rules as proposed in
|
|
|
|
// http://lists.w3.org/Archives/Public/www-style/2008Mar/0121.html
|
|
|
|
test_balanced_unparseable(":nth-child()");
|
|
|
|
test_balanced_unparseable(":nth-of-type( )");
|
|
|
|
test_parseable(":nth-last-child( odd)");
|
|
|
|
test_parseable(":nth-last-of-type(even )");
|
|
|
|
test_parseable(":nth-child(n )");
|
|
|
|
test_parseable(":nth-of-type( 2n)");
|
|
|
|
test_parseable(":nth-last-child( -n)");
|
|
|
|
test_parseable(":nth-last-of-type(-2n )");
|
|
|
|
test_balanced_unparseable(":nth-child(- n)");
|
|
|
|
test_balanced_unparseable(":nth-of-type(-2 n)");
|
|
|
|
test_balanced_unparseable(":nth-last-of-type(2n1)");
|
|
|
|
test_balanced_unparseable(":nth-child(2n++1)");
|
|
|
|
test_balanced_unparseable(":nth-of-type(2n-+1)");
|
|
|
|
test_balanced_unparseable(":nth-last-child(2n+-1)");
|
|
|
|
test_balanced_unparseable(":nth-last-of-type(2n--1)");
|
|
|
|
test_parseable(":nth-child( 3n + 1 )");
|
|
|
|
test_parseable(":nth-child( +3n - 2 )");
|
|
|
|
test_parseable(":nth-child( -n+ 6)");
|
|
|
|
test_parseable(":nth-child( +6 )");
|
|
|
|
test_balanced_unparseable(":nth-child(3 n)");
|
|
|
|
test_balanced_unparseable(":nth-child(+ 2n)");
|
|
|
|
test_balanced_unparseable(":nth-child(+ 2)");
|
|
|
|
test_parseable(":nth-child(3)");
|
|
|
|
test_parseable(":nth-of-type(-3)");
|
|
|
|
test_parseable(":nth-last-child(+3)");
|
|
|
|
test_parseable(":nth-last-of-type(0)");
|
|
|
|
test_parseable(":nth-child(-0)");
|
|
|
|
test_parseable(":nth-of-type(3n)");
|
|
|
|
test_parseable(":nth-last-child(-3n)");
|
|
|
|
test_parseable(":nth-last-of-type(+3n)");
|
|
|
|
test_parseable(":nth-last-of-type(0n)");
|
|
|
|
test_parseable(":nth-child(-0n)");
|
|
|
|
test_parseable(":nth-of-type(n)");
|
|
|
|
test_parseable(":nth-last-child(-n)");
|
|
|
|
test_parseable(":nth-last-of-type(2n+1)");
|
|
|
|
test_parseable(":nth-child(2n-1)");
|
|
|
|
test_parseable(":nth-of-type(2n+0)");
|
|
|
|
test_parseable(":nth-last-child(2n-0)");
|
|
|
|
test_parseable(":nth-child(-0n+0)");
|
|
|
|
test_parseable(":nth-of-type(n+1)");
|
|
|
|
test_parseable(":nth-last-child(n-1)");
|
|
|
|
test_parseable(":nth-last-of-type(-n+1)");
|
|
|
|
test_parseable(":nth-child(-n-1)");
|
|
|
|
test_balanced_unparseable(":nth-child(2-n)");
|
|
|
|
test_balanced_unparseable(":nth-child(2-n-1)");
|
|
|
|
test_balanced_unparseable(":nth-child(n-2-1)");
|
|
|
|
|
|
|
|
// exercise the an+b matching logic particularly hard for
|
|
|
|
// :nth-child() (since we know we use the same code for all 4)
|
|
|
|
var seven_ps = "<p></p><p></p><p></p><p></p><p></p><p></p><p></p>";
|
|
|
|
function pset(indices) { // takes an array of 1-based indices
|
|
|
|
return function pset_filter(doc) {
|
|
|
|
var a = doc.getElementsByTagName("p");
|
|
|
|
var result = [];
|
|
|
|
for (var i in indices)
|
|
|
|
result.push(a[indices[i] - 1]);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
test_selector_in_html(":nth-child(0)", seven_ps,
|
|
|
|
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(-3)", seven_ps,
|
|
|
|
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(3)", seven_ps,
|
|
|
|
pset([3]), pset([1, 2, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(0n+3)", seven_ps,
|
|
|
|
pset([3]), pset([1, 2, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(-0n+3)", seven_ps,
|
|
|
|
pset([3]), pset([1, 2, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(8)", seven_ps,
|
|
|
|
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(odd)", seven_ps,
|
|
|
|
pset([1, 3, 5, 7]), pset([2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child(even)", seven_ps,
|
|
|
|
pset([2, 4, 6]), pset([1, 3, 5, 7]));
|
|
|
|
test_selector_in_html(":nth-child(2n-1)", seven_ps,
|
|
|
|
pset([1, 3, 5, 7]), pset([2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child( 2n - 1 )", seven_ps,
|
|
|
|
pset([1, 3, 5, 7]), pset([2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child(2n+1)", seven_ps,
|
|
|
|
pset([1, 3, 5, 7]), pset([2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child( 2n + 1 )", seven_ps,
|
|
|
|
pset([1, 3, 5, 7]), pset([2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child(2n+0)", seven_ps,
|
|
|
|
pset([2, 4, 6]), pset([1, 3, 5, 7]));
|
|
|
|
test_selector_in_html(":nth-child(2n-0)", seven_ps,
|
|
|
|
pset([2, 4, 6]), pset([1, 3, 5, 7]));
|
|
|
|
test_selector_in_html(":nth-child(-n+3)", seven_ps,
|
|
|
|
pset([1, 2, 3]), pset([4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(-n-3)", seven_ps,
|
|
|
|
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(n)", seven_ps,
|
|
|
|
pset([1, 2, 3, 4, 5, 6, 7]), pset([]));
|
|
|
|
test_selector_in_html(":nth-child(n-3)", seven_ps,
|
|
|
|
pset([1, 2, 3, 4, 5, 6, 7]), pset([]));
|
|
|
|
test_selector_in_html(":nth-child(n+3)", seven_ps,
|
|
|
|
pset([3, 4, 5, 6, 7]), pset([1, 2]));
|
|
|
|
test_selector_in_html(":nth-child(2n+3)", seven_ps,
|
|
|
|
pset([3, 5, 7]), pset([1, 2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child(2n)", seven_ps,
|
|
|
|
pset([2, 4, 6]), pset([1, 3, 5, 7]));
|
|
|
|
test_selector_in_html(":nth-child(2n-3)", seven_ps,
|
|
|
|
pset([1, 3, 5, 7]), pset([2, 4, 6]));
|
|
|
|
test_selector_in_html(":nth-child(-1n+3)", seven_ps,
|
|
|
|
pset([1, 2, 3]), pset([4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-child(-2n+3)", seven_ps,
|
|
|
|
pset([1, 3]), pset([2, 4, 5, 6, 7]));
|
|
|
|
// And a few spot-checks for the other :nth-* selectors
|
|
|
|
test_selector_in_html(":nth-child(4n+1)", seven_ps,
|
|
|
|
pset([1, 5]), pset([2, 3, 4, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-last-child(4n+1)", seven_ps,
|
|
|
|
pset([3, 7]), pset([1, 2, 4, 5, 6]));
|
|
|
|
test_selector_in_html(":nth-of-type(4n+1)", seven_ps,
|
|
|
|
pset([1, 5]), pset([2, 3, 4, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-last-of-type(4n+1)", seven_ps,
|
|
|
|
pset([3, 7]), pset([1, 2, 4, 5, 6]));
|
|
|
|
test_selector_in_html(":nth-child(6)", seven_ps,
|
|
|
|
pset([6]), pset([1, 2, 3, 4, 5, 7]));
|
|
|
|
test_selector_in_html(":nth-last-child(6)", seven_ps,
|
|
|
|
pset([2]), pset([1, 3, 4, 5, 6, 7]));
|
|
|
|
test_selector_in_html(":nth-of-type(6)", seven_ps,
|
|
|
|
pset([6]), pset([1, 2, 3, 4, 5, 7]));
|
|
|
|
test_selector_in_html(":nth-last-of-type(6)", seven_ps,
|
|
|
|
pset([2]), pset([1, 3, 4, 5, 6, 7]));
|
|
|
|
|
|
|
|
// And a bunch of tests for the of-type aspect of :nth-of-type() and
|
|
|
|
// :nth-last-of-type(). Note that the last div here contains two
|
|
|
|
// children.
|
|
|
|
var mixed_elements="<p></p><p></p><div></div><p></p><div><p></p><address></address></div><address></address>";
|
|
|
|
function pdaset(ps, divs, addresses) { // takes an array of 1-based indices
|
|
|
|
var l = { p: ps, div: divs, address: addresses };
|
|
|
|
return function pdaset_filter(doc) {
|
|
|
|
var result = [];
|
|
|
|
for (var tag in l) {
|
|
|
|
var a = doc.getElementsByTagName(tag);
|
|
|
|
var indices = l[tag];
|
|
|
|
for (var i in indices)
|
|
|
|
result.push(a[indices[i] - 1]);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
test_selector_in_html(":nth-of-type(odd)", mixed_elements,
|
|
|
|
pdaset([1, 3, 4], [1], [1, 2]),
|
|
|
|
pdaset([2], [2], []));
|
|
|
|
test_selector_in_html(":nth-of-type(2n-0)", mixed_elements,
|
|
|
|
pdaset([2], [2], []),
|
|
|
|
pdaset([1, 3, 4], [1], [1, 2]));
|
|
|
|
test_selector_in_html(":nth-last-of-type(even)", mixed_elements,
|
|
|
|
pdaset([2], [1], []),
|
|
|
|
pdaset([1, 3, 4], [2], [1, 2]));
|
2008-03-05 16:06:15 -08:00
|
|
|
|
|
|
|
SimpleTest.finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</pre>
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
|