/* 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" }); } // 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.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; }