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 );