markup.js (10392B)
1 /* import-globals-from ../attributes.js */ 2 /* import-globals-from ../common.js */ 3 /* import-globals-from ../events.js */ 4 /* import-globals-from ../name.js */ 5 6 // ////////////////////////////////////////////////////////////////////////////// 7 // Name tests described by "markuprules.xml" file. 8 9 var gNameRulesFileURL = "markuprules.xml"; 10 11 var gRuleDoc = null; 12 13 // Debuggin stuff. 14 var gDumpToConsole = false; 15 16 /** 17 * Start name tests. Run through markup elements and test names for test 18 * element (see namerules.xml for details). 19 */ 20 function testNames() { 21 // enableLogging("tree,stack"); // debugging 22 23 var request = new XMLHttpRequest(); 24 request.open("get", gNameRulesFileURL, false); 25 request.send(); 26 27 gRuleDoc = request.responseXML; 28 29 var markupElms = evaluateXPath(gRuleDoc, "//rules/rulesample/markup"); 30 gTestIterator.iterateMarkups(markupElms); 31 } 32 33 // ////////////////////////////////////////////////////////////////////////////// 34 // Private section. 35 36 /** 37 * Helper class to interate through name tests. 38 */ 39 var gTestIterator = { 40 iterateMarkups: function gTestIterator_iterateMarkups(aMarkupElms) { 41 this.markupElms = aMarkupElms; 42 43 this.iterateNext(); 44 }, 45 46 iterateRules: function gTestIterator_iterateRules( 47 aElm, 48 aContainer, 49 aRuleSetElm, 50 aRuleElms, 51 aTestID 52 ) { 53 this.ruleSetElm = aRuleSetElm; 54 this.ruleElms = aRuleElms; 55 this.elm = aElm; 56 this.container = aContainer; 57 this.testID = aTestID; 58 59 this.iterateNext(); 60 }, 61 62 iterateNext: function gTestIterator_iterateNext() { 63 if (this.markupIdx == -1) { 64 this.markupIdx++; 65 testNamesForMarkup(this.markupElms[this.markupIdx]); 66 return; 67 } 68 69 this.ruleIdx++; 70 if (this.ruleIdx == this.ruleElms.length) { 71 // When test is finished then name is empty and no explicit-name. 72 var defaultName = this.ruleSetElm.hasAttribute("defaultName") 73 ? this.ruleSetElm.getAttribute("defaultName") 74 : null; 75 testName( 76 this.elm, 77 defaultName, 78 "Default name test (" + gTestIterator.testID + "). " 79 ); 80 testAbsentAttrs(this.elm, { "explicit-name": "true" }); 81 82 this.markupIdx++; 83 if (this.markupIdx == this.markupElms.length) { 84 // disableLogging("tree"); // debugging 85 SimpleTest.finish(); 86 return; 87 } 88 89 this.ruleIdx = -1; 90 91 if (gDumpToConsole) { 92 dump( 93 "\nPend next markup processing. Wait for reorder event on " + 94 prettyName(document) + 95 "'\n" 96 ); 97 } 98 waitForEvent( 99 EVENT_REORDER, 100 document, 101 testNamesForMarkup, 102 null, 103 this.markupElms[this.markupIdx] 104 ); 105 106 document.body.removeChild(this.container); 107 return; 108 } 109 110 testNameForRule(this.elm, this.ruleElms[this.ruleIdx]); 111 }, 112 113 markupElms: null, 114 markupIdx: -1, 115 rulesetElm: null, 116 ruleElms: null, 117 ruleIdx: -1, 118 elm: null, 119 container: null, 120 testID: "", 121 }; 122 123 /** 124 * Process every 'markup' element and test names for it. Used by testNames 125 * function. 126 */ 127 function testNamesForMarkup(aMarkupElm) { 128 if (gDumpToConsole) { 129 dump("\nProcessing markup '" + aMarkupElm.getAttribute("id") + "'\n"); 130 } 131 132 var div = document.createElement("div"); 133 div.setAttribute("id", "test"); 134 135 var child = aMarkupElm.firstChild; 136 while (child) { 137 var newChild = document.importNode(child, true); 138 div.appendChild(newChild); 139 child = child.nextSibling; 140 } 141 142 if (gDumpToConsole) { 143 dump( 144 "\nProcessing markup. Wait for reorder event on " + 145 prettyName(document) + 146 "'\n" 147 ); 148 } 149 waitForEvent( 150 EVENT_REORDER, 151 document, 152 testNamesForMarkupRules, 153 null, 154 aMarkupElm, 155 div 156 ); 157 158 document.body.appendChild(div); 159 } 160 161 function testNamesForMarkupRules(aMarkupElm, aContainer) { 162 var testID = aMarkupElm.getAttribute("id"); 163 if (gDumpToConsole) { 164 dump("\nProcessing markup rules '" + testID + "'\n"); 165 } 166 167 var expr = "//html/body/div[@id='test']/" + aMarkupElm.getAttribute("ref"); 168 var elm = evaluateXPath(document, expr, htmlDocResolver)[0]; 169 170 var ruleId = aMarkupElm.getAttribute("ruleset"); 171 var ruleElm = gRuleDoc.querySelector("[id='" + ruleId + "']"); 172 var ruleElms = getRuleElmsByRulesetId(ruleId); 173 174 var processMarkupRules = gTestIterator.iterateRules.bind( 175 gTestIterator, 176 elm, 177 aContainer, 178 ruleElm, 179 ruleElms, 180 testID 181 ); 182 183 // Images may be recreated after we append them into subtree. We need to wait 184 // in this case. If we are on profiling enabled build then stack tracing 185 // works and thus let's log instead. Note, that works if you enabled logging 186 // (refer to testNames() function). 187 if (isAccessible(elm) || isLogged("stack")) { 188 processMarkupRules(); 189 } else { 190 waitForEvent(EVENT_SHOW, elm, processMarkupRules); 191 } 192 } 193 194 /** 195 * Test name for current rule and current 'markup' element. Used by 196 * testNamesForMarkup function. 197 */ 198 function testNameForRule(aElm, aRuleElm) { 199 if (aRuleElm.hasAttribute("attr")) { 200 if (gDumpToConsole) { 201 dump( 202 "\nProcessing rule { attr: " + aRuleElm.getAttribute("attr") + " }\n" 203 ); 204 } 205 206 testNameForAttrRule(aElm, aRuleElm); 207 } else if (aRuleElm.hasAttribute("elm")) { 208 if (gDumpToConsole) { 209 dump( 210 "\nProcessing rule { elm: " + 211 aRuleElm.getAttribute("elm") + 212 ", elmattr: " + 213 aRuleElm.getAttribute("elmattr") + 214 " }\n" 215 ); 216 } 217 218 testNameForElmRule(aElm, aRuleElm); 219 } else if (aRuleElm.getAttribute("fromsubtree") == "true") { 220 if (gDumpToConsole) { 221 dump( 222 "\nProcessing rule { fromsubtree: " + 223 aRuleElm.getAttribute("fromsubtree") + 224 " }\n" 225 ); 226 } 227 228 testNameForSubtreeRule(aElm, aRuleElm); 229 } 230 } 231 232 function testNameForAttrRule(aElm, aRule) { 233 var name = ""; 234 235 var attr = aRule.getAttribute("attr"); 236 var attrValue = aElm.getAttribute(attr); 237 238 var type = aRule.getAttribute("type"); 239 if (type == "string") { 240 name = attrValue; 241 } else if (type == "ref" && attrValue) { 242 var ids = attrValue.split(/\s+/); 243 for (var idx = 0; idx < ids.length; idx++) { 244 var labelElm = getNode(ids[idx]); 245 if (name != "") { 246 name += " "; 247 } 248 249 name += labelElm.getAttribute("textequiv"); 250 } 251 } 252 253 var msg = "Attribute '" + attr + "' test (" + gTestIterator.testID + "). "; 254 testName(aElm, name, msg); 255 256 if (aRule.getAttribute("explicit-name") != "false") { 257 testAttrs(aElm, { "explicit-name": "true" }, true); 258 } else { 259 testAbsentAttrs(aElm, { "explicit-name": "true" }); 260 } 261 262 waitForEvent( 263 EVENT_NAME_CHANGE, 264 aElm, 265 gTestIterator.iterateNext, 266 gTestIterator 267 ); 268 269 aElm.removeAttribute(attr); 270 } 271 272 function testNameForElmRule(aElm, aRule) { 273 var labelElm; 274 275 var tagname = aRule.getAttribute("elm"); 276 var attrname = aRule.getAttribute("elmattr"); 277 if (attrname) { 278 var filter = { 279 acceptNode: function filter_acceptNode(aNode) { 280 if ( 281 aNode.localName == this.mLocalName && 282 aNode.getAttribute(this.mAttrName) == this.mAttrValue 283 ) { 284 return NodeFilter.FILTER_ACCEPT; 285 } 286 287 return NodeFilter.FILTER_SKIP; 288 }, 289 290 mLocalName: tagname, 291 mAttrName: attrname, 292 mAttrValue: aElm.getAttribute("id"), 293 }; 294 295 var treeWalker = document.createTreeWalker( 296 document.body, 297 NodeFilter.SHOW_ELEMENT, 298 filter 299 ); 300 labelElm = treeWalker.nextNode(); 301 } else { 302 // if attrname is empty then look for the element in subtree. 303 labelElm = aElm.getElementsByTagName(tagname)[0]; 304 if (!labelElm) { 305 labelElm = aElm.getElementsByTagName("html:" + tagname)[0]; 306 } 307 } 308 309 if (!labelElm) { 310 ok(false, msg + " Failed to find '" + tagname + "' element."); 311 gTestIterator.iterateNext(); 312 return; 313 } 314 315 var msg = "Element '" + tagname + "' test (" + gTestIterator.testID + ")."; 316 testName(aElm, labelElm.getAttribute("textequiv"), msg); 317 testAttrs(aElm, { "explicit-name": "true" }, true); 318 319 var parentNode = labelElm.parentNode; 320 321 if (gDumpToConsole) { 322 dump( 323 "\nProcessed elm rule. Wait for name change event on " + 324 prettyName(aElm) + 325 "\n" 326 ); 327 } 328 waitForEvent( 329 EVENT_NAME_CHANGE, 330 aElm, 331 gTestIterator.iterateNext, 332 gTestIterator 333 ); 334 335 parentNode.removeChild(labelElm); 336 } 337 338 function testNameForSubtreeRule(aElm) { 339 var msg = "From subtree test (" + gTestIterator.testID + ")."; 340 testName(aElm, aElm.getAttribute("textequiv"), msg); 341 testAbsentAttrs(aElm, { "explicit-name": "true" }); 342 343 if (gDumpToConsole) { 344 dump( 345 "\nProcessed from subtree rule. Wait for name change event on " + 346 prettyName(aElm) + 347 "\n" 348 ); 349 } 350 waitForEvent( 351 EVENT_NAME_CHANGE, 352 aElm, 353 gTestIterator.iterateNext, 354 gTestIterator 355 ); 356 357 while (aElm.firstChild) { 358 aElm.firstChild.remove(); 359 } 360 } 361 362 /** 363 * Return array of 'rule' elements. Used in conjunction with 364 * getRuleElmsFromRulesetElm() function. 365 */ 366 function getRuleElmsByRulesetId(aRulesetId) { 367 var expr = "//rules/ruledfn/ruleset[@id='" + aRulesetId + "']"; 368 var rulesetElm = evaluateXPath(gRuleDoc, expr); 369 return getRuleElmsFromRulesetElm(rulesetElm[0]); 370 } 371 372 function getRuleElmsFromRulesetElm(aRulesetElm) { 373 var rulesetId = aRulesetElm.getAttribute("ref"); 374 if (rulesetId) { 375 return getRuleElmsByRulesetId(rulesetId); 376 } 377 378 var ruleElms = []; 379 380 var child = aRulesetElm.firstChild; 381 while (child) { 382 if (child.localName == "ruleset") { 383 ruleElms = ruleElms.concat(getRuleElmsFromRulesetElm(child)); 384 } 385 if (child.localName == "rule") { 386 ruleElms.push(child); 387 } 388 389 child = child.nextSibling; 390 } 391 392 return ruleElms; 393 } 394 395 /** 396 * Helper method to evaluate xpath expression. 397 */ 398 function evaluateXPath(aNode, aExpr, aResolver) { 399 var xpe = new XPathEvaluator(); 400 401 var resolver = aResolver; 402 if (!resolver) { 403 var node = 404 aNode.ownerDocument == null 405 ? aNode.documentElement 406 : aNode.ownerDocument.documentElement; 407 resolver = xpe.createNSResolver(node); 408 } 409 410 var result = xpe.evaluate(aExpr, aNode, resolver, 0, null); 411 var found = []; 412 var res; 413 while ((res = result.iterateNext())) { 414 found.push(res); 415 } 416 417 return found; 418 } 419 420 function htmlDocResolver(aPrefix) { 421 var ns = { 422 html: "http://www.w3.org/1999/xhtml", 423 }; 424 return ns[aPrefix] || null; 425 }