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);
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
* namespaces (optional): @namespace rules to be included in the sheet
*/
function test_selector_in_html(selector, body_contents, match_fn, notmatch_fn, namespaces)
{ var zi = ++gCounter;
if (typeof(body_contents) == "string") {
ifdoc.body.innerHTML = body_contents;
} else {
// It's a function.
ifdoc.body.innerHTML = "";
body_contents(ifdoc.body);
}
if (!namespaces) {
namespaces = "";
}
style_text.data = namespaces + selector + "{ z-index: " + zi + " }";
var idx = style_text.parentNode.sheet.cssRules.length - 1;
if (namespaces == "") {
is(idx, 0, "unexpected rule index for " + selector);
}
if (idx < 0 ||
style_text.parentNode.sheet.cssRules[idx].type !=
CSSRule.STYLE_RULE)
{
ok(false, "selector " + selector + " could not be parsed");
return;
}
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");
}
for (let i = 0; i < should_match.length; ++i) {
let e = should_match[i];
is(ifwin.getComputedStyle(e).zIndex, String(zi), "element in " + body_contents + " matched " + selector);
}
for (let i = 0; i < should_not_match.length; ++i) {
let e = should_not_match[i];
is(ifwin.getComputedStyle(e).zIndex, "auto", "element in " + body_contents + " did not match " + selector);
}
// 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[idx].selectorText;
style_text.data = namespaces + ser1 + "{ z-index: " + zi + " }";
for (let i = 0; i < should_match.length; ++i) {
let e = should_match[i];
is(ifwin.getComputedStyle(e).zIndex, String(zi), "element in " + body_contents + " matched " + ser1 + " which is the reserialization of " + selector);
}
for (let i = 0; i < should_not_match.length; ++i) {
let 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.
isnot(style_text.parentNode.sheet.cssRules[idx], undefined, "parse of selector \"" + ser1 + "\" failed");
if (style_text.parentNode.sheet.cssRules[idx] !== undefined) { var ser2 = style_text.parentNode.sheet.cssRules[idx].selectorText;
is(ser2, ser1, "parse+serialize of selector \"" + selector + "\" is idempotent");
}
ifdoc.body.innerHTML = "";
style_text.data = "";
// And now test that when we clone the style sheet, we end up
// with the same selector (serializes to same string, and
// matches the same things).
zi = ++gCounter; var style_sheet = "data:text/css," +
escape(namespaces + selector + "{ z-index: " + zi + " }"); var style_sheet_link = ""'>"; var html_doc = "" +
style_sheet_link + style_sheet_link + "";
if (typeof(body_contents) == "string") {
html_doc += body_contents;
} var docurl = "data:text/html," + escape(html_doc);
defer_clonedoc_tests(docurl, function() { var wrappedCloneFrame = SpecialPowers.wrap(cloneiframe); var clonedoc = wrappedCloneFrame.contentDocument; var clonewin = wrappedCloneFrame.contentWindow;
if (typeof(body_contents) != "string") {
body_contents(clonedoc.body);
}
var links = clonedoc.getElementsByTagName("link");
// cause a clone
links[1].sheet.insertRule("#nonexistent { color: purple}", idx + 1);
// remove the uncloned sheet
links[0].remove();
var should_match1 = match_fn(clonedoc); var should_not_match1 = notmatch_fn(clonedoc);
if (should_match1.length + should_not_match1.length == 0) {
ok(false, "nothing to check");
}
for (let i = 0; i < should_match1.length; ++i) {
let e = should_match1[i];
is(clonewin.getComputedStyle(e).zIndex, String(zi), "element in " + body_contents + " matched clone of " +
selector);
}
for (let i = 0; i < should_not_match1.length; ++i) {
let e = should_not_match1[i];
is(clonewin.getComputedStyle(e).zIndex, "auto", "element in " + body_contents + " did not match clone of " +
selector);
}
var ser3 = links[0].sheet.cssRules[idx].selectorText;
is(ser3, ser1, "selector " + selector + " serializes correctly after cloning");
});
}
function should_serialize_to(selector, serialization)
{
style_text.data = selector + "{ z-index: 0 }";
is(style_text.parentNode.sheet.cssRules[0].selectorText,
serialization, "selector '" + selector + "' should serialize to '" +
serialization + "'.");
}
function test_parseable(selector)
{
ifdoc.body.innerHTML = "";
var zi = ++gCounter;
style_text.data = "p, " + selector + "{ z-index: " + zi + " }"; var should_match = ifdoc.getElementsByTagName("p")[0]; var parsed = ifwin.getComputedStyle(should_match).zIndex == zi;
ok(parsed, "selector " + selector + " was parsed");
if (!parsed) {
return;
}
// Test that it serializes to something that is also parseable. var ser1 = style_elem.sheet.cssRules[0].selectorText;
zi = ++gCounter;
style_text.data = ser1 + "{ z-index: " + zi + " }";
is(ifwin.getComputedStyle(should_match).zIndex, String(zi), "serialization " + ser1 + " of selector p, " + selector + " was parsed"); var ser2 = style_elem.sheet.cssRules[0].selectorText;
is(ser2, ser1, "parse+serialize of selector " + selector + " is idempotent");
ifdoc.body.innerHTML = "";
style_text.data = "";
// Test that it clones to the same thing it serializes to.
zi = ++gCounter; var style_sheet = "data:text/css," +
escape("p, " + selector + "{ z-index: " + zi + " }"); var style_sheet_link = ""'>"; var html_doc = "" +
style_sheet_link + style_sheet_link + ""; var docurl = "data:text/html," + escape(html_doc);
defer_clonedoc_tests(docurl, function() { var wrappedCloneFrame = SpecialPowers.wrap(cloneiframe); var clonedoc = wrappedCloneFrame.contentDocument; var clonewin = wrappedCloneFrame.contentWindow; var links = clonedoc.getElementsByTagName("link");
// cause a clone
links[1].sheet.insertRule("#nonexistent { color: purple}", 0);
// remove the uncloned sheet
links[0].remove();
function test_unparseable_via_api(selector)
{
try {
// Test that it is also unparseable when followed by EOF.
ifdoc.body.matches(selector);
ok(false, "selector '" + selector + "' plus EOF is parse error");
} catch(ex) {
is(ex.name, "SyntaxError", "selector '" + selector + "' plus EOF is parse error");
is(ex.code, DOMException.SYNTAX_ERR, "selector '" + selector + "' plus EOF is parse error");
}
}
function test_parseable_via_api(selector)
{ var threw = false;
try {
// Test that a selector is parseable when followed by EOF.
ifdoc.body.matches(selector);
} catch(ex) {
threw = true;
}
ok(!threw, "selector '" + selector + "' was parsed");
}
function test_balanced_unparseable(selector)
{ var zi1 = ++gCounter; var zi2 = ++gCounter;
ifdoc.body.innerHTML = "";
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, String(zi2), "selector " + selector + " error was recovered from");
ifdoc.body.innerHTML = "";
style_text.data = "";
test_unparseable_via_api(selector);
}
function test_unbalanced_unparseable(selector)
{ var zi1 = ++gCounter; var zi2 = ++gCounter;
ifdoc.body.innerHTML = "";
style_text.data = "p, " + selector + "{ z-index: " + zi1 + " }"; var should_not_match = ifdoc.getElementsByTagName("p")[0];
is(ifwin.getComputedStyle(should_not_match).zIndex, "auto", "selector " + selector + " was a parser error");
is(style_text.parentNode.sheet.cssRules.length, 0, "sheet should have no rules since " + selector + " is parse error");
ifdoc.body.innerHTML = "";
style_text.data = "";
test_unparseable_via_api(selector);
}
// Whether we should drop the bar is debatable. This matches Edge
// and Safari at the time of writing.
should_serialize_to("[|attr]", "[attr]");
should_serialize_to("[|ATTR]", "[ATTR]");
// Test [first|last|only]-[child|node|of-type] var interesting_doc = "
x
x
";
function idset(ids) { // takes an array of ids
return function idset_filter(doc) { var result = [];
for (var id of ids)
result.push(doc.getElementById(id));
return result;
}
}
function classset(classes) { // takes an array of classes
return function classset_filter(doc) { var i, j, els; var result = [];
for (i = 0; i < classes.length; i++) {
els = doc.getElementsByClassName(classes[i]);
for (j = 0; j < els.length; j++) {
result.push(els[j]);
}
}
return result;
}
}
function emptyset(doc) { return []; }
test_parseable(":first-child");
test_parseable(":last-child");
test_parseable(":only-child");
test_parseable(":-moz-first-node");
test_parseable(":-moz-last-node");
test_parseable(":first-of-type");
test_parseable(":last-of-type");
test_parseable(":only-of-type");
test_selector_in_html(":first-child", seven_ps,
pset([1]), pset([2, 3, 4, 5, 6, 7]));
test_selector_in_html(":first-child", interesting_doc,
idset(["p1", "s1", "s3", "s5"]),
idset(["s2", "p2", "s4", "p3"]));
test_selector_in_html(":-moz-first-node", interesting_doc,
idset(["p1", "s3", "s5"]),
idset(["s1", "s2", "p2", "s4", "p3"]));
test_selector_in_html(":last-child", seven_ps,
pset([7]), pset([1, 2, 3, 4, 5, 6]));
test_selector_in_html(":last-child", interesting_doc,
idset(["s2", "s4", "p3", "s5"]),
idset(["p1", "s1", "p2", "s3"]));
test_selector_in_html(":-moz-last-node", interesting_doc,
idset(["s2", "p3", "s5"]),
idset(["p1", "s1", "p2", "s3", "s4"]));
test_selector_in_html(":only-child", seven_ps,
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
test_selector_in_html(":only-child", interesting_doc,
idset(["s5"]),
idset(["p1", "s1", "s2", "p2", "s3", "s4", "p3"]));
test_selector_in_html(":first-of-type", seven_ps,
pset([1]), pset([2, 3, 4, 5, 6, 7]));
test_selector_in_html(":first-of-type", interesting_doc,
idset(["p1", "s1", "p2", "s3", "s5"]),
idset(["s2", "s4", "p3"]));
test_selector_in_html(":last-of-type", seven_ps,
pset([7]), pset([1, 2, 3, 4, 5, 6]));
test_selector_in_html(":last-of-type", interesting_doc,
idset(["s2", "p2", "s4", "p3", "s5"]),
idset(["p1", "s1", "s3"]));
test_selector_in_html(":only-of-type", seven_ps,
pset([]), pset([1, 2, 3, 4, 5, 6, 7]));
test_selector_in_html(":only-of-type", interesting_doc,
idset(["p2", "s5"]),
idset(["p1", "s1", "s2", "s3", "s4", "p3"]));
// 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="
";
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]));
// Test greediness of descendant combinators. var four_children="
// We're not in a UA sheet, so this should be invalid.
test_balanced_unparseable(":-moz-inert");
test_balanced_unparseable(":-moz-native-anonymous");
test_balanced_unparseable(":-moz-table-border-nonzero");
// Case sensitivity of tag selectors
function setup_cased_spans(body) { var data = [
{ tag: "span" },
{ tag: "sPaN" },
{ tag: "Span" },
{ tag: "SPAN" },
{ ns: "http://www.w3.org/1999/xhtml", tag: "span" },
{ ns: "http://www.w3.org/1999/xhtml", tag: "sPaN" },
{ ns: "http://www.w3.org/1999/xhtml", tag: "Span" },
{ ns: "http://www.w3.org/1999/xhtml", tag: "SPAN" },
{ ns: "http://example.com/useless", tag: "span" },
{ ns: "http://example.com/useless", tag: "sPaN" },
{ ns: "http://example.com/useless", tag: "Span" },
{ ns: "http://example.com/useless", tag: "SPAN" },
]
for (var i in data) { var ent = data[i]; var elem;
if ("ns" in ent) {
elem = body.ownerDocument.createElementNS(ent.ns, ent.tag);
} else {
elem = body.ownerDocument.createElement(ent.tag);
} body.appendChild(elem);
}
}
function bodychildset(indices) {
return function bodychildset_filter(doc) { varbody = doc.body; var result = [];
for (var i in indices) {
result.push(body.childNodes[indices[i]]);
}
return result;
}
}
test_selector_in_html("span", setup_cased_spans,
bodychildset([0, 1, 2, 3, 4, 8]),
bodychildset([5, 6, 7, 9, 10, 11]));
test_selector_in_html("sPaN", setup_cased_spans,
bodychildset([0, 1, 2, 3, 4, 9]),
bodychildset([5, 6, 7, 8, 10, 11]));
test_selector_in_html("Span", setup_cased_spans,
bodychildset([0, 1, 2, 3, 4, 10]),
bodychildset([5, 6, 7, 8, 9, 11]));
test_selector_in_html("SPAN", setup_cased_spans,
bodychildset([0, 1, 2, 3, 4, 11]),
bodychildset([5, 6, 7, 8, 9, 10]));
// bug 528096 (tree pseudos)
test_unbalanced_unparseable(":-moz-tree-column((){} a");
test_unbalanced_unparseable(":-moz-tree-column(x(){} a");
test_unbalanced_unparseable(":-moz-tree-column(a b (){} a");
test_unbalanced_unparseable(":-moz-tree-column(a, b (){} a");
// Test that we don't tokenize an empty HASH.
test_balanced_unparseable("#");
test_balanced_unparseable("# ");
test_balanced_unparseable("#, p");
test_balanced_unparseable("# , p");
test_balanced_unparseable("p #");
test_balanced_unparseable("p # ");
test_balanced_unparseable("p #, p");
test_balanced_unparseable("p # , p");
// Test that a backslash alone at EOF outside of a string is treated
// as U+FFFD.
test_parseable_via_api("#a\\");
test_parseable_via_api("#\\");
test_parseable_via_api("\\");
// Test that newline escapes are only supported in strings.
test_balanced_unparseable("di\\\nv");
test_balanced_unparseable("div \\\n p");
test_balanced_unparseable("div\\\n p");
test_balanced_unparseable("div \\\np");
test_balanced_unparseable("div\\\np");
// Test that :-moz-placeholder is parsable.
test_parseable(":-moz-placeholder");
// Test that things other than user-action pseudo-classes are
// rejected after pseudo-elements. Some of these tests rely on
// using a pseudo-element that supports a user-action pseudo-class
// after it, so we need to use the prefixed ::-moz-color-swatch,
// which is one of the ones with
// CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE (none of which are
// unprefixed).
test_parseable("::-moz-color-swatch:hover");
test_parseable("::-moz-color-swatch:is(:hover)");
test_parseable("::-moz-color-swatch:not(:hover)");
test_parseable("::-moz-color-swatch:where(:hover)");
test_balanced_unparseable("::-moz-color-swatch:not(.foo)");
test_balanced_unparseable("::-moz-color-swatch:first-child");
test_balanced_unparseable("::-moz-color-swatch:host");
test_balanced_unparseable("::-moz-color-swatch:host(div)");
test_balanced_unparseable("::-moz-color-swatch:nth-child(1)");
test_balanced_unparseable("::-moz-color-swatch:hover#foo");
test_balanced_unparseable(".foo::after:not(.bar) ~ h3");
for (let selector of [ "::-moz-color-swatch:where(.foo)", "::-moz-color-swatch:is(.foo)", "::-moz-color-swatch:where(p, :hover)", "::-moz-color-swatch:is(p, :hover)",
]) {
test_parseable(selector);
should_serialize_to(selector, selector);
ok(!CSS.supports(`selector(${selector})`), "supports should report false for forging selector parse failure");
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.47Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.