tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }