//////////////////////////////////////////////////////////////////////////////// // 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 serializer = new XMLSerializer(); 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"}); // If @recreated attribute is used then this attribute change recreates an // accessible. Wait for reorder event in this case or otherwise proceed next // test immediately. if (aRule.hasAttribute("recreated")) { waitForEvent(EVENT_REORDER, aElm.parentNode, gTestIterator.iterateNext, gTestIterator); aElm.removeAttribute(attr); } else if (aRule.hasAttribute("textchanged")) { waitForEvent(EVENT_TEXT_INSERTED, aElm, gTestIterator.iterateNext, gTestIterator); aElm.removeAttribute(attr); } else if (aRule.hasAttribute("contentchanged")) { waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator); aElm.removeAttribute(attr); } else { aElm.removeAttribute(attr); gTestIterator.iterateNext(); } } 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 reorder event on " + prettyName(parentNode) + "\n"); } waitForEvent(EVENT_REORDER, parentNode, gTestIterator.iterateNext, gTestIterator); parentNode.removeChild(labelElm); } function testNameForSubtreeRule(aElm, aRule) { 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 reorder event on " + prettyName(aElm) + "\n"); } waitForEvent(EVENT_REORDER, aElm, gTestIterator.iterateNext, gTestIterator); while (aElm.firstChild) aElm.removeChild(aElm.firstChild); } /** * 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; }