/* import-globals-from ../attributes.js */
/* import-globals-from ../common.js */
/* import-globals-from ../events.js */
/* import-globals-from ../name.js */
// //////////////////////////////////////////////////////////////////////////////
// Name tests described by "markuprules.xml" file.
var gNameRulesFileURL =
"markuprules.xml";
var gRuleDoc =
null;
// Debuggin stuff.
var gDumpToConsole =
false;
/**
* Start name tests. Run through markup elements and test names for test
* element (see namerules.xml for details).
*/
function testNames() {
// enableLogging("tree,stack"); // debugging
var request =
new XMLHttpRequest();
request.open(
"get", gNameRulesFileURL,
false);
request.send();
gRuleDoc = request.responseXML;
var markupElms = evaluateXPath(gRuleDoc,
"//rules/rulesample/markup");
gTestIterator.iterateMarkups(markupElms);
}
// //////////////////////////////////////////////////////////////////////////////
// Private section.
/**
* Helper class to interate through name tests.
*/
var gTestIterator = {
iterateMarkups:
function gTestIterator_iterateMarkups(aMarkupElms) {
this.markupElms = aMarkupElms;
this.iterateNext();
},
iterateRules:
function gTestIterator_iterateRules(
aElm,
aContainer,
aRuleSetElm,
aRuleElms,
aTestID
) {
this.ruleSetElm = aRuleSetElm;
this.ruleElms = aRuleElms;
this.elm = aElm;
this.container = aContainer;
this.testID = aTestID;
this.iterateNext();
},
iterateNext:
function gTestIterator_iterateNext() {
if (
this.markupIdx == -1) {
this.markupIdx++;
testNamesForMarkup(
this.markupElms[
this.markupIdx]);
return;
}
this.ruleIdx++;
if (
this.ruleIdx ==
this.ruleElms.length) {
// When test is finished then name is empty and no explict-name.
var defaultName =
this.ruleSetElm.hasAttribute(
"defaultName")
?
this.ruleSetElm.getAttribute(
"defaultName")
:
null;
testName(
this.elm,
defaultName,
"Default name test (" + gTestIterator.testID +
"). "
);
testAbsentAttrs(
this.elm, {
"explicit-name":
"true" });
this.markupIdx++;
if (
this.markupIdx ==
this.markupElms.length) {
// disableLogging("tree"); // debugging
SimpleTest.finish();
return;
}
this.ruleIdx = -1;
if (gDumpToConsole) {
dump(
"\nPend next markup processing. Wait for reorder event on " +
prettyName(document) +
"'\n"
);
}
waitForEvent(
EVENT_REORDER,
document,
testNamesForMarkup,
null,
this.markupElms[
this.markupIdx]
);
document.body.removeChild(
this.container);
return;
}
testNameForRule(
this.elm,
this.ruleElms[
this.ruleIdx]);
},
markupElms:
null,
markupIdx: -1,
rulesetElm:
null,
ruleElms:
null,
ruleIdx: -1,
elm:
null,
container:
null,
testID:
"",
};
/**
* Process every 'markup' element and test names for it. Used by testNames
* function.
*/
function testNamesForMarkup(aMarkupElm) {
if (gDumpToConsole) {
dump(
"\nProcessing markup '" + aMarkupElm.getAttribute(
"id") +
"'\n");
}
var div = document.createElement(
"div");
div.setAttribute(
"id",
"test");
var child = aMarkupElm.firstChild;
while (child) {
var newChild = document.importNode(child,
true);
div.appendChild(newChild);
child = child.nextSibling;
}
if (gDumpToConsole) {
dump(
"\nProcessing markup. Wait for reorder event on " +
prettyName(document) +
"'\n"
);
}
waitForEvent(
EVENT_REORDER,
document,
testNamesForMarkupRules,
null,
aMarkupElm,
div
);
document.body.appendChild(div);
}
function testNamesForMarkupRules(aMarkupElm, aContainer) {
var testID = aMarkupElm.getAttribute(
"id");
if (gDumpToConsole) {
dump(
"\nProcessing markup rules '" + testID +
"'\n");
}
var expr =
"//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref");
var elm = evaluateXPath(document, expr, htmlDocResolver)[0];
var ruleId = aMarkupElm.getAttribute(
"ruleset");
var ruleElm = gRuleDoc.querySelector(
"[id='" + ruleId +
"']");
var ruleElms = getRuleElmsByRulesetId(ruleId);
var processMarkupRules = gTestIterator.iterateRules.bind(
gTestIterator,
elm,
aContainer,
ruleElm,
ruleElms,
testID
);
// Images may be recreated after we append them into subtree. We need to wait
// in this case. If we are on profiling enabled build then stack tracing
// works and thus let's log instead. Note, that works if you enabled logging
// (refer to testNames() function).
if (isAccessible(elm) || isLogged(
"stack")) {
processMarkupRules();
}
else {
waitForEvent(EVENT_SHOW, elm, processMarkupRules);
}
}
/**
* Test name for current rule and current 'markup' element. Used by
* testNamesForMarkup function.
*/
function testNameForRule(aElm, aRuleElm) {
if (aRuleElm.hasAttribute(
"attr")) {
if (gDumpToConsole) {
dump(
"\nProcessing rule { attr: " + aRuleElm.getAttribute(
"attr") +
" }\n"
);
}
testNameForAttrRule(aElm, aRuleElm);
}
else if (aRuleElm.hasAttribute(
"elm")) {
if (gDumpToConsole) {
dump(
"\nProcessing rule { elm: " +
aRuleElm.getAttribute(
"elm") +
", elmattr: " +
aRuleElm.getAttribute(
"elmattr") +
" }\n"
);
}
testNameForElmRule(aElm, aRuleElm);
}
else if (aRuleElm.getAttribute(
"fromsubtree") ==
"true") {
if (gDumpToConsole) {
dump(
"\nProcessing rule { fromsubtree: " +
aRuleElm.getAttribute(
"fromsubtree") +
" }\n"
);
}
testNameForSubtreeRule(aElm, aRuleElm);
}
}
function testNameForAttrRule(aElm, aRule) {
var name =
"";
var attr = aRule.getAttribute(
"attr");
var attrValue = aElm.getAttribute(attr);
var type = aRule.getAttribute(
"type");
if (type ==
"string") {
name = attrValue;
}
else if (type ==
"ref" && attrValue) {
var ids = attrValue.split(/\s+/);
for (
var idx = 0; idx < ids.length; idx++) {
var labelElm = getNode(ids[idx]);
if (name !=
"") {
name +=
" ";
}
name += labelElm.getAttribute(
"textequiv");
}
}
var msg =
"Attribute '" + attr +
"' test (" + gTestIterator.testID +
"). ";
testName(aElm, name, msg);
if (aRule.getAttribute(
"explict-name") !=
"false") {
testAttrs(aElm, {
"explicit-name":
"true" },
true);
}
else {
testAbsentAttrs(aElm, {
"explicit-name":
"true" });
}
waitForEvent(
EVENT_NAME_CHANGE,
aElm,
gTestIterator.iterateNext,
gTestIterator
);
aElm.removeAttribute(attr);
}
function testNameForElmRule(aElm, aRule) {
var labelElm;
var tagname = aRule.getAttribute(
"elm");
var attrname = aRule.getAttribute(
"elmattr");
if (attrname) {
var filter = {
acceptNode:
function filter_acceptNode(aNode) {
if (
aNode.localName ==
this.mLocalName &&
aNode.getAttribute(
this.mAttrName) ==
this.mAttrValue
) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_SKIP;
},
mLocalName: tagname,
mAttrName: attrname,
mAttrValue: aElm.getAttribute(
"id"),
};
var treeWalker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
filter
);
labelElm = treeWalker.nextNode();
}
else {
// if attrname is empty then look for the element in subtree.
labelElm = aElm.getElementsByTagName(tagname)[0];
if (!labelElm) {
labelElm = aElm.getElementsByTagName(
"html:" + tagname)[0];
}
}
if (!labelElm) {
ok(
false, msg +
" Failed to find '" + tagname +
"' element.");
gTestIterator.iterateNext();
return;
}
var msg =
"Element '" + tagname +
"' test (" + gTestIterator.testID +
").";
testName(aElm, labelElm.getAttribute(
"textequiv"), msg);
testAttrs(aElm, {
"explicit-name":
"true" },
true);
var parentNode = labelElm.parentNode;
if (gDumpToConsole) {
dump(
"\nProcessed elm rule. Wait for name change event on " +
prettyName(aElm) +
"\n"
);
}
waitForEvent(
EVENT_NAME_CHANGE,
aElm,
gTestIterator.iterateNext,
gTestIterator
);
parentNode.removeChild(labelElm);
}
function testNameForSubtreeRule(aElm) {
var msg =
"From subtree test (" + gTestIterator.testID +
").";
testName(aElm, aElm.getAttribute(
"textequiv"), msg);
testAbsentAttrs(aElm, {
"explicit-name":
"true" });
if (gDumpToConsole) {
dump(
"\nProcessed from subtree rule. Wait for name change event on " +
prettyName(aElm) +
"\n"
);
}
waitForEvent(
EVENT_NAME_CHANGE,
aElm,
gTestIterator.iterateNext,
gTestIterator
);
while (aElm.firstChild) {
aElm.firstChild.remove();
}
}
/**
* Return array of 'rule' elements. Used in conjunction with
* getRuleElmsFromRulesetElm() function.
*/
function getRuleElmsByRulesetId(aRulesetId) {
var expr =
"//rules/ruledfn/ruleset[@id='" + aRulesetId + "']";
var rulesetElm = evaluateXPath(gRuleDoc, expr);
return getRuleElmsFromRulesetElm(rulesetElm[0]);
}
function getRuleElmsFromRulesetElm(aRulesetElm) {
var rulesetId = aRulesetElm.getAttribute(
"ref");
if (rulesetId) {
return getRuleElmsByRulesetId(rulesetId);
}
var ruleElms = [];
var child = aRulesetElm.firstChild;
while (child) {
if (child.localName ==
"ruleset") {
ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child));
}
if (child.localName ==
"rule") {
ruleElms.push(child);
}
child = child.nextSibling;
}
return ruleElms;
}
/**
* Helper method to evaluate xpath expression.
*/
function evaluateXPath(aNode, aExpr, aResolver) {
var xpe =
new XPathEvaluator();
var resolver = aResolver;
if (!resolver) {
var node =
aNode.ownerDocument ==
null
? aNode.documentElement
: aNode.ownerDocument.documentElement;
resolver = xpe.createNSResolver(node);
}
var result = xpe.evaluate(aExpr, aNode, resolver, 0,
null);
var found = [];
var res;
while ((res = result.iterateNext())) {
found.push(res);
}
return found;
}
function htmlDocResolver(aPrefix) {
var ns = {
html:
"http://www.w3.org/1999/xhtml",
};
return ns[aPrefix] ||
null;
}