tor-browser

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

browser_caching_table.js (18932B)


      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 /**
      6 * Test tables for both local and remote Accessibles. There is more extensive
      7 * coverage in ../../mochitest/table. These tests are primarily to ensure that
      8 * the cache works as expected and that there is consistency between local and
      9 * remote.
     10 */
     11 
     12 "use strict";
     13 
     14 /* import-globals-from ../../mochitest/table.js */
     15 /* import-globals-from ../../mochitest/attributes.js */
     16 loadScripts(
     17  { name: "table.js", dir: MOCHITESTS_DIR },
     18  { name: "attributes.js", dir: MOCHITESTS_DIR }
     19 );
     20 
     21 /**
     22 * Test table counts, indexes, extents and implicit headers.
     23 */
     24 addAccessibleTask(
     25  `
     26 <table id="table">
     27  <thead>
     28    <tr><th id="a">a</th><th id="bc" colspan="2">bc</th><th id="d">d</th></tr>
     29  </thead>
     30  <tbody>
     31    <tr><th id="ei" rowspan="2">ei</th><td id="fj" rowspan="0">fj</td><td id="g">g</td><td id="h">h</td></tr>
     32    <tr><td id="k">k</td></tr>
     33  </tbody>
     34 </table>
     35  `,
     36  async function (browser, docAcc) {
     37    const table = findAccessibleChildByID(docAcc, "table", [
     38      nsIAccessibleTable,
     39    ]);
     40    is(table.rowCount, 3, "table rowCount correct");
     41    is(table.columnCount, 4, "table columnCount correct");
     42    testTableIndexes(table, [
     43      [0, 1, 1, 2],
     44      [3, 4, 5, 6],
     45      [3, 4, 7, -1],
     46    ]);
     47    const cells = {};
     48    for (const id of ["a", "bc", "d", "ei", "fj", "g", "h", "k"]) {
     49      cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]);
     50    }
     51    is(cells.a.rowExtent, 1, "a rowExtent correct");
     52    is(cells.a.columnExtent, 1, "a columnExtent correct");
     53    is(cells.bc.rowExtent, 1, "bc rowExtent correct");
     54    is(cells.bc.columnExtent, 2, "bc columnExtent correct");
     55    is(cells.ei.rowExtent, 2, "ei rowExtent correct");
     56    is(cells.fj.rowExtent, 2, "fj rowExtent correct");
     57    testHeaderCells([
     58      {
     59        cell: cells.ei,
     60        rowHeaderCells: [],
     61        columnHeaderCells: [cells.a],
     62      },
     63      {
     64        cell: cells.g,
     65        rowHeaderCells: [cells.ei],
     66        columnHeaderCells: [cells.bc],
     67      },
     68      {
     69        cell: cells.k,
     70        rowHeaderCells: [cells.ei],
     71        columnHeaderCells: [cells.bc],
     72      },
     73    ]);
     74  },
     75  {
     76    chrome: true,
     77    topLevel: true,
     78    iframe: true,
     79    remoteIframe: true,
     80  }
     81 );
     82 
     83 /**
     84 * Test table explicit headers.
     85 */
     86 addAccessibleTask(
     87  `
     88 <table id="table">
     89  <tr><th id="a">a</th><th id="b">b</th></tr>
     90  <tr><td id="c" headers="b d">c</td><th scope="row" id="d">d</th></tr>
     91  <tr><td id="e" headers="c f">e</td><td id="f">f</td></tr>
     92 </table>
     93  `,
     94  async function (browser, docAcc) {
     95    const cells = {};
     96    for (const id of ["a", "b", "c", "d", "e", "f"]) {
     97      cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]);
     98    }
     99    testHeaderCells([
    100      {
    101        cell: cells.c,
    102        rowHeaderCells: [cells.d],
    103        columnHeaderCells: [cells.b],
    104      },
    105      {
    106        cell: cells.e,
    107        rowHeaderCells: [cells.f],
    108        columnHeaderCells: [cells.c],
    109      },
    110    ]);
    111  },
    112  {
    113    chrome: true,
    114    topLevel: true,
    115    iframe: true,
    116    remoteIframe: true,
    117  }
    118 );
    119 
    120 /**
    121 * Test that an inner table doesn't impact an outer table.
    122 */
    123 addAccessibleTask(
    124  `
    125 <table id="outerTable">
    126  <tr><th id="outerCell">outerCell<table id="innerTable">
    127    <tr><th id="innerCell">a</th></tr></table>
    128  </table></th></tr>
    129 </table>
    130  `,
    131  async function (browser, docAcc) {
    132    const outerTable = findAccessibleChildByID(docAcc, "outerTable", [
    133      nsIAccessibleTable,
    134    ]);
    135    is(outerTable.rowCount, 1, "outerTable rowCount correct");
    136    is(outerTable.columnCount, 1, "outerTable columnCount correct");
    137    const outerCell = findAccessibleChildByID(docAcc, "outerCell");
    138    is(
    139      outerTable.getCellAt(0, 0),
    140      outerCell,
    141      "outerTable returns correct cell"
    142    );
    143    const innerTable = findAccessibleChildByID(docAcc, "innerTable", [
    144      nsIAccessibleTable,
    145    ]);
    146    is(innerTable.rowCount, 1, "innerTable rowCount correct");
    147    is(innerTable.columnCount, 1, "innerTable columnCount correct");
    148    const innerCell = findAccessibleChildByID(docAcc, "innerCell");
    149    is(
    150      innerTable.getCellAt(0, 0),
    151      innerCell,
    152      "innerTable returns correct cell"
    153    );
    154  },
    155  {
    156    chrome: true,
    157    topLevel: true,
    158    iframe: true,
    159    remoteIframe: true,
    160  }
    161 );
    162 
    163 /**
    164 * Test table caption and summary.
    165 */
    166 addAccessibleTask(
    167  `
    168 <table id="t1">
    169  <caption id="c1">c1</caption>
    170  <tr><th>a</th></tr>
    171 </table>
    172 <table id="t2" summary="s2">
    173  <tr><th>a</th></tr>
    174 </table>
    175 <table id="t3" summary="s3">
    176  <caption id="c3">c3</caption>
    177  <tr><th>a</th></tr>
    178 </table>
    179  `,
    180  async function (browser, docAcc) {
    181    const t1 = findAccessibleChildByID(docAcc, "t1", [nsIAccessibleTable]);
    182    const c1 = findAccessibleChildByID(docAcc, "c1");
    183    is(t1.caption, c1, "t1 caption correct");
    184    ok(!t1.summary, "t1 no summary");
    185    const t2 = findAccessibleChildByID(docAcc, "t2", [nsIAccessibleTable]);
    186    ok(!t2.caption, "t2 caption is null");
    187    is(t2.summary, "s2", "t2 summary correct");
    188    const t3 = findAccessibleChildByID(docAcc, "t3", [nsIAccessibleTable]);
    189    const c3 = findAccessibleChildByID(docAcc, "c3");
    190    is(t3.caption, c3, "t3 caption correct");
    191    is(t3.summary, "s3", "t3 summary correct");
    192  },
    193  {
    194    chrome: true,
    195    topLevel: true,
    196    iframe: true,
    197    remoteIframe: true,
    198  }
    199 );
    200 
    201 /**
    202 * Test table layout guess.
    203 */
    204 addAccessibleTask(
    205  `
    206 <table id="layout"><tr><td>a</td></tr></table>
    207 <table id="data"><tr><th>a</th></tr></table>
    208 <table id="mutate"><tr><td>a</td><td>b</td></tr></table>
    209 <div id="newTableContainer"></div>
    210  `,
    211  async function (browser, docAcc) {
    212    const layout = findAccessibleChildByID(docAcc, "layout");
    213    testAttrs(layout, { "layout-guess": "true" }, true);
    214    const data = findAccessibleChildByID(docAcc, "data");
    215    testAbsentAttrs(data, { "layout-guess": "true" });
    216    const mutate = findAccessibleChildByID(docAcc, "mutate");
    217    testAttrs(mutate, { "layout-guess": "true" }, true);
    218 
    219    info("mutate: Adding 5 rows");
    220    let reordered = waitForEvent(EVENT_REORDER, mutate);
    221    await invokeContentTask(browser, [], () => {
    222      const frag = content.document.createDocumentFragment();
    223      for (let r = 0; r < 6; ++r) {
    224        const tr = content.document.createElement("tr");
    225        tr.innerHTML = "<td>a</td><td>b</td>";
    226        frag.append(tr);
    227      }
    228      content.document.getElementById("mutate").tBodies[0].append(frag);
    229    });
    230    await reordered;
    231    testAbsentAttrs(mutate, { "layout-guess": "true" });
    232 
    233    info("mutate: Removing 5 rows");
    234    reordered = waitForEvent(EVENT_REORDER, mutate);
    235    await invokeContentTask(browser, [], () => {
    236      // Pause refresh driver so all the children removals below will
    237      // be collated into the same tick and only one 'reorder' event will
    238      // be dispatched.
    239      content.windowUtils.advanceTimeAndRefresh(100);
    240 
    241      let tBody = content.document.getElementById("mutate").tBodies[0];
    242      for (let r = 0; r < 6; ++r) {
    243        tBody.lastChild.remove();
    244      }
    245 
    246      // Resume refresh driver
    247      content.windowUtils.restoreNormalRefresh();
    248    });
    249    await reordered;
    250    testAttrs(mutate, { "layout-guess": "true" }, true);
    251 
    252    info("mutate: Adding new table");
    253    let shown = waitForEvent(EVENT_SHOW, "newTable");
    254    await invokeContentTask(browser, [], () => {
    255      content.document.getElementById("newTableContainer").innerHTML =
    256        `<table id="newTable"><tr><th>a</th></tr></table>`;
    257    });
    258    let newTable = (await shown).accessible;
    259    testAbsentAttrs(newTable, { "layout-guess": "true" });
    260  },
    261  {
    262    chrome: true,
    263    topLevel: true,
    264    iframe: true,
    265    remoteIframe: true,
    266  }
    267 );
    268 
    269 /**
    270 * Test table layout guess with border styling changes.
    271 */
    272 addAccessibleTask(
    273  `
    274  <table id="layout"><tr><td id="cell">a</td><td>b</td></tr>
    275  <tr><td>c</td><td>d</td></tr><tr><td>c</td><td>d</td></tr></table>
    276  `,
    277  async function (browser, docAcc) {
    278    const layout = findAccessibleChildByID(docAcc, "layout");
    279    testAttrs(layout, { "layout-guess": "true" }, true);
    280    info("changing border style on table cell");
    281    await invokeContentTask(browser, [], () => {
    282      content.document.getElementById("cell").style.border = "1px solid black";
    283      content.document.body.offsetTop; // Flush layout.
    284    });
    285    await untilCacheOk(() => {
    286      // manually verify the attribute doesn't exist, since `testAbsentAttrs`
    287      // has internal calls to ok() which fail if the cache hasn't yet updated
    288      for (let prop of layout.attributes.enumerate()) {
    289        if (prop.key == "layout-guess") {
    290          return false;
    291        }
    292      }
    293      return true;
    294    }, "Table is a data table");
    295  },
    296  {
    297    chrome: true,
    298    topLevel: true,
    299    iframe: true,
    300    remoteIframe: true,
    301  }
    302 );
    303 
    304 /**
    305 * Test ARIA grid.
    306 */
    307 addAccessibleTask(
    308  `
    309 <div id="grid" role="grid">
    310  <div role="rowgroup">
    311    <div role="row"><div id="a" role="columnheader">a</div><div id="b" role="columnheader">b</div></div>
    312  </div>
    313  <div tabindex="-1">
    314    <div role="row"><div id="c" role="rowheader">c</div><div id="d" role="gridcell">d</div></div>
    315  </div>
    316 </div>
    317  `,
    318  async function (browser, docAcc) {
    319    const grid = findAccessibleChildByID(docAcc, "grid", [nsIAccessibleTable]);
    320    is(grid.rowCount, 2, "grid rowCount correct");
    321    is(grid.columnCount, 2, "grid columnCount correct");
    322    testTableIndexes(grid, [
    323      [0, 1],
    324      [2, 3],
    325    ]);
    326    const cells = {};
    327    for (const id of ["a", "b", "c", "d"]) {
    328      cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]);
    329    }
    330    is(cells.a.rowExtent, 1, "a rowExtent correct");
    331    is(cells.a.columnExtent, 1, "a columnExtent correct");
    332    testHeaderCells([
    333      {
    334        cell: cells.c,
    335        rowHeaderCells: [],
    336        columnHeaderCells: [cells.a],
    337      },
    338      {
    339        cell: cells.d,
    340        rowHeaderCells: [cells.c],
    341        columnHeaderCells: [cells.b],
    342      },
    343    ]);
    344  },
    345  {
    346    chrome: true,
    347    topLevel: true,
    348    iframe: true,
    349    remoteIframe: true,
    350  }
    351 );
    352 
    353 function setNodeHidden(browser, id, hidden) {
    354  return invokeContentTask(browser, [id, hidden], (cId, cHidden) => {
    355    content.document.getElementById(cId).hidden = cHidden;
    356  });
    357 }
    358 
    359 /**
    360 * Test that the table is updated correctly when it is mutated.
    361 */
    362 addAccessibleTask(
    363  `
    364 <table id="table">
    365  <tr id="r1"><td>a</td><td id="b">b</td></tr>
    366  <tr id="r2" hidden><td>c</td><td>d</td></tr>
    367 </table>
    368 <div id="owner"></div>
    369  `,
    370  async function (browser, docAcc) {
    371    const table = findAccessibleChildByID(docAcc, "table", [
    372      nsIAccessibleTable,
    373    ]);
    374    is(table.rowCount, 1, "table rowCount correct");
    375    is(table.columnCount, 2, "table columnCount correct");
    376    testTableIndexes(table, [[0, 1]]);
    377    info("Showing r2");
    378    let reordered = waitForEvent(EVENT_REORDER, table);
    379    await setNodeHidden(browser, "r2", false);
    380    await reordered;
    381    is(table.rowCount, 2, "table rowCount correct");
    382    testTableIndexes(table, [
    383      [0, 1],
    384      [2, 3],
    385    ]);
    386    info("Hiding r2");
    387    reordered = waitForEvent(EVENT_REORDER, table);
    388    await setNodeHidden(browser, "r2", true);
    389    await reordered;
    390    is(table.rowCount, 1, "table rowCount correct");
    391    testTableIndexes(table, [[0, 1]]);
    392    info("Hiding b");
    393    reordered = waitForEvent(EVENT_REORDER, "r1");
    394    await setNodeHidden(browser, "b", true);
    395    await reordered;
    396    is(table.columnCount, 1, "table columnCount correct");
    397    testTableIndexes(table, [[0]]);
    398    info("Showing b");
    399    reordered = waitForEvent(EVENT_REORDER, "r1");
    400    await setNodeHidden(browser, "b", false);
    401    await reordered;
    402    is(table.columnCount, 2, "table columnCount correct");
    403    info("Moving b out of table using aria-owns");
    404    reordered = waitForEvent(EVENT_REORDER, "r1");
    405    await invokeContentTask(browser, [], () => {
    406      content.document.getElementById("owner").setAttribute("aria-owns", "b");
    407    });
    408    await reordered;
    409    is(table.columnCount, 1, "table columnCount correct");
    410  },
    411  {
    412    chrome: true,
    413    topLevel: true,
    414    iframe: true,
    415    remoteIframe: true,
    416  }
    417 );
    418 
    419 /**
    420 * Test the handling of ARIA tables with display: contents.
    421 */
    422 addAccessibleTask(
    423  `
    424 <div id="table" role="table" style="display: contents;">
    425  <div role="row"><div role="cell">a</div></div>
    426 </div>
    427  `,
    428  async function (browser, docAcc) {
    429    const table = findAccessibleChildByID(docAcc, "table", [
    430      nsIAccessibleTable,
    431    ]);
    432    is(table.rowCount, 1, "table rowCount correct");
    433    is(table.columnCount, 1, "table columnCount correct");
    434  },
    435  {
    436    chrome: true,
    437    topLevel: true,
    438    iframe: true,
    439    remoteIframe: true,
    440  }
    441 );
    442 
    443 /**
    444 * Test a broken ARIA table with an invalid cell.
    445 */
    446 addAccessibleTask(
    447  `
    448 <div id="table" role="table">
    449  <div role="main">
    450    <div role="row">
    451      <div id="cell" role="cell">a</div>
    452    </div>
    453  </div>
    454 </div>
    455  `,
    456  async function (browser, docAcc) {
    457    const table = findAccessibleChildByID(docAcc, "table", [
    458      nsIAccessibleTable,
    459    ]);
    460    is(table.rowCount, 0, "table rowCount correct");
    461    is(table.columnCount, 0, "table columnCount correct");
    462    const cell = findAccessibleChildByID(docAcc, "cell");
    463    let queryOk = false;
    464    try {
    465      cell.QueryInterface(nsIAccessibleTableCell);
    466      queryOk = true;
    467    } catch (e) {}
    468    ok(!queryOk, "Got nsIAccessibleTableCell on an invalid cell");
    469  },
    470  {
    471    chrome: true,
    472    topLevel: true,
    473    iframe: true,
    474    remoteIframe: true,
    475  }
    476 );
    477 
    478 /**
    479 * Test that building the cache for a malformed table with an iframe inside a
    480 * row doesn't crash (bug 1800780).
    481 */
    482 addAccessibleTask(
    483  `<table><tr id="tr"></tr></table>`,
    484  async function (browser) {
    485    let reordered = waitForEvent(EVENT_REORDER, "tr");
    486    await invokeContentTask(browser, [], () => {
    487      const iframe = content.document.createElement("iframe");
    488      content.document.getElementById("tr").append(iframe);
    489    });
    490    await reordered;
    491  },
    492  { topLevel: true }
    493 );
    494 
    495 /**
    496 * Verify that table row and column information is correct when there are
    497 * intervening generics between the table and a rowgroup.
    498 */
    499 addAccessibleTask(
    500  `
    501 <div id="table" role="grid">
    502  <div role="rowgroup">
    503    <div role="row">
    504      <div role="columnheader">a</div>
    505    </div>
    506  </div>
    507  <div tabindex="-1" style="height: 1px; overflow: auto;">
    508    <div role="rowgroup">
    509      <div role="row">
    510        <div id="cell" role="gridcell">b</div>
    511      </div>
    512    </div>
    513  </div>
    514 </div>
    515  `,
    516  async function (browser, docAcc) {
    517    const table = findAccessibleChildByID(docAcc, "table", [
    518      nsIAccessibleTable,
    519    ]);
    520 
    521    info("Verifying that the table row and column counts are correct.");
    522    is(table.rowCount, 2, "table rowCount correct");
    523    is(table.columnCount, 1, "table columnCount correct");
    524 
    525    info("Verifying that the cell row and column extents are correct.");
    526    const cell = findAccessibleChildByID(docAcc, "cell", [
    527      nsIAccessibleTableCell,
    528    ]);
    529    is(cell.rowExtent, 1, "cell rowExtent correct");
    530    is(cell.columnExtent, 1, "cell colExtent correct");
    531    is(cell.rowIndex, 1, "cell rowIndex correct");
    532    is(cell.columnIndex, 0, "cell columnIndex correct");
    533  },
    534  { chrome: true, topLevel: true, iframe: true, remoteIframe: true }
    535 );
    536 
    537 /**
    538 * Verify that table row and column information is correct when there are
    539 * intervening generics between rows and cells.
    540 */
    541 addAccessibleTask(
    542  `
    543 <div id="table" role="grid">
    544  <div role="rowgroup">
    545    <div role="row">
    546      <div role="columnheader">a</div>
    547    </div>
    548  </div>
    549  <div role="rowgroup">
    550    <div role="row">
    551      <div tabindex="-1" style="height: 1px; overflow: auto;">
    552        <div id="cell" role="gridcell">b</div>
    553      </div>
    554    </div>
    555  </div>
    556 </div>
    557  `,
    558  async function (browser, docAcc) {
    559    const table = findAccessibleChildByID(docAcc, "table", [
    560      nsIAccessibleTable,
    561    ]);
    562 
    563    info("Verifying that the table row and column counts are correct.");
    564    is(table.rowCount, 2, "table rowCount correct");
    565    is(table.columnCount, 1, "table columnCount correct");
    566 
    567    info("Verifying that the cell row and column extents are correct.");
    568    const cell = findAccessibleChildByID(docAcc, "cell", [
    569      nsIAccessibleTableCell,
    570    ]);
    571    is(cell.rowExtent, 1, "cell rowExtent correct");
    572    is(cell.columnExtent, 1, "cell colExtent correct");
    573    is(cell.rowIndex, 1, "cell rowIndex correct");
    574    is(cell.columnIndex, 0, "cell columnIndex correct");
    575  },
    576  { chrome: true, topLevel: true, iframe: true, remoteIframe: true }
    577 );
    578 
    579 /**
    580 * Verify that we don't crash for authoring error like <table role="gridcell">.
    581 */
    582 addAccessibleTask(
    583  `<table id="table" role="gridcell">`,
    584  async function (browser, docAcc) {
    585    const table = findAccessibleChildByID(docAcc, "table");
    586    ok(table, "Retrieved table Accessible");
    587  },
    588  { chrome: true, topLevel: true }
    589 );
    590 
    591 /**
    592 * Test ARIA tables in SVG.
    593 */
    594 addAccessibleTask(
    595  `
    596 <svg id="table" role="table">
    597  <text id="caption" role="caption">caption</text>
    598  <g role="row">
    599    <text id="a" role="columnheader">a</text>
    600    <text id="b" role="columnheader">b</text>
    601  </g>
    602  <g role="row">
    603    <text id="c" role="cell">c</text>
    604    <text id="d" role="cell">d</text>
    605  </g>
    606 </svg>
    607  `,
    608  async function (browser, docAcc) {
    609    const table = findAccessibleChildByID(docAcc, "table", [
    610      nsIAccessibleTable,
    611    ]);
    612    is(table.rowCount, 2, "table rowCount correct");
    613    is(table.columnCount, 2, "table columnCount correct");
    614    const caption = findAccessibleChildByID(docAcc, "caption");
    615    is(table.caption, caption, "table caption correct");
    616    testTableIndexes(table, [
    617      [0, 1],
    618      [2, 3],
    619    ]);
    620    const cells = {};
    621    for (const id of ["a", "b", "c", "d"]) {
    622      cells[id] = findAccessibleChildByID(docAcc, id, [nsIAccessibleTableCell]);
    623    }
    624    testHeaderCells([
    625      {
    626        cell: cells.c,
    627        rowHeaderCells: [],
    628        columnHeaderCells: [cells.a],
    629      },
    630      {
    631        cell: cells.d,
    632        rowHeaderCells: [],
    633        columnHeaderCells: [cells.b],
    634      },
    635    ]);
    636  },
    637  { chrome: true, topLevel: true, remoteIframe: true }
    638 );
    639 
    640 /**
    641 * Verify that we don't crash for authoring error like <tr role="grid">.
    642 */
    643 addAccessibleTask(
    644  `
    645 <table id="table">
    646  <tr><th>a</th></tr>
    647  <tr role="grid"><td id="b">b</td></tr>
    648 </table>
    649  `,
    650  async function (browser, docAcc) {
    651    const table = findAccessibleChildByID(docAcc, "table", [
    652      nsIAccessibleTable,
    653    ]);
    654    is(table.rowCount, 1, "table rowCount correct");
    655    is(table.columnCount, 1, "table columnCount correct");
    656    const b = findAccessibleChildByID(docAcc, "b");
    657    let queryOk = false;
    658    try {
    659      b.QueryInterface(nsIAccessibleTableCell);
    660      queryOk = true;
    661    } catch (e) {}
    662    ok(!queryOk, "No nsIAccessibleTableCell on invalid cell b");
    663  }
    664 );