tor-browser

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

browser_relations_general.js (10994B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 requestLongerTimeout(2);
      7 
      8 /**
      9 * A test specification that has the following format:
     10 * [
     11 *   attr                 relevant aria attribute
     12 *   hostRelation         corresponding host relation type
     13 *   dependantRelation    corresponding dependant relation type
     14 * ]
     15 */
     16 const attrRelationsSpec = [
     17  ["aria-labelledby", RELATION_LABELLED_BY, RELATION_LABEL_FOR],
     18  ["aria-describedby", RELATION_DESCRIBED_BY, RELATION_DESCRIPTION_FOR],
     19  ["aria-controls", RELATION_CONTROLLER_FOR, RELATION_CONTROLLED_BY],
     20  ["aria-flowto", RELATION_FLOWS_TO, RELATION_FLOWS_FROM],
     21  ["aria-details", RELATION_DETAILS, RELATION_DETAILS_FOR],
     22  ["aria-errormessage", RELATION_ERRORMSG, RELATION_ERRORMSG_FOR],
     23  ["aria-actions", RELATION_ACTION, RELATION_ACTION_FOR],
     24 ];
     25 
     26 /**
     27 * Test caching of relations between accessible objects.
     28 */
     29 addAccessibleTask(
     30  `
     31  <div id="dependant1">label</div>
     32  <div id="dependant2">label2</div>
     33  <div role="checkbox" id="host"></div>`,
     34  async function (browser, accDoc) {
     35    for (let spec of attrRelationsSpec) {
     36      await testRelated(browser, accDoc, ...spec);
     37    }
     38  },
     39  { iframe: true, remoteIframe: true }
     40 );
     41 
     42 /**
     43 * Test caching of relations with respect to label objects and their "for" attr.
     44 */
     45 addAccessibleTask(
     46  `
     47  <input type="checkbox" id="dependant">
     48  <label id="host">label</label>`,
     49  async function (browser, accDoc) {
     50    const host = findAccessibleChildByID(accDoc, "host");
     51    const dependant = findAccessibleChildByID(accDoc, "dependant");
     52    async function testLabel(hasLabel) {
     53      await testCachedRelation(
     54        host,
     55        RELATION_LABEL_FOR,
     56        hasLabel ? dependant : []
     57      );
     58      await testCachedRelation(
     59        dependant,
     60        RELATION_LABELLED_BY,
     61        hasLabel ? host : []
     62      );
     63    }
     64 
     65    await testLabel(false);
     66    await invokeSetAttribute(browser, "host", "for", "dependant");
     67    await testLabel(true);
     68    await invokeSetAttribute(browser, "dependant", "id", "invalid");
     69    await testLabel(false);
     70  },
     71  { chrome: true, iframe: true, remoteIframe: true }
     72 );
     73 
     74 /**
     75 * Test rel caching for element with existing relation attribute.
     76 */
     77 addAccessibleTask(
     78  `<div id="label">label</div><button id="button" aria-labelledby="label">`,
     79  async function (browser, accDoc) {
     80    const button = findAccessibleChildByID(accDoc, "button");
     81    const label = findAccessibleChildByID(accDoc, "label");
     82 
     83    await testCachedRelation(button, RELATION_LABELLED_BY, label);
     84    await testCachedRelation(label, RELATION_LABEL_FOR, button);
     85  },
     86  { iframe: true, remoteIframe: true }
     87 );
     88 
     89 /**
     90 * Test caching of relations with respect to output objects and their "for" attr.
     91 */
     92 addAccessibleTask(
     93  `
     94  <form oninput="host.value=parseInt(dependant1.value)+parseInt(dependant2.value)">
     95    <input type="number" id="dependant1" value="50"> +
     96    <input type="number" id="dependant2" value="25"> =
     97    <output name="host" id="host"></output>
     98  </form>`,
     99  async function (browser, accDoc) {
    100    await testRelated(
    101      browser,
    102      accDoc,
    103      "for",
    104      RELATION_CONTROLLED_BY,
    105      RELATION_CONTROLLER_FOR
    106    );
    107  },
    108  { iframe: true, remoteIframe: true }
    109 );
    110 
    111 /**
    112 * Test rel caching for <label> element with existing "for" attribute.
    113 */
    114 addAccessibleTask(
    115  `data:text/html,<label id="label" for="input">label</label><input id="input">`,
    116  async function (browser, accDoc) {
    117    const input = findAccessibleChildByID(accDoc, "input");
    118    const label = findAccessibleChildByID(accDoc, "label");
    119    await testCachedRelation(input, RELATION_LABELLED_BY, label);
    120    await testCachedRelation(label, RELATION_LABEL_FOR, input);
    121  },
    122  { iframe: true, remoteIframe: true }
    123 );
    124 
    125 /*
    126 * Test caching of relations with respect to label objects that are ancestors of
    127 * their target.
    128 */
    129 addAccessibleTask(
    130  `
    131  <label id="host">
    132    <input type="checkbox" id="dependant1">
    133  </label>`,
    134  async function (browser, accDoc) {
    135    const input = findAccessibleChildByID(accDoc, "dependant1");
    136    const label = findAccessibleChildByID(accDoc, "host");
    137 
    138    await testCachedRelation(input, RELATION_LABELLED_BY, label);
    139    await testCachedRelation(label, RELATION_LABEL_FOR, input);
    140  },
    141  { iframe: true, remoteIframe: true }
    142 );
    143 
    144 /*
    145 * Test EMBEDS on root accessible.
    146 */
    147 addAccessibleTask(
    148  `hello world`,
    149  async function (browser, primaryDocAcc, secondaryDocAcc) {
    150    // The root accessible should EMBED the top level
    151    // content document. If this test runs in an iframe,
    152    // the test harness will pass in doc accs for both the
    153    // iframe (primaryDocAcc) and the top level remote
    154    // browser (secondaryDocAcc). We should use the second
    155    // one.
    156    // If this is not in an iframe, we'll only get
    157    // a single docAcc (primaryDocAcc) which refers to
    158    // the top level content doc.
    159    const topLevelDoc = secondaryDocAcc ? secondaryDocAcc : primaryDocAcc;
    160    await testRelation(
    161      getRootAccessible(document),
    162      RELATION_EMBEDS,
    163      topLevelDoc
    164    );
    165  },
    166  { chrome: true, iframe: true, remoteIframe: true }
    167 );
    168 
    169 /**
    170 * Test embeds while a modal dialog is displayed which blocks all UI and
    171 * documents behind it.
    172 */
    173 addAccessibleTask(`test`, async function testEmbedsModal(browser, tabDoc) {
    174  const root = getRootAccessible(document);
    175  testRelation(root, RELATION_EMBEDS, tabDoc);
    176  ok(tabDoc.parent, "tabDoc has a parent");
    177 
    178  info("Showing modal");
    179  const modal = document.createElement("dialog");
    180  let shown = waitForEvent(EVENT_SHOW, modal);
    181  document.body.append(modal);
    182  modal.showModal();
    183  await shown;
    184  ok(!tabDoc.parent, "tabDoc has no parent");
    185  testRelation(root, RELATION_EMBEDS, null);
    186 
    187  info("Removing modal");
    188  let hidden = waitForEvent(EVENT_HIDE, modal);
    189  modal.remove();
    190  await hidden;
    191  ok(tabDoc.parent, "tabDoc has a parent");
    192  testRelation(root, RELATION_EMBEDS, tabDoc);
    193 });
    194 
    195 /**
    196 * Test CONTAINING_TAB_PANE
    197 */
    198 addAccessibleTask(
    199  `<p id="p">hello world</p>`,
    200  async function (browser, primaryDocAcc, secondaryDocAcc) {
    201    // The CONTAINING_TAB_PANE of any acc should be the top level
    202    // content document. If this test runs in an iframe,
    203    // the test harness will pass in doc accs for both the
    204    // iframe (primaryDocAcc) and the top level remote
    205    // browser (secondaryDocAcc). We should use the second
    206    // one.
    207    // If this is not in an iframe, we'll only get
    208    // a single docAcc (primaryDocAcc) which refers to
    209    // the top level content doc.
    210    const topLevelDoc = secondaryDocAcc ? secondaryDocAcc : primaryDocAcc;
    211    await testCachedRelation(
    212      findAccessibleChildByID(primaryDocAcc, "p"),
    213      RELATION_CONTAINING_TAB_PANE,
    214      topLevelDoc
    215    );
    216  },
    217  {
    218    chrome: true,
    219    topLevel: true,
    220    iframe: true,
    221    remoteIframe: true,
    222  }
    223 );
    224 
    225 /*
    226 * Test relation caching on link
    227 */
    228 addAccessibleTask(
    229  `
    230  <a id="link" href="#item">a</a>
    231  <div id="item">hello</div>
    232  <div id="item2">world</div>
    233  <a id="link2" href="#anchor">b</a>
    234  <a id="namedLink" name="anchor">c</a>`,
    235  async function (browser, accDoc) {
    236    const link = findAccessibleChildByID(accDoc, "link");
    237    const link2 = findAccessibleChildByID(accDoc, "link2");
    238    const namedLink = findAccessibleChildByID(accDoc, "namedLink");
    239    const item = findAccessibleChildByID(accDoc, "item");
    240    const item2 = findAccessibleChildByID(accDoc, "item2");
    241 
    242    await testCachedRelation(link, RELATION_LINKS_TO, item);
    243    await testCachedRelation(link2, RELATION_LINKS_TO, namedLink);
    244 
    245    await invokeContentTask(browser, [], () => {
    246      content.document.getElementById("link").href = "";
    247      content.document.getElementById("namedLink").name = "newName";
    248    });
    249 
    250    await testCachedRelation(link, RELATION_LINKS_TO, []);
    251    await testCachedRelation(link2, RELATION_LINKS_TO, []);
    252 
    253    await invokeContentTask(browser, [], () => {
    254      content.document.getElementById("link").href = "#item2";
    255    });
    256 
    257    await testCachedRelation(link, RELATION_LINKS_TO, item2);
    258  },
    259  {
    260    chrome: true,
    261    // IA2 doesn't have a LINKS_TO relation and Windows non-cached
    262    // RemoteAccessible uses IA2, so we can't run these tests in this case.
    263    topLevel: true,
    264    iframe: true,
    265    remoteIframe: true,
    266  }
    267 );
    268 
    269 /*
    270 * Test relation caching for NODE_CHILD_OF and NODE_PARENT_OF with aria trees.
    271 */
    272 addAccessibleTask(
    273  `
    274  <div role="tree" id="tree">
    275    <div role="treeitem" id="treeitem">test</div>
    276    <div role="treeitem" id="treeitem2">test</div>
    277  </div>`,
    278  async function (browser, accDoc) {
    279    const tree = findAccessibleChildByID(accDoc, "tree");
    280    const treeItem = findAccessibleChildByID(accDoc, "treeitem");
    281    const treeItem2 = findAccessibleChildByID(accDoc, "treeitem2");
    282 
    283    await testCachedRelation(tree, RELATION_NODE_PARENT_OF, [
    284      treeItem,
    285      treeItem2,
    286    ]);
    287    await testCachedRelation(treeItem, RELATION_NODE_CHILD_OF, tree);
    288  },
    289  { chrome: true, iframe: true, remoteIframe: true }
    290 );
    291 
    292 /*
    293 * Test relation caching for NODE_CHILD_OF and NODE_PARENT_OF with aria lists.
    294 */
    295 addAccessibleTask(
    296  `
    297  <div id="l1" role="list">
    298    <div id="l1i1" role="listitem" aria-level="1">a</div>
    299    <div id="l1i2" role="listitem" aria-level="2">b</div>
    300    <div id="l1i3" role="listitem" aria-level="1">c</div>
    301  </div>`,
    302  async function (browser, accDoc) {
    303    const list = findAccessibleChildByID(accDoc, "l1");
    304    const listItem1 = findAccessibleChildByID(accDoc, "l1i1");
    305    const listItem2 = findAccessibleChildByID(accDoc, "l1i2");
    306    const listItem3 = findAccessibleChildByID(accDoc, "l1i3");
    307 
    308    await testCachedRelation(list, RELATION_NODE_PARENT_OF, [
    309      listItem1,
    310      listItem3,
    311    ]);
    312    await testCachedRelation(listItem1, RELATION_NODE_CHILD_OF, list);
    313    await testCachedRelation(listItem3, RELATION_NODE_CHILD_OF, list);
    314 
    315    await testCachedRelation(listItem1, RELATION_NODE_PARENT_OF, listItem2);
    316    await testCachedRelation(listItem2, RELATION_NODE_CHILD_OF, listItem1);
    317  },
    318  { chrome: true, iframe: true, remoteIframe: true }
    319 );
    320 
    321 /*
    322 * Test NODE_CHILD_OF relation caching for JAWS window emulation special case.
    323 */
    324 addAccessibleTask(
    325  ``,
    326  async function (browser, accDoc) {
    327    await testCachedRelation(accDoc, RELATION_NODE_CHILD_OF, accDoc.parent);
    328  },
    329  { topLevel: true, chrome: true }
    330 );
    331 
    332 /*
    333 * Test relation caching for LABELLED_BY and LABEL_FOR with legend/fieldset.
    334 */
    335 addAccessibleTask(
    336  `
    337  <fieldset id="fs">
    338    <legend id="leg">legend</legend>
    339    inner content
    340  </fieldset>`,
    341  async function testFieldsetLegendLabels(browser, accDoc) {
    342    const fs = findAccessibleChildByID(accDoc, "fs");
    343    const leg = findAccessibleChildByID(accDoc, "leg");
    344 
    345    await testCachedRelation(fs, RELATION_LABELLED_BY, leg);
    346    await testCachedRelation(leg, RELATION_LABEL_FOR, fs);
    347  },
    348  { chrome: true, iframe: true, remoteIframe: true }
    349 );