diff options
Diffstat (limited to '')
91 files changed, 14086 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/tree/a11y.ini b/accessible/tests/mochitest/tree/a11y.ini new file mode 100644 index 0000000000..c2a78c76a7 --- /dev/null +++ b/accessible/tests/mochitest/tree/a11y.ini @@ -0,0 +1,58 @@ +[DEFAULT] +support-files = + dockids.html + wnd.xhtml + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/formimage.png + !/accessible/tests/mochitest/letters.gif + !/accessible/tests/mochitest/moz.png + !/accessible/tests/mochitest/tree/wnd.xhtml + !/dom/media/test/bug461281.ogg + +[test_applicationacc.xhtml] +skip-if = true # Bug 561508 +[test_aria_display_contents.html] +[test_aria_globals.html] +[test_aria_grid.html] +[test_aria_imgmap.html] +[test_aria_list.html] +[test_aria_menu.html] +[test_aria_owns.html] +[test_aria_presentation.html] +[test_aria_table.html] +[test_brokencontext.html] +[test_button.xhtml] +[test_canvas.html] +[test_combobox.xhtml] +[test_cssflexbox.html] +[test_cssoverflow.html] +[test_display_contents.html] +[test_divs.html] +[test_dochierarchy.html] +[test_dockids.html] +[test_filectrl.html] +[test_formctrl.html] +[test_formctrl.xhtml] +[test_gencontent.html] +[test_groupbox.xhtml] +[test_html_in_mathml.html] +[test_iframe.html] +[test_image.xhtml] +[test_img.html] +[test_invalid_img.xhtml] +[test_invalidationlist.html] +[test_list.html] +[test_map.html] +[test_media.html] +[test_select.html] +[test_svg.html] +[test_tabbox.xhtml] +[test_tabbrowser.xhtml] +skip-if = (os == 'linux' && debug) || (os == 'win' && ccov) # Bug 1389365 || bug 1423218 +[test_table.html] +[test_table_2.html] +[test_table_3.html] +[test_tree.xhtml] +[test_txtcntr.html] +[test_txtctrl.html] +[test_txtctrl.xhtml] diff --git a/accessible/tests/mochitest/tree/dockids.html b/accessible/tests/mochitest/tree/dockids.html new file mode 100644 index 0000000000..e964cd759e --- /dev/null +++ b/accessible/tests/mochitest/tree/dockids.html @@ -0,0 +1,32 @@ +<!doctype html> +<html> + <head> + <link rel="next" href="http://www.mozilla.org"> + <style> + head, link, a { display: block; } + link:after { content: "Link to " attr(href); } + </style> + <script> + window.onload = function() { + document.documentElement.appendChild(document.createElement("input")); + + var l = document.createElement("link"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + l.href = "http://www.mozilla.org"; + l.textContent = "Another "; + document.documentElement.appendChild(l); + + l = document.createElement("a"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + l.href = "http://www.mozilla.org"; + l.textContent = "Yet another link to mozilla"; + document.documentElement.appendChild(l); + }; + </script> + </head> + <body> + Hey, I'm a <body> with three links that are not inside me and an input + that's not inside me. + </body> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_applicationacc.xhtml b/accessible/tests/mochitest/tree/test_applicationacc.xhtml new file mode 100644 index 0000000000..5e00a2878f --- /dev/null +++ b/accessible/tests/mochitest/tree/test_applicationacc.xhtml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible Application Accessible hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + // Note: bug 560239 can be tested if this test runs in standalone mode only. + + var gURL = "../tree/wnd.xhtml" + var gWnd = window.openDialog(gURL, "wnd", "chrome,width=600,height=600"); + + function doTest() + { + // Application accessible should contain two root document accessibles, + // one is for browser window, another one is for open dialog window. + var accTree = { + role: ROLE_APP_ROOT, + children: [ + { + role: ROLE_CHROME_WINDOW, + name: "Accessibility Chrome Test Harness - Minefield" + }, + { + role: ROLE_CHROME_WINDOW, + name: "Empty Window" + } + ] + }; + testAccessibleTree(getApplicationAccessible(), accTree); + + gWnd.close(); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + + // We need to open dialog window before accessibility is started. + addLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=560239" + title="no children of application accessible for windows open before accessibility was started"> + Mozilla Bug 560239 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/tree/test_aria_display_contents.html b/accessible/tests/mochitest/tree/test_aria_display_contents.html new file mode 100644 index 0000000000..5c6f7f20fb --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_display_contents.html @@ -0,0 +1,173 @@ +<!DOCTYPE html> +<html> +<head> + <title>ARIA and style="display: contents;"</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // Test ARIA grids that have display: contents; on different elements. + // They should all have equivalent trees. + var accTree = + { TABLE: [ + { ROW: [ + { role: ROLE_COLUMNHEADER, + children: [ { TEXT_LEAF: [ ] }, ] + }, + { role: ROLE_COLUMNHEADER, + children: [ { TEXT_LEAF: [ ] }, ] + }, + ] }, + { ROW: [ + { ROWHEADER: [ + { TEXT_LEAF: [ ] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("gridWithoutDisplayContents", accTree); + testAccessibleTree("gridWithDisplayContents", accTree); + testAccessibleTree("gridWithDisplayContentsRow", accTree); + testAccessibleTree("gridWithDisplayContentsColHeader", accTree); + testAccessibleTree("gridWithDisplayContentsRowHeader", accTree); + testAccessibleTree("gridWithDisplayContentsGridCell", accTree); + + // Test divs with ARIA roles and attributes and display: contents to + // verify that Accessibles are created appropriately. + accTree = + { SECTION: [ + { LIST: [ + { LISTITEM: [ + { TEXT_LEAF: [ ] } + ] }, + ] }, + { SECTION: [ + { LISTITEM: [ + { TEXT_LEAF: [ ] } + ] }, + ] }, + { LISTITEM: [ + { TEXT_LEAF: [ ] } + ] }, + ] }; + testAccessibleTree("container", accTree); + + // Test paragraph with display: contents. It should create a generic + // Accessible that reports the role correctly. + accTree = + { SECTION: [ + { PARAGRAPH: [ { TEXT_LEAF: [ ] } ] }, + { TEXT_LEAF: [ ] }, // space between paragraphs + { TEXT_LEAF: [ ] }, + ] }; + testAccessibleTree("paragraphContainer", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Element with ARIA role and display: contents doesn't get an accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1494196"> + Mozilla Bug 1494196 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="gridWithoutDisplayContents" role="grid"> + <div role="row">
+ <div role="columnheader">col1</div>
+ <div role="columnheader">col2</div>
+ </div>
+ <div role="row">
+ <div role="rowheader">row1</div>
+ <div role="gridcell">cell1</div>
+ </div> + </div> + <div id="gridWithDisplayContents" role="grid" style="display:contents;"> + <div role="row">
+ <div role="columnheader">col1</div>
+ <div role="columnheader">col2</div>
+ </div>
+ <div role="row">
+ <div role="rowheader">row1</div>
+ <div role="gridcell">cell1</div>
+ </div> + </div> + <div id="gridWithDisplayContentsRow" role="grid"> + <div role="row" style="display:contents;">
+ <div role="columnheader">col1</div>
+ <div role="columnheader">col2</div>
+ </div>
+ <div role="row">
+ <div role="rowheader">row1</div>
+ <div role="gridcell">cell1</div>
+ </div> + </div> + <div id="gridWithDisplayContentsColHeader" role="grid"> + <div role="row">
+ <div role="columnheader" style="display:contents;">col1</div>
+ <div role="columnheader">col2</div>
+ </div>
+ <div role="row">
+ <div role="rowheader">row1</div>
+ <div role="gridcell">cell1</div>
+ </div> + </div> + <div id="gridWithDisplayContentsRowHeader" role="grid"> + <div role="row">
+ <div role="columnheader">col1</div>
+ <div role="columnheader">col2</div>
+ </div>
+ <div role="row">
+ <div role="rowheader" style="display:contents;">row1</div>
+ <div role="gridcell">cell1</div>
+ </div> + </div> + <div id="gridWithDisplayContentsGridCell" role="grid"> + <div role="row">
+ <div role="columnheader">col1</div>
+ <div role="columnheader">col2</div>
+ </div>
+ <div role="row">
+ <div role="rowheader">row1</div>
+ <div role="gridcell" style="display:contents;">cell1</div>
+ </div> + </div> + + <div id="container"> + <div role="list" style="display: contents;"> + <div role="listitem">test</div> + </div> + <div aria-label="test" style="display: contents;"> + <div role="listitem">test</div> + </div> + <div role="none" style="display: contents;"> + <div role="listitem">test</div> + </div> + </div> + + <div id="paragraphContainer"> + <p style="display: contents;">test</p> + <p style="display: contents;" role="none">test</p> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_globals.html b/accessible/tests/mochitest/tree/test_aria_globals.html new file mode 100644 index 0000000000..bb5fe14cdf --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_globals.html @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test Global ARIA States and Accessible Creation</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + var globalIds = [ + "atomic", + "busy", + "controls", + "describedby", + "description", + "disabled", + "dropeffect", + "flowto", + "grabbed", + "haspopup", + "invalid", + "label", + "labelledby", + "live", + "owns", + "relevant", + ]; + + // Elements having ARIA global state or properties or referred by another + // element must be accessible. + ok(isAccessible("pawn"), + "Must be accessible because referred by another element."); + + for (let idx = 0; idx < globalIds.length; idx++) { + ok(isAccessible(globalIds[idx]), + "Must be accessible becuase of aria-" + globalIds[idx] + + " presence"); + } + + // Unfocusable elements, having ARIA global state or property with a valid + // IDREF value, and an inherited presentation role. A generic accessible + // is created (to prevent table cells text jamming). + ok(!isAccessible("td_nothing", nsIAccessibleTableCell), + "inherited presentation role takes a place"); + + for (let idx = 0; idx < globalIds.length; idx++) { + ok(isAccessible("td_" + globalIds[idx]), + "Inherited presentation role must be ignored becuase of " + + "aria-" + globalIds[idx] + " presence"); + } + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Update universal ARIA attribute support to latest spec" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=551978"> + Mozilla Bug 551978 + </a> + <a target="_blank" + title="Presentational table related elements referred or having global ARIA attributes must be accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=809751"> + Mozilla Bug 809751 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- Test that global aria states and properties are enough to cause the + creation of accessible objects --> + <div id="global_aria_states_and_props" role="group"> + <span id="pawn"></span> + <span id="atomic" aria-atomic="true"></span> + <span id="busy" aria-busy="false"></span> + <span id="controls" aria-controls="pawn"></span> + <span id="describedby" aria-describedby="pawn"></span> + <span id="description" aria-description="hi"></span> + <span id="disabled" aria-disabled="true"></span> + <span id="dropeffect" aria-dropeffect="move"></span> + <span id="flowto" aria-flowto="pawn"></span> + <span id="grabbed" aria-grabbed="false"></span> + <span id="haspopup" aria-haspopup="false"></span> + <span id="invalid" aria-invalid="false"></span> + <span id="label" aria-label="hi"></span> + <span id="labelledby" aria-labelledby="label"></span> + <span id="live" aria-live="polite"></span> + <span id="owns" aria-owns="pawn"></span> + <span id="relevant" aria-relevant="additions"></span> + </div> + + <table role="presentation"> + <tr> + <td id="td_nothing"></td> + <td id="td_atomic" aria-atomic="true"></td> + <td id="td_busy" aria-busy="false"></td> + <td id="td_controls" aria-controls="pawn"></td> + <td id="td_describedby" aria-describedby="pawn"></td> + <td id="td_description" aria-description="hi"></td> + <td id="td_disabled" aria-disabled="true"></td> + <td id="td_dropeffect" aria-dropeffect="move"></td> + <td id="td_flowto" aria-flowto="pawn"></td> + <td id="td_grabbed" aria-grabbed="false"></td> + <td id="td_haspopup" aria-haspopup="false"></td> + <td id="td_invalid" aria-invalid="false"></td> + <td id="td_label" aria-label="hi"></td> + <td id="td_labelledby" aria-labelledby="label"></td> + <td id="td_live" aria-live="polite"></td> + <td id="td_owns" aria-owns="pawn"></td> + <td id="td_relevant" aria-relevant="additions"></td> + </tr> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_grid.html b/accessible/tests/mochitest/tree/test_aria_grid.html new file mode 100644 index 0000000000..80ff97095b --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_grid.html @@ -0,0 +1,318 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML table tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // grid having rowgroups + + var accTree = + { TABLE: [ + { GROUPING: [ + { ROW: [ + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("grid", accTree); + + // //////////////////////////////////////////////////////////////////////// + // strange grids (mix of ARIA and HTML tables) + + accTree = { + role: ROLE_TABLE, + children: [ + { // div@role="row" + role: ROLE_ROW, + tagName: "DIV", + children: [ + { // caption text leaf + role: ROLE_TEXT_LEAF, + name: "caption", + children: [ ], + }, + { // th generic accessible + role: ROLE_TEXT_CONTAINER, + children: [ + { // th text leaf + role: ROLE_TEXT_LEAF, + name: "header1", + children: [ ], + }, + ], + }, + { // td@role="columnheader" + role: ROLE_COLUMNHEADER, + name: "header2", + children: [ { TEXT_LEAF: [ ] } ], + }, + ], + }, + ], + }; + testAccessibleTree("strange_grid1", accTree); + + accTree = { + role: ROLE_TABLE, + children: [ + { // tr@role="row" + role: ROLE_ROW, + tagName: "TR", + children: [ + { // td implicit role="gridcell" + role: ROLE_GRID_CELL, + children: [ + { // td text leaf + role: ROLE_TEXT_LEAF, + name: "cell1", + children: [ ], + }, + ], + }, + { // td@role="gridcell" + role: ROLE_GRID_CELL, + name: "cell2", + children: [ { TEXT_LEAF: [ ] } ], + }, + ], + }, + ], + }; + testAccessibleTree("strange_grid2", accTree); + + accTree = { + role: ROLE_TABLE, + children: [ + { // div@role="row" + role: ROLE_ROW, + children: [ + { // div@role="gridcell" + role: ROLE_GRID_CELL, + children: [ + { // td generic accessible + role: ROLE_TEXT_CONTAINER, + children: [ + { // text leaf from presentational table + role: ROLE_TEXT_LEAF, + name: "cell3", + children: [ ], + }, + ], + }, + ], + }, + ], + }, + ], + }; + testAccessibleTree("strange_grid3", accTree); + + accTree = { + role: ROLE_TABLE, + children: [ + { // div@role="row" + role: ROLE_ROW, + children: [ + { // div@role="gridcell" + role: ROLE_GRID_CELL, + children: [ + { // table + role: ROLE_TABLE, + children: [ + { // tr + role: ROLE_ROW, + children: [ + { // td + role: ROLE_CELL, + children: [ + { // caption text leaf of presentational table + role: ROLE_TEXT_LEAF, + name: "caption", + children: [ ], + }, + { // td generic accessible + role: ROLE_TEXT_CONTAINER, + children: [ + { // td text leaf of presentational table + role: ROLE_TEXT_LEAF, + name: "cell4", + children: [ ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; + + testAccessibleTree("strange_grid4", accTree); + + // //////////////////////////////////////////////////////////////////////// + // grids that could contain whitespace accessibles but shouldn't. + + accTree = + { TREE_TABLE: [ + { ROW: [ + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("whitespaces-grid", accTree); + + // grids that could contain text container accessibles but shouldn't. + + accTree = + { TABLE: [ + { ROW: [ + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + { ROW: [ + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("gridWithPresentationalBlockElement", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Support ARIA role rowgroup" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=525909"> + Mozilla Bug 525909 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="grid" role="grid"> + <div role="rowgroup"> + <div role="row"> + <div role="gridcell">cell</div> + </div> + </div> + </div> + + <div id="strange_grid1" role="grid"> + <div role="row"> + <table role="presentation"> + <caption>caption</caption> + <tr> + <th>header1</th> + <td role="columnheader">header2</td> + </tr> + </table> + </div> + </div> + + <div id="strange_grid2" role="grid"> + <table role="presentation"> + <tr role="row"> + <td id="implicit_gridcell">cell1</td> + <td role="gridcell">cell2</td> + </tr> + </table> + </div> + + <div id="strange_grid3" role="grid"> + <div role="row"> + <div role="gridcell"> + <table role="presentation"> + <tr> + <td>cell3</td> + </tr> + </table> + </div> + </div> + </div> + + <div id="strange_grid4" role="grid"> + <div role="row"> + <div role="gridcell"> + <table> + <tr> + <td> + <table role="presentation"> + <caption>caption</caption> + <tr><td>cell4</td></tr> + </table> + </td> + </tr> + </table> + </div> + </div> + </div> + + <div role="treegrid" id="whitespaces-grid"> + <div role="row" aria-selected="false" tabindex="-1"> + <span role="gridcell">03:30PM-04:30PM</span> + <span role="gridcell" style="font-weight:bold;">test</span> + <span role="gridcell">a user1</span> + </div> + </div> + + <div id="gridWithPresentationalBlockElement" role="grid"> + <span style="display: block;"> + <div role="row"> + <div role="gridcell">Cell 1</div> + <div role="gridcell">Cell 2</div> + </div> + </span> + <span style="display: block;"> + <div role="row"> + <span style="display: block;"> + <div role="gridcell">Cell 3</div> + <div role="gridcell">Cell 4</div> + </span> + </div> + </span> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_imgmap.html b/accessible/tests/mochitest/tree/test_aria_imgmap.html new file mode 100644 index 0000000000..0cd4867cae --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_imgmap.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test usemap elements and ARIA</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + // gA11yEventDumpToConsole = true; + function doPreTest() { + waitForImageMap("imagemap", doTest); + } + + function doTest() { + var accTree = { + role: ROLE_IMAGE_MAP, + children: [ + { + role: ROLE_ENTRY, + name: "first name", + }, + { + role: ROLE_ENTRY, + name: "last name", + }, + { + role: ROLE_RADIOBUTTON, + name: "male", + }, + { + role: ROLE_RADIOBUTTON, + name: "female", + }, + { + role: ROLE_CHECKBUTTON, + name: "have bike", + }, + { + role: ROLE_EDITCOMBOBOX, + name: "bike model", + }, + { + role: ROLE_CHECKBUTTON, + name: "have car", + }, + { + role: ROLE_CHECKBUTTON, + name: "have airplane", + }, + { + role: ROLE_PUSHBUTTON, + name: "submit", + }, + ], + }; + + // Test image map tree structure, roles, and names. + testAccessibleTree("imagemap", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doPreTest); + </script> + +</head> +<body> + +<a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=548291" + title="Accessible tree of ARIA image maps"> +Mozilla Bug 548291 +</a> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"> +</pre> + +<img id="imagemap" src="../formimage.png" width="219" height="229" border="0" usemap="#ariaMap"> +<map id="ariaMap" name="ariaMap"> + <area id="t1" role="textbox" shape="rect" tabindex="0" alt="" title="first name" coords="4,20,108,48" href="#" /> + <area id="t2" role="textbox" shape="rect" alt="" title="last name" coords="111,21,215,50" href="#" /> + <area id="rb1" role="radio" aria-checked="true" shape="circle" alt="" title="male" coords="60,75,11" href="#" /> + <area id="rb2" role="radio" shape="circle" alt="" title="female" coords="73,94,11" href="#" /> + <area id="cb1" role="checkbox" aria-checked="true" shape="rect" alt="" title="have bike" coords="95,123,118,145" href="#" /> + <area id="cbox" role="combobox" shape="rect" alt="" title="bike model" coords="120,124,184,146" href="#" /> + <area id="cb2" role="checkbox" shape="rect" alt="" title="have car" coords="90,145,114,164" href="#" /> + <area id="cb3" role="checkbox" shape="rect" alt="" title="have airplane" coords="130,163,152,184" href="#" /> + <area id="b1" role="button" shape="rect" alt="" title="submit" coords="4,198,67,224" href="#" /> +</map> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_list.html b/accessible/tests/mochitest/tree/test_aria_list.html new file mode 100644 index 0000000000..f3642c801d --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_list.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html> +<head> + <title>ARIA lists</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // list + + var accTree = + { LIST: [ + { LISTITEM: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }; + + testAccessibleTree("list", accTree); + + // //////////////////////////////////////////////////////////////////////// + // strange list (mix of ARIA and HTML) + + accTree = { // div@role="list" + role: ROLE_LIST, + children: [ + { // li + role: ROLE_TEXT_CONTAINER, + children: [ + { // li text leaf + role: ROLE_TEXT_LEAF, + name: "item1", + children: [ ], + }, + ], + }, + { // li@role="listitem" + role: ROLE_LISTITEM, + children: [ + { // text leaf + role: ROLE_TEXT_LEAF, + name: "item2", + children: [ ], + }, + ], + }, + ], + }; + + testAccessibleTree("strange_list", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Build the context dependent tree" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=804461"> + Mozilla Bug 804461 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="list" role="list"> + <div role="listitem">item1</div> + </div> + + <div id="strange_list" role="list"> + <ul role="presentation"> + <li>item1</li> + <li role="listitem">item2</li> + </ul> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_menu.html b/accessible/tests/mochitest/tree/test_aria_menu.html new file mode 100644 index 0000000000..2f1f6645db --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_menu.html @@ -0,0 +1,91 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test accessible tree when ARIA role menuitem is used</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // Menuitem with no popup. + let tree = + { SECTION: [ // container + { MENUPOPUP: [ // menu + { MENUITEM: [ + { LISTITEM_MARKER: [] }, // bullet + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree("menu", tree); + + // Menuitem with explicit no popup. + tree = + { SECTION: [ // container + { MENUPOPUP: [ // menu + { MENUITEM: [ + { LISTITEM_MARKER: [] }, // bullet + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree("menu_nopopup", tree); + + // Menuitem with popup. + tree = + { SECTION: [ // container + { MENUPOPUP: [ // menu + { PARENT_MENUITEM: [ // menuitem with aria-haspopup="true" + { LISTITEM_MARKER: [] }, // bullet + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree("menu_popup", tree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=786566" + title="ARIA menuitem acting as submenu should have PARENT_MENUITEM role"> + Mozilla Bug 786566 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="menu"> + <ul role="menu"> + <li role="menuitem">Normal Menu</li> + </ul> + </div> + + <div id="menu_nopopup"> + <ul role="menu"> + <li role="menuitem" aria-haspopup="false">Menu with explicit no popup</li> + </ul> + </div> + + <div id="menu_popup"> + <ul role="menu"> + <li role="menuitem" aria-haspopup="true">Menu with popup</li> + </ul> + </div> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_owns.html b/accessible/tests/mochitest/tree/test_aria_owns.html new file mode 100644 index 0000000000..a01968521b --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_owns.html @@ -0,0 +1,197 @@ +<!DOCTYPE html> +<html> + +<head> + <title>@aria-owns attribute testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + // ////////////////////////////////////////////////////////////////////////// + // Tests + // ////////////////////////////////////////////////////////////////////////// + + // enableLogging("tree,verbose"); // debug stuff + + var gQueue = null; + + function doTest() { + var tree = + { SECTION: [ // t1_1 + { HEADING: [ // t1_2 + // no kids, no loop + ] }, + ] }; + testAccessibleTree("t1_1", tree); + + tree = + { SECTION: [ // t2_1 + { GROUPING: [ // t2_2 + { HEADING: [ // t2_3 + // no kids, no loop + ] }, + ] }, + ] }; + testAccessibleTree("t2_1", tree); + + tree = + { SECTION: [ // t3_3 + { GROUPING: [ // t3_1 + { NOTE: [ // t3_2 + { HEADING: [ // DOM child of t3_2 + // no kids, no loop + ] }, + ] }, + ] }, + ] }; + testAccessibleTree("t3_3", tree); + + tree = + { SECTION: [ // t4_1 + { GROUPING: [ // DOM child of t4_1, aria-owns ignored + // no kids, no loop + ] }, + ] }; + testAccessibleTree("t4_1", tree); + + tree = + { SECTION: [ // t5_1 + { GROUPING: [ // DOM child of t5_1 + { NOTE: [ // t5_2 + { HEADING: [ // DOM child of t5_2 + { FORM: [ // t5_3 + { TOOLTIP: [ // DOM child of t5_3 + // no kids, no loop + ]}, + ]}, + ]}, + ] }, + ] }, + ] }; + testAccessibleTree("t5_1", tree); + + tree = + { SECTION: [ // t6_1 + { RADIOBUTTON: [ ] }, + { CHECKBUTTON: [ ] }, // t6_3, rearranged by aria-owns + { PUSHBUTTON: [ ] }, // t6_2, rearranged by aria-owns + ] }; + testAccessibleTree("t6_1", tree); + + tree = + { SECTION: [ // ariaowns_container + { SECTION: [ // ariaowns_self + { SECTION: [ // ariaowns_uncle + ] }, + ] }, + ] }; + testAccessibleTree("ariaowns_container", tree); + + tree = + { TABLE: [ + { ROW: [ + { GRID_CELL: [ + { TEXT_LEAF: [] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [] }, + ] }, + ] }, + { ROW: [ + { GRID_CELL: [ + { TEXT_LEAF: [] }, + ] }, + { GRID_CELL: [ + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree("grid", tree); + + tree = + { SECTION: [ // presentation_owner + // Can't own ancestor, so no children. + ] }; + testAccessibleTree("presentation_owner", tree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + + </script> +</head> + +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- simple loop --> + <div id="t1_1" aria-owns="t1_2"></div> + <div id="t1_2" aria-owns="t1_1" role="heading"></div> + + <!-- loop --> + <div id="t2_2" aria-owns="t2_3" role="group"></div> + <div id="t2_1" aria-owns="t2_2"></div> + <div id="t2_3" aria-owns="t2_1" role="heading"></div> + + <!-- loop #2 --> + <div id="t3_1" aria-owns="t3_2" role="group"></div> + <div id="t3_2" role="note"> + <div aria-owns="t3_3" role="heading"></div> + </div> + <div id="t3_3" aria-owns="t3_1"></div> + + <!-- self loop --> + <div id="t4_1"><div aria-owns="t4_1" role="group"></div></div> + + <!-- natural and aria-owns hierarchy --> + <div id="t5_2" role="note"><div aria-owns="t5_3" role="heading"></div></div> + <div id="t5_1"><div aria-owns="t5_2" role="group"></div></div> + <div id="t5_3" role="form"><div aria-owns="t5_1" role="tooltip"></div></div> + + <!-- rearrange children --> + <div id="t6_1" aria-owns="t6_3 t6_2"> + <div id="t6_2" role="button"></div> + <div id="t6_3" role="checkbox"></div> + <div role="radio"></div> + </div> + + <div id="ariaowns_container"> + <div id="ariaowns_self" + aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div> + </div> + <div id="ariaowns_uncle"></div> + + <!-- grid --> + <div aria-owns="grid-row2" role="grid" id="grid"> + <div role="row"> + <div role="gridcell">cell 1,1</div> + <div role="gridcell">cell 1,2</div> + </div> + </div> + <div role="row" id="grid-row2"> + <div role="gridcell">cell 2,1</div> + <div role="gridcell">cell 2,2</div> + </div> + + <!-- Owned child which is an ancestor of its owner but didn't yet exist when + aria-owns relocation was processed (bug 1485097). --> + <div id="presentation" role="presentation"> + <div id="presentation_owner" aria-owns="presentation"></div> + </div> +</body> + +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_presentation.html b/accessible/tests/mochitest/tree/test_aria_presentation.html new file mode 100644 index 0000000000..5680193441 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_presentation.html @@ -0,0 +1,176 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test accessible tree when ARIA role presentation is used</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // Presentation role don't allow accessible. + var tree = + { SECTION: [ // container + { TEXT_LEAF: [ ] }, // child text of 'presentation' node + { TEXT_LEAF: [ ] }, // child text of 'none' node + ] }; + testAccessibleTree("div_cnt", tree); + + // Focusable element, 'presentation' and 'none' roles are ignored. + tree = + { SECTION: [ // container + { PUSHBUTTON: [ // button having 'presentation' role + { TEXT_LEAF: [ ] }, + ] }, + { PUSHBUTTON: [ // button having 'none' role + { TEXT_LEAF: [ ] }, + ] }, + ] }; + testAccessibleTree("btn_cnt", tree); + + // Presentation table, no table structure is exposed. + tree = + { SECTION: [ // container + { TEXT_CONTAINER: [ // td generic accessible inside 'presentation' table + { TEXT_LEAF: [ ] }, // cell text + ] }, + { TEXT_CONTAINER: [ // td generic accessible inside 'none' table + { TEXT_LEAF: [ ] }, // cell text + ] }, + ] }; + testAccessibleTree("tbl_cnt", tree); + + // Focusable table, 'presentation' and 'none' roles are ignored. + tree = + { SECTION: [ // container + { TABLE: [ // table having 'presentation' role + { ROW: [ // tr + { CELL: [ // td + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + { TABLE: [ // table having 'none' role + { ROW: [ // tr + { CELL: [ // td + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree("tblfocusable_cnt", tree); + + // Presentation list, expose generic accesisble for list items. + tree = + { SECTION: [ // container + { TEXT_CONTAINER: [ // li generic accessible inside 'presentation' role + { TEXT_LEAF: [ ] }, // li text + ] }, + { TEXT_CONTAINER: [ // li generic accessible inside 'none' role + { TEXT_LEAF: [ ] }, // li text + ] }, + ] }; + testAccessibleTree("list_cnt", tree); + + // Has ARIA globals or referred by ARIA relationship, role='presentation' + // and role='none' are ignored. + tree = + { SECTION: [ // container + { LABEL: [ // label, has aria-owns + { TEXT_LEAF: [ ] }, + { LABEL: [ // label, referenced by aria-owns + { TEXT_LEAF: [ ] }, + ] }, + ] }, + { LABEL: [ // label, has aria-owns + { TEXT_LEAF: [ ] }, + { LABEL: [ // label, referenced by aria-owns + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("airaglobalprop_cnt", tree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=548291" + title="Accessible tree of ARIA image maps"> + Bug 548291 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=666504" + title="Ignore role presentation on focusable elements"> + Bug 666504 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=971212" + title="Implement ARIA role=none"> + Bug 971212 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="div_cnt"><div role="presentation">t</div><div role="none">t</div></div> + + <div id="btn_cnt"><button role="presentation">btn</button><button role="none">btn</button></div> + + <div id="tbl_cnt"> + <table role="presentation"> + <tr> + <td>cell</td> + </tr> + </table> + <table role="none"> + <tr> + <td>cell</td> + </tr> + </table> + </div> + + <div id="tblfocusable_cnt"> + <table role="presentation" tabindex="0"> + <tr> + <td>cell</td> + </tr> + </table> + <table role="none" tabindex="0"> + <tr> + <td>cell</td> + </tr> + </table> + </div> + + <div id="list_cnt"> + <ul role="presentation"> + <li>item</li> + </ul> + <ul role="none"> + <li>item</li> + </ul> + </div> + + <div id="airaglobalprop_cnt"><label + role="presentation" aria-owns="ariaowned">has aria-owns</label><label + role="presentation" id="ariaowned">referred by aria-owns</label><label + role="none" aria-owns="ariaowned2">has aria-owns</label><label + role="none" id="ariaowned2">referred by aria-owns</label></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_aria_table.html b/accessible/tests/mochitest/tree/test_aria_table.html new file mode 100644 index 0000000000..22375faf59 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_aria_table.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<html> +<head> + <title>ARIA table tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // table having rowgroups + + var accTree = + { TABLE: [ + { GROUPING: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("table", accTree); + + // tables that could contain text container accessibles but shouldn't. + + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("tableWithPresentationalBlockElement", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="support ARIA table and cell roles" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1173364"> + Bug 1173364 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="table" role="table"> + <div role="rowgroup"> + <div role="row"> + <div role="cell">cell</div> + </div> + </div> + </div> + + <div id="tableWithPresentationalBlockElement" role="table"> + <span style="display: block;"> + <div role="row"> + <div role="cell">Cell 1</div> + <div role="cell">Cell 2</div> + </div> + </span> + <span style="display: block;"> + <div role="row"> + <span style="display: block;"> + <div role="cell">Cell 3</div> + <div role="cell">Cell 4</div> + </span> + </div> + </span> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_brokencontext.html b/accessible/tests/mochitest/tree/test_brokencontext.html new file mode 100644 index 0000000000..fbdefd398f --- /dev/null +++ b/accessible/tests/mochitest/tree/test_brokencontext.html @@ -0,0 +1,214 @@ +<!DOCTYPE html> +<html> +<head> + <title>Broken context hierarchy</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + /** + * Return true if TD element has a generic accessible. + */ + function isTDGeneric(aID) { + return isAccessible(aID) && !isAccessible(aID, nsIAccessibleTableCell); + } + + function checkIfNotAccessible(aID) { + ok(!isAccessible(aID), "'" + aID + "' shouldn't be accessible"); + } + function checkIfTDGeneric(aID) { + ok(isTDGeneric(aID), "'" + aID + "' shouldn't have cell accessible"); + } + + function doTest() { + // ////////////////////////////////////////////////////////////////////////// + // HTML table elements outside table context. + + // HTML table role="presentation" + checkIfNotAccessible("tr_in_presentation_table"); + checkIfTDGeneric("th_in_presentation_table"); + checkIfTDGeneric("td_in_presentation_table"); + + // HTML table role="button" + var tree = + { PUSHBUTTON: [ // table + { TEXT_CONTAINER: [ // tr + { TEXT_CONTAINER: [ // th + { TEXT_LEAF: [ ] }, + ] }, + { TEXT_CONTAINER: [ // td + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("button_table", tree); + + // ////////////////////////////////////////////////////////////////////////// + // HTML list elements outside list context. + + ok(!isAccessible("presentation_ul"), + "presentational ul shouldn't be accessible"); + ok(isAccessible("item_in_presentation_ul"), + "li in presentational ul should have generic accessible"); + ok(isAccessible("styleditem_in_presentation_ul"), + "list styled span in presentational ul should have generic accessible"); + + ok(!isAccessible("presentation_ol"), + "presentational ol shouldn't be accessible"); + ok(isAccessible("item_in_presentation_ol"), + "li in presentational ol should have generic accessible"); + + ok(!isAccessible("presentation_dl"), + "presentational dl shouldn't be accessible"); + ok(!isAccessible("dt_in_presentation_dl"), + "dt in presentational dl shouldn't be accessible"); + ok(!isAccessible("dd_in_presentation_dl"), + "dd in presentational dl shouldn't be accessible"); + + tree = + { PUSHBUTTON: [ // ul + { TEXT_CONTAINER: [ // li + { LISTITEM_MARKER: [ ] }, + { TEXT_LEAF: [ ] }, + ] }, + { TEXT_CONTAINER: [ // span styled as a list + { LISTITEM_MARKER: [ ] }, + { TEXT_LEAF: [ ] }, + ] }, + ] }; + testAccessibleTree("button_ul", tree); + + tree = + { PUSHBUTTON: [ // ol + { TEXT_CONTAINER: [ // li + { LISTITEM_MARKER: [ ] }, + { TEXT_LEAF: [ ] }, + ] }, + ] }; + testAccessibleTree("button_ol", tree); + + tree = + { PUSHBUTTON: [ // dl + { TEXT_CONTAINER: [ // dt + { TEXT_LEAF: [ ] }, + ] }, + { TEXT_CONTAINER: [ // dd + { TEXT_LEAF: [ ] }, + ] }, + ] }; + testAccessibleTree("button_dl", tree); + + // ////////////////////////////////////////////////////////////////////////// + // Styled as HTML table elements, accessible is created by tag name + + tree = + { LINK: [ // a + { TEXT_LEAF: [ ] }, + ] }; + testAccessibleTree("a_as_td", tree); + + tree = + { HEADING: [ + { TEXT_LEAF: [ ] }, + ] }; + testAccessibleTree("h1_as_td", tree); + testAccessibleTree("h2_as_td", tree); + testAccessibleTree("h3_as_td", tree); + testAccessibleTree("h4_as_td", tree); + testAccessibleTree("h5_as_td", tree); + testAccessibleTree("h6_as_td", tree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=706849" + title="Create accessible by tag name as fallback if table descendant style is used out of table context"> + Bug 706849 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=804461" + title="Build the context dependent tree "> + Bug 804461 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=945435" + title="Create generic accessible for td to not jamm the cell text"> + Bug 945435 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- HTML table elements out of table --> + <table role="presentation"> + <tr id="tr_in_presentation_table"> + <th id="th_in_presentation_table">not a header</th> + <td id="td_in_presentation_table">not a cell</td> + </tr> + </table> + + <table role="button" id="button_table"> + <tr id="tr_in_button_table"> + <th id="th_in_button_table">not a header</th> + <td id="td_in_button_table">not a cell</td> + </tr> + </table> + + <!-- HTML list elements out of list --> + <ul role="presentation" id="presentation_ul"> + <li id="item_in_presentation_ul">item</li> + <span id="styleditem_in_presentation_ul" + style="display:list-item">Oranges</span> + </ul> + + <ol role="presentation" id="presentation_ol"> + <li id="item_in_presentation_ol">item</li> + </ol> + + <dl role="presentation" id="presentation_dl"> + <dt id="dt_in_presentation_dl">term</dt> + <dd id="dd_in_presentation_dl">definition</dd> + </dl> + + <ul role="button" id="button_ul"> + <li id="item_in_button_ul">item</li> + <span id="styleditem_in_button_ul" + style="display:list-item">Oranges</span> + </ul> + + <ol role="button" id="button_ol"> + <li id="item_in_button_ul">item</li> + </ol> + + <dl role="button" id="button_dl"> + <dt id="dt_in_button_dl">term</ld> + <dd id="dd_in_button_dl">definition</dd> + </dl> + + <!-- styled as HTML table elements --> + <a id="a_as_td" style="display:table-cell;" href="http://www.google.com">Google</a> + <h1 id="h1_as_td" style="display: table-cell;">h1</h1> + <h2 id="h2_as_td" style="display: table-cell;">h2</h2> + <h3 id="h3_as_td" style="display: table-cell;">h3</h3> + <h4 id="h4_as_td" style="display: table-cell;">h4</h4> + <h5 id="h5_as_td" style="display: table-cell;">h5</h5> + <h6 id="h6_as_td" style="display: table-cell;">h6</h6> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_button.xhtml b/accessible/tests/mochitest/tree/test_button.xhtml new file mode 100644 index 0000000000..fec453b717 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_button.xhtml @@ -0,0 +1,83 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL button hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + ////////////////////////////////////////////////////////////////////////// + // button + + var accTree = { + role: ROLE_PUSHBUTTON, + name: "hello", + children: [ ] + }; + testAccessibleTree("button1", accTree); + + ////////////////////////////////////////////////////////////////////////// + // toolbarbutton + + accTree = { + role: ROLE_PUSHBUTTON, + name: "hello", + children: [ ] + }; + testAccessibleTree("button2", accTree); + + ////////////////////////////////////////////////////////////////////////// + // toolbarbutton with type="checkbox" + + accTree = { + role: ROLE_TOGGLE_BUTTON, + name: "hello", + children: [ ] + }; + testAccessibleTree("button3", accTree); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu'"> + Mozilla Bug 249292 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <button id="button1" label="hello"/> + <toolbarbutton id="button2" label="hello"/> + <toolbarbutton id="button3" type="checkbox" label="hello"/> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/tree/test_canvas.html b/accessible/tests/mochitest/tree/test_canvas.html new file mode 100644 index 0000000000..804bcc1f6e --- /dev/null +++ b/accessible/tests/mochitest/tree/test_canvas.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=495912 +--> +<head> + <title>File Input Control tests</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + var accTree = + { CANVAS: [ + { CHECKBUTTON: [] }, + { ENTRY: [] }, + ] }; + + testAccessibleTree("canvas", accTree); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Expose alternative content in Canvas element to ATs" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912">Mozilla Bug 495912</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <canvas id="canvas" tabindex="0"><input type="checkbox"><input></canvas> + + <script type="text/javascript"> + var c = document.getElementById("canvas"); + var cxt = c.getContext("2d"); + cxt.fillStyle = "#005500"; + cxt.fillRect(0, 0, 150, 75); + </script> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_combobox.xhtml b/accessible/tests/mochitest/tree/test_combobox.xhtml new file mode 100644 index 0000000000..36e4e716c9 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_combobox.xhtml @@ -0,0 +1,116 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + title="Accessible XUL menulist and textbox @autocomplete hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + ////////////////////////////////////////////////////////////////////////// + // menulist + + var selectedOptionChildren = []; + if (MAC) { + // checkmark is part of the Mac menu styling + selectedOptionChildren = [{ + role: ROLE_STATICTEXT, + children: [] + }]; + } + + var accTree = { + role: ROLE_COMBOBOX, + children: [ + { + role: ROLE_COMBOBOX_LIST, + children: [ + { + role: ROLE_COMBOBOX_OPTION, + children: selectedOptionChildren + }, + { + role: ROLE_COMBOBOX_OPTION, + children: [] + } + ] + } + ] + }; + + testAccessibleTree("menulist", accTree); + + ////////////////////////////////////////////////////////////////////////// + // textbox@type=autocomplete #1 (history) + + accTree = { + // html:input + role: ROLE_ENTRY, + children: [ + { + // #text + role: ROLE_TEXT_LEAF, + name: "http://mochi.test:8888/redirect-a11y.html", + children: [] + } + ], + }; + + testAccessibleTree("autocomplete", accTree); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu'"> + Mozilla Bug 249292 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=626660" + title="Cache rendered text on a11y side"> + Mozilla Bug 626660 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <menulist id="menulist"> + <menupopup> + <menuitem label="item"/> + <menuitem label="item"/> + </menupopup> + </menulist> + + <html:input is="autocomplete-input" + id="autocomplete" + value="http://mochi.test:8888/redirect-a11y.html"/> + </vbox> + </hbox> + +</window> diff --git a/accessible/tests/mochitest/tree/test_cssflexbox.html b/accessible/tests/mochitest/tree/test_cssflexbox.html new file mode 100644 index 0000000000..72fafba0a2 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_cssflexbox.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<html> + +<head> + <title>CSS flexbox tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // Ensure that flexbox ordering and absolute positioning do not affect + // the accessibility tree. + // Note that there is no accessible for a div with display:flex style. + var accTree = { + role: ROLE_SECTION, + children: [ + { // Bug 1277559. Button outside the flexed content + role: ROLE_PUSHBUTTON, + name: "Button", + }, + { // Visually first button in the 3 button row + role: ROLE_PUSHBUTTON, + name: "First", + }, + { // Flushed right third button in the 3 button row + role: ROLE_PUSHBUTTON, + name: "Second", + }, + { // Middle button in the 3 button row + role: ROLE_PUSHBUTTON, + name: "Third", + }, // end bug 1277559 + { // Bug 962558: DOM first, Order 2. + role: ROLE_PUSHBUTTON, + name: "two, tab first", + }, + { // DOM order second, flex order 1 + role: ROLE_PUSHBUTTON, + name: "one, tab second", + }, // end bug 962558 + ], + }; + testAccessibleTree("flex_elements", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="flex_elements"> + <button type="button">Button</button> + <div style="position: relative; display: flex; width: 200px;"> + <button type="button" style="order: 1">First</button> + <button type="button" style="order: 2; position: absolute; right: 0">Second</button> + <button type="button" style="order: 3">Third</button> + </div> + <div style="display: flex"> + <button id="two" style="order: 2">two, tab first</button> + <button id="one" style="order: 1">one, tab second</button> + </div> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_cssoverflow.html b/accessible/tests/mochitest/tree/test_cssoverflow.html new file mode 100644 index 0000000000..87898fef6c --- /dev/null +++ b/accessible/tests/mochitest/tree/test_cssoverflow.html @@ -0,0 +1,135 @@ +<html> + +<head> + <title>CSS overflow testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + a.link:focus { + overflow: scroll; + } + </style> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + function focusAnchor(aID) { + this.linkNode = getNode(aID); + this.link = getAccessible(this.linkNode); + + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode), + ]; + + this.invoke = function focusAnchor_invoke() { + this.linkNode.focus(); + }; + + this.check = function focusAnchor_check(aEvent) { + is(this.link, aEvent.accessible, + "Focus should be fired against new link accessible!"); + }; + + this.getID = function focusAnchor_getID() { + return "focus a:focus{overflow:scroll} #1"; + }; + } + + function tabAnchor(aID) { + this.linkNode = getNode(aID); + this.link = getAccessible(this.linkNode); + + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getAccessible, this.linkNode), + ]; + + this.invoke = function tabAnchor_invoke() { + synthesizeKey("VK_TAB", { shiftKey: false }); + }; + + this.check = function tabAnchor_check(aEvent) { + is(this.link, aEvent.accessible, + "Focus should be fired against new link accessible!"); + }; + + this.getID = function tabAnchor_getID() { + return "focus a:focus{overflow:scroll} #2"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Do tests + + var gQueue = null; + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + function doTests() { + // Shift+Tab not working, and a test timeout, bug 746977 + if (MAC) { + todo(false, "Shift+tab isn't working on OS X, needs to be disabled until bug 746977 is fixed!"); + SimpleTest.finish(); + return; + } + + gQueue = new eventQueue(); + + // CSS 'overflow: scroll' property setting and unsetting causes accessible + // recreation (and fire show/hide events). For example, the focus and + // blur of HTML:a with ':focus {overflow: scroll; }' CSS style causes its + // accessible recreation. The focus event should be fired on new + // accessible. + gQueue.push(new focusAnchor("a")); + gQueue.push(new tabAnchor("a2")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=591163" + title="mochitest for bug 413777: focus the a:focus {overflow: scroll;} shouldn't recreate HTML a accessible"> + Mozilla Bug 591163 + </a><br> + <a target="_blank" + title="Rework accessible tree update code" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> + Mozilla Bug 570275 + </a><br> + <a target="_blank" + title="Text control frames should accept dynamic changes to the CSS overflow property" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=686247"> + Mozilla Bug 686247 + </a><br> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="eventdump"></div> + + <div> + <a id="a" class="link" href="www">link</a> + </div> + <div> + <a id="a2" class="link" href="www">link2</a> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_display_contents.html b/accessible/tests/mochitest/tree/test_display_contents.html new file mode 100644 index 0000000000..8393a35b41 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_display_contents.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<html> + +<head> +<title>CSS display:contents tests</title> +<link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script type="application/javascript" + src="../common.js"></script> +<script type="application/javascript" + src="../role.js"></script> + +<script type="application/javascript"> +function doTest() { + let tree = + { LIST: [ + { LISTITEM: [ + { LISTITEM_MARKER: [] }, + { TEXT_LEAF: [] }, + ]}, + { LISTITEM: [ + { LISTITEM_MARKER: [] }, + { TEXT_LEAF: [] }, + ]}, + ] }; + testAccessibleTree("ul", tree); + + tree = + { TABLE: [ + { ROW: [ + { CELL: [{ TEXT_LEAF: [] } ] }, + { CELL: [{ TEXT_LEAF: [] } ] }, + ]}, + ] }; + testAccessibleTree("tableTableContents", tree); + testAccessibleTree("tableTrContents", tree); + testAccessibleTree("tableTdContents", tree); + + tree = + { TABLE: [ + { GROUPING : [ + { ROW: [ + { CELL: [{ TEXT_LEAF: [] } ] }, + { CELL: [{ TEXT_LEAF: [] } ] }, + ]}, + ]}, + ] }; + testAccessibleTree("tableTbodyContents", tree); + + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +addA11yLoadEvent(doTest); +</script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ul id="ul" style="display: contents;"> + <li>Supermarket 1</li> + <li>Supermarket 2</li> + </ul> + + <!-- The summary attribute in these tables ensures they are treated as data + tables. --> + <table id="tableTableContents" summary="summary" style="display: contents;"> + <tr><td>a</td><td>b</td></tr> + </table> + <table id="tableTrContents" summary="table" style="display: block;"> + <tr style="display: contents;"><td>a</td><td>b</td></tr> + </table> + <table id="tableTdContents" summary="summary"> + <tr> + <td style="display: contents;">a</td> + <td style="display: contents;">b</td> + </tr> + </table> + <table id="tableTbodyContents" summary="summary" style="display: block;"> + <tbody style="display: contents;"> + <tr><td>a</td><td>b</td></tr> + </tbody> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_divs.html b/accessible/tests/mochitest/tree/test_divs.html new file mode 100644 index 0000000000..24d610aef2 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_divs.html @@ -0,0 +1,351 @@ +<!DOCTYPE html> +<html> + +<head> +<title>div element creation tests</title> +<link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script type="application/javascript" + src="../common.js"></script> +<script type="application/javascript" + src="../role.js"></script> +<script type="application/javascript" + src="../attributes.js"></script> + +<script type="application/javascript"> +function getAccessibleDescendantFor(selector) { + return gAccService.getAccessibleDescendantFor(document.querySelector(selector)); +} + +function doTest() { + // All below test cases are wrapped in a div which always gets rendered. + // c1 through c10 are the containers, the actual test cases are inside. + + // c1: Expose the div with text content + let tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // inner div + children: [ + { TEXT_LEAF: [] }, + ], // end children inner div + }, // end inner div + ], // end children outer div + }; + testAccessibleTree("c1", tree); + + // c2: Only the outermost and innermost divs are exposed. + // The middle one is skipped. This is identical to the above tree. + testAccessibleTree("c2", tree); + + // c3: Make sure the inner div with ID is exposed, but the middle one is + // skipped. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // inner div + attributes: { id: "b" }, + children: [ + { TEXT_LEAF: [] }, + ], // end children inner div + }, // end inner div + ], // end children outer div + }; + testAccessibleTree("c3", tree); + + // c4: Expose all three divs including the middle one due to its ID. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // middle div + attributes: { id: "a" }, + children: [ + { role: ROLE_SECTION, // inner div + attributes: { id: "b" }, + children: [ + { TEXT_LEAF: [] }, + ], // end children inner div + }, // end inner div + ], // end children middle div + }, // end middle div + ], // end children outer div + }; + testAccessibleTree("c4", tree); + + // c5: Expose the inner div with class b due to its text contents. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // inner div with class and text + attributes: { class: "b" }, + children: [ + { TEXT_LEAF: [] }, + ], // end children inner div + }, // end inner div + ], // end children outer div + }; + testAccessibleTree("c5", tree); + + // c6: Expose the outer div due to its ID, and the two inner divs due to + // their text contents. Skip the middle one. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // first inner div + children: [ + { TEXT_LEAF: [] }, + ], // end children first inner div + }, // end first inner div + { role: ROLE_SECTION, // second inner div + children: [ + { TEXT_LEAF: [] }, + ], // end children second inner div + }, // end second inner div + ], // end children outer div + }; + testAccessibleTree("c6", tree); + + // c7: Expose all three divs including the middle one due to it being block + // breaking. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // middle div + children: [ + { TEXT_LEAF: [] }, // foo + { role: ROLE_SECTION, // inner div + children: [ + { TEXT_LEAF: [] }, // bar + ], // end children inner div + }, // end inner div + { TEXT_LEAF: [] }, // baz + ], // end children middle div + }, // end middle div + ], // end children outer div + }; + testAccessibleTree("c7", tree); + + // c8: Expose all divs due to them all being text block breakers. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // foo div + children: [ + { TEXT_LEAF: [] }, // foo + { role: ROLE_SECTION, // baz div + children: [ + { role: ROLE_SECTION, // bar div + children: [ + { TEXT_LEAF: [] }, // bar + ], // end children bar div + }, // end bar div + { TEXT_LEAF: [] }, // baz + ], // end children baz div + }, // end baz div + ], // end children foo div + }, // end foo div + ], // end children outer div + }; + testAccessibleTree("c8", tree); + + // c9: The same, but in a different nesting order. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // c div + children: [ + { role: ROLE_SECTION, // b div + children: [ + { role: ROLE_SECTION, // a div + children: [ + { TEXT_LEAF: [] }, // a + ], // end children a div + }, // end a div + { TEXT_LEAF: [] }, // b + ], // end children b div + }, // end b div + { TEXT_LEAF: [] }, // c + ], // end children c div + }, // end foo div + ], // end children outer div + }; + testAccessibleTree("c9", tree); + + // c10: Both inner divs must be exposed so there is a break after b. + tree = + { role: ROLE_SECTION, // outer div with ID + children: [ + { role: ROLE_SECTION, // first inner div + children: [ + { TEXT_LEAF: [] }, // a + { TEXT_LEAF: [] }, // b + ], // end children first inner div + }, // end first inner div + { role: ROLE_SECTION, // second inner div + children: [ + { TEXT_LEAF: [] }, // c + { TEXT_LEAF: [] }, // d + ], // end children second inner div + }, // end second inner div + ], // end children outer div + }; + testAccessibleTree("c10", tree); + + // c11: A div must be exposed if it contains a br element. + tree = + { role: ROLE_SECTION, // outer div + children: [ + { role: ROLE_SECTION, // First line + children: [ + { TEXT_LEAF: [] }, // text + ], // end children first line + }, // end first line + { role: ROLE_SECTION, // Second line + children: [ + { WHITESPACE: [] }, // whitespace + ], // end children second line + }, // end second line + { role: ROLE_SECTION, // Third line + children: [ + { TEXT_LEAF: [] }, // text + ], // end children third line + }, // end third line + ], // end children outer div + }; + testAccessibleTree("c11", tree); + + // c12: Div shouldn't be rendered if first/last child text node is invisible. + tree = + { role: ROLE_SECTION, // outer div + children: [ + { role: ROLE_PARAGRAPH, + children: [ + { TEXT_LEAF: [] }, // text + ], + }, + ], // end children outer div + }; + testAccessibleTree("c12", tree); + + // c13: Div should be rendered if there is an inline frame after/before + // invisible text nodes. + tree = + { role: ROLE_SECTION, // outer div + children: [ + { TEXT_LEAF: [] }, // l1 + { role: ROLE_SECTION, // l2 + children: [ + { TEXT_LEAF: [] }, // l2 + ], // end children l2 + }, + ], // end children outer div + }; + testAccessibleTree("c13", tree); + + // c14: Div should be rendered if it contains an inline-block. + tree = + { role: ROLE_SECTION, // outer div + children: [ + { TEXT_LEAF: [] }, // l1 + { role: ROLE_SECTION, // l2 + children: [ + { role: ROLE_PUSHBUTTON, + children: [ + { TEXT_LEAF: [] }, + ], + }, + ], // end children l2 + }, + ], // end children outer div + }; + testAccessibleTree("c14", tree); + + // c15: Div should be rendered if previous sibling is text. + tree = + { role: ROLE_SECTION, // outer div + children: [ + { TEXT_LEAF: [] }, // l1 + { SECTION: [] }, // Block break + { TEXT_LEAF: [] }, // l2 + ], // end children outer div + }; + testAccessibleTree("c15", tree); + + // Test getting descendants of unrendered nodes. + ok(!getAccessibleDescendantFor("#c16 > span"), + "Span has no accessible children"); + + ok(!getAccessibleDescendantFor("#c17 > span"), + "Span with relocated child should return null"); + + is(getAccessibleDescendantFor("#c12 > div").role, ROLE_PARAGRAPH, + "Descendant has correct role") + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); +addA11yLoadEvent(doTest); +</script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- Expose the div if it has plain text contents --> + <div id="c1"><div>foo</div></div> + + <!-- Expose the outer and inner div, skip the middle one. --> + <div id="c2"><div><div>foo</div></div></div> + + <!-- Expose the outer and inner divs due to the ID, but skip the middle one. --> + <div id="c3"><div><div id="b">foo</div></div></div> + + <!-- Expose all three divs and their IDs. --> + <div id="c4"><div id="a"><div id="b">foo</div></div></div> + + <!-- Expose outer and inner divs, due to text content, not class. --> + <div id="c5"><div class="a"><div class="b">foo</div></div></div> + + <!-- Expose the outer and two inner divs, skip the single middle one. --> + <div id="c6"><div><div>foo</div><div>bar</div></div></div> + + <!-- Expose all divs due to the middle one being block breaking. --> + <div id="c7"><div>foo<div>bar</div>baz</div></div> + + <!-- Expose all divs due to them all being text block breakers. --> + <div id="c8"><div>foo<div><div>bar</div>baz</div></div></div> + <div id="c9"><div><div><div>a</div>b</div>c</div></div> + + <!-- Both inner divs need to be rendered so there is a break after b. --> + <div id="c10"><div><b>a</b>b</div><div><b>c</b><b>d</b></div></div> + + <!-- Div must be rendered if it contains a br --> + <div id="c11"><div>first line.</div><div><br /></div><div>third line</div></div> + + <!-- Inner div shouldn't be rendered because although its first and last + children are text nodes, they are invisible. + --> + <div id="c12"><div> <p>Test</p> </div></div> + + <!-- Inner div should be rendered because despite the first/last invisible + text nodes, there is also an inline frame. + --> + <div id="c13">l1<div> <span>l2 </span> </div></div> + + <!-- Inner div should be rendered because it contains an inline-block. --> + <div id="c14">l1<div><button>l2</button></div></div> + + <!-- Inner div should be rendered because previous sibling is text. --> + <div id="c15">l1<div></div>l2</div> + + <div id="c16">hello <span></span> world</div> + + <div id="c17"><div aria-owns="c"></div>hello <span><button id="c">b</button></span> world</div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_dochierarchy.html b/accessible/tests/mochitest/tree/test_dochierarchy.html new file mode 100644 index 0000000000..4822cecb84 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_dochierarchy.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test document hierarchy</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + function doTest() { + // tabDoc and testDoc are different documents depending on whether test + // is running in standalone mode or not. + + var root = getRootAccessible(); + var tabDoc = window.parent ? + getAccessible(window.parent.document, [nsIAccessibleDocument]) : + getAccessible(document, [nsIAccessibleDocument]); + var testDoc = getAccessible(document, [nsIAccessibleDocument]); + var iframeDoc = getAccessible("iframe").firstChild. + QueryInterface(nsIAccessibleDocument); + + is(root.parentDocument, null, + "Wrong parent document of root accessible"); + ok(root.childDocumentCount >= 1, + "Wrong child document count of root accessible"); + + var tabDocumentFound = false; + for (var i = 0; i < root.childDocumentCount && !tabDocumentFound; i++) { + tabDocumentFound = root.getChildDocumentAt(i) == tabDoc; + } + ok(tabDocumentFound, + "Tab document not found in children of the root accessible"); + + is(tabDoc.parentDocument, root, + "Wrong parent document of tab document"); + is(tabDoc.childDocumentCount, 1, + "Wrong child document count of tab document"); + is(tabDoc.getChildDocumentAt(0), (tabDoc == testDoc ? iframeDoc : testDoc), + "Wrong child document at index 0 of tab document"); + + if (tabDoc != testDoc) { + is(testDoc.parentDocument, tabDoc, + "Wrong parent document of test document"); + is(testDoc.childDocumentCount, 1, + "Wrong child document count of test document"); + is(testDoc.getChildDocumentAt(0), iframeDoc, + "Wrong child document at index 0 of test document"); + } + + is(iframeDoc.parentDocument, (tabDoc == testDoc ? tabDoc : testDoc), + "Wrong parent document of iframe document"); + is(iframeDoc.childDocumentCount, 0, + "Wrong child document count of iframe document"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=592913" + title="Provide a way to quickly determine whether an accessible object is a descendant of a tab document"> + Mozilla Bug 592913 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <iframe src="about:mozilla" id="iframe"></iframe> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_dockids.html b/accessible/tests/mochitest/tree/test_dockids.html new file mode 100644 index 0000000000..b19a68624a --- /dev/null +++ b/accessible/tests/mochitest/tree/test_dockids.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test document hierarchy</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + // gA11yEventDumpToConsole = true; + // enableLogging("tree,verbose"); + function doTest() { + var tree = + { DOCUMENT: [ + { TEXT_CONTAINER: [ // head + { TEXT_CONTAINER: [ // link + { STATICTEXT: [] }, // generated content + { STATICTEXT: [] }, // generated content + ] }, + ] }, + { TEXT_LEAF: [ ] }, // body text + { ENTRY: [ ] }, // input under document element + { TEXT_CONTAINER: [ // link under document element + { TEXT_LEAF: [ ] }, // link content + { STATICTEXT: [ ] }, // generated content + { STATICTEXT: [ ] }, // generated content + ] }, + { LINK: [ // anchor under document element + { TEXT_LEAF: [ ] }, // anchor content + ] }, + ] }; + testAccessibleTree(getNode("iframe").contentDocument, tree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887" + title="Elements appended outside the body aren't accessible"> + Mozilla Bug 608887 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <iframe src="dockids.html" id="iframe"></iframe> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_filectrl.html b/accessible/tests/mochitest/tree/test_filectrl.html new file mode 100644 index 0000000000..f0cd5baa5b --- /dev/null +++ b/accessible/tests/mochitest/tree/test_filectrl.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=483573 +--> +<head> + <title>File Input Control tests</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + var accTree = { + role: ROLE_GROUPING, + children: [ + { + role: ROLE_PUSHBUTTON, + }, + { + role: ROLE_LABEL, + children: [ + { + role: ROLE_TEXT_LEAF, + }, + ], + }, + ], + }; + testAccessibleTree("filectrl", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Expose HTML5 video and audio elements' embedded controls through accessibility APIs" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=483573">Mozilla Bug 483573</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input type="file" id="filectrl" /> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_formctrl.html b/accessible/tests/mochitest/tree/test_formctrl.html new file mode 100644 index 0000000000..3046ec2bf7 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_formctrl.html @@ -0,0 +1,125 @@ +<!DOCTYPE html> +<html> + +<head> + <title>HTML form controls tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // input@type="checkbox" + var accTree = { + role: ROLE_CHECKBUTTON, + children: [ ], + }; + + testAccessibleTree("checkbox", accTree); + + // input@type="radio" + accTree = { + role: ROLE_RADIOBUTTON, + children: [ ], + }; + + testAccessibleTree("radio", accTree); + + // input@type="button" and input@type="submit" + // button + accTree = { + role: ROLE_PUSHBUTTON, + children: [ + { + role: ROLE_TEXT_LEAF, // XXX Bug 567203 + }, + ], + }; + + testAccessibleTree("btn1", accTree); + testAccessibleTree("submit", accTree); + testAccessibleTree("btn2", accTree); + + // input@type="image" + accTree = { + role: ROLE_PUSHBUTTON, + children: [ + { + role: ROLE_STATICTEXT, + }, + ], + }; + testAccessibleTree("image_submit", accTree); + + // input@type="range" + accTree = { SLIDER: [ ] }; + testAccessibleTree("range", accTree); + + // input@type="number" + accTree = { SPINBUTTON: [ ] }; + testAccessibleTree("number", accTree); + + // output + accTree = { + role: ROLE_STATUSBAR, + children: [ + { + role: ROLE_TEXT_LEAF, + }, + ], + }; + testAccessibleTree("output", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Fix O(n^2) access to all the children of a container" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"> + Bug 342045 + </a> + <a target="_blank" + title="add test for role of input type='image'" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=524521"> + Bug 524521 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036" + title="make HTML <output> accessible"> + Bug 558036 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764" + title="make HTML5 input@type=range element accessible"> + Bug 559764 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <input type="checkbox" id="checkbox"> + <input type="radio" id="radio"> + <input type="button" value="button" id="btn1"> + <button id="btn2">button</button> + + <input type="submit" id="submit"> + <input type="image" id="image_submit"> + <input type="range" id="range"> + <input type="number" id="number"> + <output id="output">1337</output> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_formctrl.xhtml b/accessible/tests/mochitest/tree/test_formctrl.xhtml new file mode 100644 index 0000000000..d85a97171f --- /dev/null +++ b/accessible/tests/mochitest/tree/test_formctrl.xhtml @@ -0,0 +1,129 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<!-- Firefox toolbar --> +<?xml-stylesheet href="chrome://browser/content/browser.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL checkbox and radio hierarchy tests"> + + <!-- Firefox toolbar --> + <script type="application/javascript" + src="chrome://browser/content/browser.js"/> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + // checkbox + var accTree = { + role: ROLE_CHECKBUTTON, + children: [ ] + }; + + testAccessibleTree("checkbox", accTree); + + // radiogroup + accTree = { + role: ROLE_RADIO_GROUP, + children: [ + { + role: ROLE_RADIOBUTTON, + children: [ ] + }, + { + role: ROLE_RADIOBUTTON, + children: [ ] + } + ] + }; + + testAccessibleTree("radiogroup", accTree); + + // toolbar + accTree = { + role: ROLE_TOOLBAR, + name: "My toolbar", + children: [ + { + role: ROLE_PUSHBUTTON, + name: "hello", + children: [ ] + } + ] + }; + + testAccessibleTree("toolbar", accTree); + + // toolbar + accTree = { + role: ROLE_TOOLBAR, + name: "My second toolbar", + children: [ + { + role: ROLE_PUSHBUTTON, + name: "hello", + children: [ ] + } + ] + }; + + testAccessibleTree("toolbar2", accTree); + + if (!SEAMONKEY) + testAccessibleTree("tb_customizable", { TOOLBAR: [] }); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045" + title="Fix O(n^2) access to all the children of a container"> + Mozilla Bug 342045 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <checkbox id="checkbox" label="checkbox"/> + <radiogroup id="radiogroup"> + <radio label="radio1"/> + <radio label="radio2"/> + </radiogroup> + <toolbar id="toolbar" toolbarname="My toolbar"> + <toolbarbutton id="button1" label="hello"/> + </toolbar> + <toolbar id="toolbar2" toolbarname="2nd" aria-label="My second toolbar"> + <toolbarbutton id="button2" label="hello"/> + </toolbar> + + <toolbar id="tb_customizable" customizable="true"/> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/tree/test_gencontent.html b/accessible/tests/mochitest/tree/test_gencontent.html new file mode 100644 index 0000000000..3cd5e35e9a --- /dev/null +++ b/accessible/tests/mochitest/tree/test_gencontent.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Generated content tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + .gentext:before { + content: "START" + } + .gentext:after { + content: "END" + } + </style> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // :before and :after pseudo styles + var accTree = { + role: ROLE_SECTION, + children: [ + { + role: ROLE_STATICTEXT, + name: "START", + }, + { + role: ROLE_TEXT_LEAF, + name: "MIDDLE", + }, + { + role: ROLE_STATICTEXT, + name: "END", + }, + ], + }; + + testAccessibleTree("gentext", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Clean up our tree walker" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=530081"> + Mozilla Bug 530081 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div class="gentext" id="gentext">MIDDLE</div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_groupbox.xhtml b/accessible/tests/mochitest/tree/test_groupbox.xhtml new file mode 100644 index 0000000000..ba873d68c8 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_groupbox.xhtml @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL groupbox hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + var accTree = + { GROUPING: [ + { LABEL: [ + { STATICTEXT: [ ] } + ] }, + { CHECKBUTTON: [ ] } + ] }; + testAccessibleTree("groupbox", accTree); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045" + title="Fix O(n^2) access to all the children of a container"> + Mozilla Bug 342045 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <groupbox id="groupbox"> + <label value="Some caption"/> + <checkbox label="some checkbox label" /> + </groupbox> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/tree/test_html_in_mathml.html b/accessible/tests/mochitest/tree/test_html_in_mathml.html new file mode 100644 index 0000000000..214658de4a --- /dev/null +++ b/accessible/tests/mochitest/tree/test_html_in_mathml.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML in MathML Tests</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // <label> is generally not valid in MathML and shouldn't create an HTMl + // label. + testAccessibleTree("invalidLabel", { + MATHML_MATH: [ { + TEXT: [ { + TEXT_LEAF: [] + } ], + } ], + }); + + // HTML is valid inside <mtext>, so <label> should create an HTMl label. + testAccessibleTree("validLabel", { + MATHML_MATH: [ { + MATHML_TEXT: [ { + LABEL: [ { + TEXT_LEAF: [] + } ], + } ], + } ], + }); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <math id="invalidLabel"> + <label>Text</label> + </math> + + <math id="validLabel"> + <mtext> + <label>Text</label> + </mtext> + </math> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_iframe.html b/accessible/tests/mochitest/tree/test_iframe.html new file mode 100644 index 0000000000..e0e691550b --- /dev/null +++ b/accessible/tests/mochitest/tree/test_iframe.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> +<head> + <title>Outer document accessible tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + var accTree = { + role: ROLE_INTERNAL_FRAME, + children: [ + { + role: ROLE_DOCUMENT, + }, + ], + }; + + testAccessibleTree("iframe", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Fix O(n^2) access to all the children of a container" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"> + Mozilla Bug 342045 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <iframe id="iframe" src="about:mozilla"> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_image.xhtml b/accessible/tests/mochitest/tree/test_image.xhtml new file mode 100644 index 0000000000..ee7a6eabb1 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_image.xhtml @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL textbox and textarea hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + var accTree = { + role: ROLE_GRAPHIC, + children: [] + }; + testAccessibleTree("image", accTree); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1403231" + title="Remove the image XBL binding"> + Mozilla Bug 1403231 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <image id="image" src="../moz.png" tooltiptext="hello"/> + </vbox> + </hbox> + +</window> diff --git a/accessible/tests/mochitest/tree/test_img.html b/accessible/tests/mochitest/tree/test_img.html new file mode 100644 index 0000000000..c2a7351a15 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_img.html @@ -0,0 +1,84 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML img tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + // gA11yEventDumpToConsole = true; + function doPreTest() { + waitForImageMap("imgmap", doTest); + } + + function doTest() { + // image map + var accTree = { + role: ROLE_IMAGE_MAP, + children: [ + { + role: ROLE_LINK, + children: [], + }, + { + role: ROLE_LINK, + children: [], + }, + ], + }; + + testAccessibleTree("imgmap", accTree); + + // img + accTree = { + role: ROLE_GRAPHIC, + children: [], + }; + + testAccessibleTree("img", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doPreTest); + </script> + +</head> +<body> + + <a target="_blank" + title="Fix O(n^2) access to all the children of a container" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"> + Mozilla Bug 342045 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <map name="atoz_map"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b" + coords="17,0,30,14" alt="b" shape="rect"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a" + coords="0,0,13,14" alt="a" shape="rect"> + </map> + + <img id="imgmap" width="447" height="15" + usemap="#atoz_map" + src="../letters.gif"> + + <img id="img" src="../moz.png"> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_invalid_img.xhtml b/accessible/tests/mochitest/tree/test_invalid_img.xhtml new file mode 100644 index 0000000000..3d02900cd8 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_invalid_img.xhtml @@ -0,0 +1,48 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>invalid html img</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script> + <![CDATA[ + function doTest() { + document.getElementsByTagName("img")[0].firstChild.data = "2"; + + var accTree = { + role: ROLE_GRAPHIC, + children: [], + }; + testAccessibleTree("the_img", accTree); + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> +</head> +<body> + + <a target="_blank" + title="use HyperTextAccessible for invalid img" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=852129"> + Mozilla Bug 852129 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <img id="the_img">1</img> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_invalidationlist.html b/accessible/tests/mochitest/tree/test_invalidationlist.html new file mode 100644 index 0000000000..bad908f3c8 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_invalidationlist.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test document hierarchy</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + function doTest() { + var tree = + {SECTION: [ + { role: ROLE_PUSHBUTTON, name: "Hello" }, + { SECTION: [ + { TEXT: [ { role: ROLE_TEXT_LEAF, name: "Hello" } ] }, + { role: ROLE_TEXT_LEAF, name: "World" } + ]} + ]}; + testAccessibleTree("container", tree); + dumpTree("container"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=673757" + title="Do not process invalidation list while tree is created"> + Mozilla Bug 673757 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container"> + <div role="button" aria-labelledby="a"></div> + <div> + <span id="a">Hello</span><span id="b">World</span> + </div> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_list.html b/accessible/tests/mochitest/tree/test_list.html new file mode 100644 index 0000000000..46acda1516 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_list.html @@ -0,0 +1,346 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML ul/li element tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function listItemTree(aBulletText, aName, aSubtree) { + var obj = { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_LISTITEM_MARKER, + name: aBulletText, + }, + { + role: ROLE_TEXT_LEAF, + name: aName, + }, + ], + }; + + if (aSubtree) + obj.children.push(aSubtree); + + return obj; + } + + function doTest() { + // list1 + var discAccTree = { + role: ROLE_LIST, + children: [ + new listItemTree(kDiscBulletText, "Oranges"), + new listItemTree(kDiscBulletText, "Apples"), + new listItemTree(kDiscBulletText, "Bananas"), + ], + }; + + testAccessibleTree("list1", discAccTree); + + // list2 + var circleAccTree = { + role: ROLE_LIST, + children: [ + new listItemTree(kCircleBulletText, "Oranges"), + new listItemTree(kCircleBulletText, "Apples"), + new listItemTree(kCircleBulletText, "Bananas"), + ], + }; + + testAccessibleTree("list2", circleAccTree); + + // list3 + var squareAccTree = { + role: ROLE_LIST, + children: [ + new listItemTree(kSquareBulletText, "Oranges"), + new listItemTree(kSquareBulletText, "Apples"), + new listItemTree(kSquareBulletText, "Bananas"), + ], + }; + + testAccessibleTree("list3", squareAccTree); + + // list4 + var nestedAccTree = { + role: ROLE_LIST, + children: [ + new listItemTree("1. ", "Oranges"), + new listItemTree("2. ", "Apples"), + new listItemTree("3. ", "Bananas", circleAccTree), + ], + }; + + testAccessibleTree("list4", nestedAccTree); + + // dl list + var tree = + { DEFINITION_LIST: [ // dl + { TERM: [ // dt + { TEXT_LEAF: [] }, + ] }, + { DEFINITION: [ // dd + { TEXT_LEAF: [] }, + ] }, + { TERM: [ // dt + { TEXT_LEAF: [] }, + ] }, + { DEFINITION: [ // dd + { TEXT_LEAF: [] }, + ] }, + ] }; + + testAccessibleTree("list5", tree); + + // dl list inside ordered list + tree = + { LIST: [ // ol + { LISTITEM: [ // li + { LISTITEM_MARKER: [ ] }, + { DEFINITION_LIST: [ // dl + { TERM: [ // dt + { TEXT_LEAF: [] }, + ] }, + { DEFINITION: [ // dd + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }, + ] }; + + testAccessibleTree("list6", tree); + + // li having no display:list-item style + tree = + { LIST: [ // ul + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + { TEXT_LEAF: [] }, + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree("list7", tree); + + tree = + { LIST: [ // ul + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree("list8", tree); + + // span having display:list-item style + testAccessibleTree("list9", discAccTree); + + // dl with div grouping dt/dd + tree = + { DEFINITION_LIST: [ // dl + { TERM: [ // dt + { TEXT_LEAF: [] }, + ] }, + { DEFINITION: [ // dd + { TEXT_LEAF: [] }, + ] }, + { TERM: [ // dt + { TEXT_LEAF: [] }, + ] }, + { DEFINITION: [ // dd + { TEXT_LEAF: [] }, + ] }, + ] }; + + testAccessibleTree("list10", tree); + + // list-style-image + testAccessibleTree("list11", discAccTree); + + // list-style: none + tree = + { LIST: [ // ul + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree("list12", tree); + + // ::marker with content + tree = { // ol + role: ROLE_LIST, + children: [ + { // li + role: ROLE_LISTITEM, + children: [ + { // ::marker content text and counter + role: ROLE_LISTITEM_MARKER, + name: "foo1", + }, + { + role: ROLE_TEXT_LEAF, + name: "Oranges", + }, + ], + }, + { // li + role: ROLE_LISTITEM, + children: [ + { // ::marker content text and counter + role: ROLE_LISTITEM_MARKER, + name: "foo2", + }, + { + role: ROLE_TEXT_LEAF, + name: "Apples", + }, + ], + }, + ], + }; + testAccessibleTree("list13", tree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Fix O(n^2) access to all the children of a container" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=342045"> + Mozilla Bug 342045 + </a> + <a target="_blank" + title="Wrong accessible is created for HTML:li having block display style" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=507555"> + Mozilla Bug 507555 + </a> + <a target="_blank" + title="Bullets of nested not ordered lists have one and the same character." + href="https://bugzilla.mozilla.org/show_bug.cgi?id=604587"> + Mozilla Bug 604587 + </a> + <a target="_blank" + title="Fix list bullets for DL list (crash [@ nsBulletFrame::GetListItemText])" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=629114"> + Mozilla Bug 629114 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ul id="list1"> + <li id="l1_li1">Oranges</li> + <li id="l1_li2">Apples</li> + <li id="l1_li3">Bananas</li> + </ul> + + <ul id="list2" style="list-style-type: circle"> + <li id="l2_li1">Oranges</li> + <li id="l2_li2">Apples</li> + <li id="l2_li3">Bananas</li> + </ul> + + <ul id="list3" style="list-style-type: square"> + <li id="l3_li1">Oranges</li> + <li id="l3_li2">Apples</li> + <li id="l3_li3">Bananas</li> + </ul> + + <ol id="list4"> + <li id="li4">Oranges</li> + <li id="li5">Apples</li> + <li id="li6">Bananas<ul> + <li id="n_li4">Oranges</li> + <li id="n_li5">Apples</li> + <li id="n_li6">Bananas</li> + </ul> + </li> + </ol> + + <dl id="list5"> + <dt>item1</dt><dd>description</dd> + <dt>item2</td><dd>description</dd> + </dl> + + <ol id="list6"> + <li> + <dl id="dl"> + <dt>item1</dt><dd>description</dd> + </dl> + </li> + </ol> + + <!-- display style different than list-item --> + <ul id="list7"> + <li id="l7_li1" style="display:inline-block;">Oranges</li> + <li id="l7_li2" style="display:inline-block;">Apples</li> + </ul> + + <ul id="list8"> + <li id="l8_li1" style="display:inline; float:right;">Oranges</li> + <li id="l8_li2" style="display:inline; float:right;">Apples</li> + </ul> + + <!-- list-item display style --> + <ul id="list9"> + <span id="l9_li1" style="display:list-item">Oranges</span> + <span id="l9_li2" style="display:list-item">Apples</span> + <span id="l9_li3" style="display:list-item">Bananas</span> + </ul> + + <!-- dl with div grouping dd/dt elements (bug 1476347) --> + <dl id="list10"> + <div><dt>item1</dt><dd>description</dd></div> + <div><dt>item2</td><dd>description</dd></div> + </dl> + + <!-- list-style-image --> + <ul id="list11" + style="list-style-type: none; list-style-image: url('../moz.png');"> + <li>Oranges</li> + <li>Apples</li> + <li>Bananas</li> + </ul> + + <!-- list-style: none --> + <ul id="list12" style="list-style: none;"> + <li>Oranges</li> + <li>Apples</li> + </ul> + + <!-- ::marker with content --> + <style> + #list13 li { + counter-increment: list13counter; + } + #list13 li::marker { + content: 'foo' counter(list13counter); + } + </style> + <ol id="list13"> + <li>Oranges</li> + <li>Apples</li> + </ol> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_map.html b/accessible/tests/mochitest/tree/test_map.html new file mode 100644 index 0000000000..72de628f9f --- /dev/null +++ b/accessible/tests/mochitest/tree/test_map.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML map accessible tree tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // map used as imagemap, not accessible + var accTree = + { SECTION: [ ] }; + + testAccessibleTree("imagemapcontainer", accTree); + + // map group. Imagemaps are inlines by default, so TEXT_CONTAINER. + accTree = + { TEXT_CONTAINER: [ + { PARAGRAPH: [ + { TEXT_LEAF: [ ] }, + { LINK: [ + { TEXT_LEAF: [ ] }, + ] }, + { TEXT_LEAF: [ ] }, + { LINK: [ + { TEXT_LEAF: [ ] }, + ] }, + { TEXT_LEAF: [ ] }, + ] }, + ] }; + + testAccessibleTree("mapgroup", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Map used for grouping is not accessible under certain circumstances" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=627718"> + Mozilla Bug 627718 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="imagemapcontainer"> + <map name="atoz_map"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b" + coords="17,0,30,14" alt="b" shape="rect"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a" + coords="0,0,13,14" alt="a" shape="rect"> + </map> + </div> + + <img id="imgmap" width="447" height="15" + usemap="#atoz_map" + src="../letters.gif"> + + <map id="mapgroup" title="Navigation Bar" name="mapgroup"> + <p> + [<a href="#how">Bypass navigation bar</a>] + [<a href="home.html">Home</a>] + </p> + </map> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_media.html b/accessible/tests/mochitest/tree/test_media.html new file mode 100644 index 0000000000..5d9fe0ef24 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_media.html @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=483573 +--> +<head> + <title>HTML5 audio/video tests</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + + async function loadAudioSource() { + /** + * Setting the source dynamically and wait for it to load, + * so we can test the accessibility tree of the control in its ready and + * stable state. + * + * See bug 1484048 comment 25 for discussion on how it switches UI when + * loading a statically declared source. + */ + const bufferA11yShown = waitForEvent( + EVENT_SHOW, + evt => evt.accessible.role == ROLE_TEXT_LEAF && + evt.accessible.indexInParent == 2 && + evt.accessible.parent.role == ROLE_STATUSBAR + ); + await new Promise(resolve => { + let el = document.getElementById("audio"); + el.addEventListener("canplaythrough", resolve, {once: true}); + el.src = "../bug461281.ogg"; + }); + // Wait for this to be reflected in the a11y tree. + await bufferA11yShown; + + // Give Fluent time to update the value of the scrubber asynchronously. + let scrubber = document.getElementById("audio") + .openOrClosedShadowRoot.getElementById("scrubber"); + await SimpleTest.promiseWaitForCondition(() => + scrubber.getAttribute("aria-valuetext") == "0:00 / 0:02"); + + doTest(); + } + + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // test the accessible tree + + var accTree = { + role: ROLE_GROUPING, + children: [ + { // start/stop button + role: ROLE_PUSHBUTTON, + name: "Play", + children: [], + }, + { // buffer bar + role: ROLE_STATUSBAR, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "Loading:", + }, + { + role: ROLE_TEXT_LEAF, + name: " ", + }, + { + role: ROLE_TEXT_LEAF, + // The name is the percentage buffered; e.g. "0%", "100%". + // We can't check it here because it might be different + // depending on browser caching. + }, + ], + }, + { // slider of progress bar + role: ROLE_SLIDER, + name: "Position", + value: "0:00 / 0:02", + children: [], + }, + { // mute button + role: ROLE_PUSHBUTTON, + name: "Mute", + children: [], + }, + { // slider of volume bar + role: ROLE_SLIDER, + children: [], + name: "Volume", + }, + ], + }; + testAccessibleTree("audio", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(loadAudioSource); + </script> +</head> +<body> + + <a target="_blank" + title="Expose HTML5 video and audio elements' embedded controls through accessibility APIs" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=483573">Mozilla Bug 483573</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <audio id="audio" controls="true"></audio> + + <div id="eventDump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_select.html b/accessible/tests/mochitest/tree/test_select.html new file mode 100644 index 0000000000..80c17b9ef4 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_select.html @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML select control tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + var accTree = { + role: ROLE_LISTBOX, + children: [ + { + role: ROLE_GROUPING, + children: [ + { + role: ROLE_STATICTEXT, + children: [ ], + }, + { + role: ROLE_OPTION, + children: [ + { + role: ROLE_TEXT_LEAF, + }, + ], + }, + { + role: ROLE_OPTION, + children: [ + { + role: ROLE_TEXT_LEAF, + }, + ], + }, + ], + }, + { + role: ROLE_OPTION, + children: [ + { + role: ROLE_TEXT_LEAF, + }, + ], + }, + ], + }; + testAccessibleTree("listbox", accTree); + + accTree = { + role: ROLE_COMBOBOX, + children: [ + { + role: ROLE_COMBOBOX_LIST, + children: [ + { + role: ROLE_GROUPING, + children: [ + { + role: ROLE_COMBOBOX_OPTION, + children: [ ], + }, + { + role: ROLE_COMBOBOX_OPTION, + children: [ ], + }, + ], + }, + { + role: ROLE_COMBOBOX_OPTION, + children: [ ], + }, + ], + }, + ], + }; + testAccessibleTree("combobox", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="remove all the code in #ifdef COMBO_BOX_WITH_THREE_CHILDREN" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=506616"> + Mozilla Bug 506616 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <select id="listbox" size="4"> + <optgroup label="Colors"> + <option>Red</option> + <option>Blue</option> + </optgroup> + <option>Animal</option> + </select> + + <select id="combobox"> + <optgroup label="Colors"> + <option>Red</option> + <option>Blue</option> + </optgroup> + <option>Animal</option> + </select> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_svg.html b/accessible/tests/mochitest/tree/test_svg.html new file mode 100644 index 0000000000..4a071d0f4a --- /dev/null +++ b/accessible/tests/mochitest/tree/test_svg.html @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> +<head> + <title>SVG Tree Tests</title> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // svgText + var accTree = { + role: ROLE_DIAGRAM, + children: [ + { + role: ROLE_TEXT_CONTAINER, + children: [ + { + role: ROLE_TEXT_LEAF, + }, + ], + }, + ], + }; + testAccessibleTree("svgText", accTree); + + // svg1 + accTree = { + role: ROLE_DIAGRAM, + children: [] + }; + testAccessibleTree("svg1", accTree); + + // svg2 + accTree = { + role: ROLE_DIAGRAM, + children: [ + { + role: ROLE_GROUPING, + children: [] + } + ] + }; + testAccessibleTree("svg2", accTree); + + // svg3 + accTree = { + role: ROLE_DIAGRAM, + children: [ + { + role: ROLE_GRAPHIC, + children: [] + } + ] + }; + testAccessibleTree("svg3", accTree); + + // svg4 + accTree = { + role: ROLE_DIAGRAM, + children: [ + { + role: ROLE_GROUPING, + children: [ + { + role: ROLE_GRAPHIC, + children: [] + } + ] + } + ] + }; + testAccessibleTree("svg4", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <svg id="svgText"> + <text>This is some text</text> + </svg> + + <!-- no accessible objects --> + <svg id="svg1"> + <g id="g1"> + <rect width="300" height="100" id="rect1" style="fill:#00f" /> + </g> + </svg> + + <svg id="svg2"> + <g id="g2"> + <title>g</title> + <rect width="300" height="100" id="rect2" style="fill:#00f" /> + </g> + </svg> + + <svg id="svg3"> + <g id="g3"> + <rect width="300" height="100" id="rect3" style="fill:#00f"> + <title>rect</title> + </rect> + </g> + </svg> + + <svg id="svg4"> + <g id="g4"> + <title>g</title> + <rect width="300" height="100" id="rect4" style="fill:#00f"> + <title>rect</title> + </rect> + </g> + </svg> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_tabbox.xhtml b/accessible/tests/mochitest/tree/test_tabbox.xhtml new file mode 100644 index 0000000000..27e9222873 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_tabbox.xhtml @@ -0,0 +1,108 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL tabbox hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + ////////////////////////////////////////////////////////////////////////// + // tabbox + + var accTree = { + role: ROLE_PAGETABLIST, + children: [ + { + role: ROLE_PAGETAB, + children: [ + { + role: ROLE_STATICTEXT, + children: [], + } + ] + }, + { + role: ROLE_PAGETAB, + children: [ + { + role: ROLE_STATICTEXT, + children: [], + } + ] + } + ] + }; + testAccessibleTree("tabs", accTree); + + accTree = { + role: ROLE_PANE, + children: [ + { + role: ROLE_PROPERTYPAGE, + children: [] + }, + { + role: ROLE_PROPERTYPAGE, + children: [] + } + ] + }; + testAccessibleTree("tabpanels", accTree); + + SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389" + title=" WARNING: Bad accessible tree!: [tabbrowser tab] "> + Mozilla Bug 540389 + </a><br/> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944" + title="No relationship between tabs and associated property page in new tabbrowser construct"> + Mozilla Bug 552944 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <tabbox> + <tabs id="tabs"> + <tab label="tab1"/> + <tab label="tab2"/> + </tabs> + <tabpanels id="tabpanels"> + <tabpanel/> + <tabpanel/> + </tabpanels> + </tabbox> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/tree/test_tabbrowser.xhtml b/accessible/tests/mochitest/tree/test_tabbrowser.xhtml new file mode 100644 index 0000000000..401ea1a2b1 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_tabbrowser.xhtml @@ -0,0 +1,261 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL tabbrowser hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + <script type="application/javascript" + src="../browser.js"></script> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // invoker + function testTabHierarchy() + { + this.eventSeq = [ + new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 0), + new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1) + ]; + + this.invoke = function testTabHierarchy_invoke() + { + var docURIs = ["about:license", "about:mozilla"]; + tabBrowser().loadTabs(docURIs, { + inBackground: false, + replace: true, + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + // Flush layout, so as to guarantee that the a11y tree is constructed. + browserDocument().documentElement.getBoundingClientRect(); + } + + this.finalCheck = function testTabHierarchy_finalCheck(aEvent) + { + //////////////////// + // Tab bar + //////////////////// + var tabsAccTree = { + // xul:tabs + role: ROLE_PAGETABLIST, + children: [ + // Children depend on application (UI): see below. + ] + }; + + // SeaMonkey and Firefox tabbrowser UIs differ. + if (SEAMONKEY) { + SimpleTest.ok(true, "Testing SeaMonkey tabbrowser UI."); + + tabsAccTree.children.splice(0, 0, + { + // xul:toolbarbutton ("Open a new tab") + role: ROLE_PUSHBUTTON, + children: [] + }, + { + // xul:tab ("about:license") + role: ROLE_PAGETAB, + children: [] + }, + { + // tab ("about:mozilla") + role: ROLE_PAGETAB, + children: [] + }, + { + // xul:toolbarbutton ("List all tabs") + role: ROLE_PUSHBUTTON, + children: [ + { + // xul:menupopup + role: ROLE_MENUPOPUP, + children: [] + } + ] + }, + { + // xul:toolbarbutton ("Close current tab") + role: ROLE_PUSHBUTTON, + children: [] + } + ); + } else { + SimpleTest.ok(true, "Testing Firefox tabbrowser UI."); + let newTabChildren = []; + if (SpecialPowers.getBoolPref("privacy.userContext.enabled")) { + newTabChildren = [ + { + role: ROLE_MENUPOPUP, + children: [] + } + ]; + } + + // NB: The (3) buttons are not visible, unless manually hovered, + // probably due to size reduction in this test. + tabsAccTree.children.splice(0, 0, + { + // xul:tab ("about:license") + role: ROLE_PAGETAB, + children: [ + { + // xul:text, i.e. the tab label text + role: ROLE_TEXT_LEAF, + children: [] + } + ] + }, + { + // tab ("about:mozilla") + role: ROLE_PAGETAB, + children: [ + { + // xul:text, i.e. the tab label text + role: ROLE_TEXT_LEAF, + children: [] + } + ] + }, + { + // xul:toolbarbutton ("Open a new tab") + role: ROLE_PUSHBUTTON, + children: newTabChildren + } + // "List all tabs" dropdown + // XXX: This child(?) is not present in this test. + // I'm not sure why (though probably expected). + ); + } + + testAccessibleTree(tabBrowser().tabContainer, tabsAccTree); + + //////////////////// + // Tab contents + //////////////////// + var tabboxAccTree = { + // xul:tabpanels + role: ROLE_PANE, + children: [ + { + // xul:notificationbox + role: ROLE_PROPERTYPAGE, + children: [ + { + // xul:browser + role: ROLE_INTERNAL_FRAME, + children: [ + { + // #document ("about:license") + role: ROLE_DOCUMENT + // children: [ ... ] // Ignore document content. + } + ] + } + ] + }, + { + // notificationbox + role: ROLE_PROPERTYPAGE, + children: [ + { + // browser + role: ROLE_INTERNAL_FRAME, + children: [ + { + // #document ("about:mozilla") + role: ROLE_DOCUMENT + // children: [ ... ] // Ignore document content. + } + ] + } + ] + }, + { + // notificationbox + role: ROLE_PROPERTYPAGE, + children: [ + { + // browser + role: ROLE_INTERNAL_FRAME, + children: [ + { + // #document ("about:newtab" preloaded) + role: ROLE_DOCUMENT + // children: [ ... ] // Ignore document content. + } + ] + } + ] + } + ] + }; + + testAccessibleTree(tabBrowser().tabbox.tabpanels, tabboxAccTree); + } + + this.getID = function testTabHierarchy_getID() + { + return "hierarchy of tabs"; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + gA11yEventDumpToConsole = true; + //enableLogging("tree,verbose,stack"); + + var gQueue = null; + function doTest() + { + SimpleTest.requestCompleteLog(); + + // Load documents into tabs and wait for docLoadComplete events caused by these + // documents load before we start the test. + gQueue = new eventQueue(); + + gQueue.push(new testTabHierarchy()); + gQueue.onFinish = function() { closeBrowserWindow(); } + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + openBrowserWindow(doTest); + ]]> + </script> + + <vbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=540389" + title=" WARNING: Bad accessible tree!: [tabbrowser tab] "> + Mozilla Bug 540389 + </a><br/> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=552944" + title="No relationship between tabs and associated property page in new tabbrowser construct"> + Mozilla Bug 552944 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox id="eventdump"></vbox> + </vbox> + +</window> diff --git a/accessible/tests/mochitest/tree/test_table.html b/accessible/tests/mochitest/tree/test_table.html new file mode 100644 index 0000000000..5f34c12067 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_table.html @@ -0,0 +1,507 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML table tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // //////////////////////////////////////////////////////////////////////// + // tables having captions + + // Two captions, first is used, second is ignored. + var accTree = + { TABLE: [ + { CAPTION: [ + { + role: ROLE_TEXT_LEAF, + name: "caption", + }, + ] }, + { ROW: [ + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + ] }; + + testAccessibleTree("table", accTree); + + // One caption, empty text, caption is included. + accTree = + { TABLE: [ + { CAPTION: [ ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + ] }; + + testAccessibleTree("table_caption_empty", accTree); + + // Two captions, first has empty text, second is ignored. + accTree = + { TABLE: [ + { CAPTION: [ ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + ] }; + + testAccessibleTree("table_caption_firstempty", accTree); + + // One caption, placed in the end of table. In use. + accTree = + { TABLE: [ + { CAPTION: [ + { + role: ROLE_TEXT_LEAF, + name: "caption", + }, + ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + ] }; + + testAccessibleTree("table_caption_intheend", accTree); + + // One caption, collapsed to zero width and height. In use. + accTree = + { TABLE: [ + { CAPTION: [ + { + role: ROLE_TEXT_LEAF, + name: "caption", + }, + ] }, + { ROW: [ + { CELL: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ { TEXT_LEAF: [ ] } ] }, + ] }, + ] }; + + testAccessibleTree("table_caption_collapsed", accTree); + + // //////////////////////////////////////////////////////////////////////// + // table2 (consist of one column) + + accTree = { + role: ROLE_TABLE, + children: [ + { + role: ROLE_ROW, + children: [ + { + role: ROLE_COLUMNHEADER, + }, + ], + }, + { + role: ROLE_ROW, + children: [ + { + role: ROLE_CELL, + }, + ], + }, + ], + }; + + testAccessibleTree("table2", accTree); + + // //////////////////////////////////////////////////////////////////////// + // table3 (consist of one row) + + accTree = { + role: ROLE_TABLE, + children: [ + { + role: ROLE_ROW, + children: [ + { + role: ROLE_ROWHEADER, + }, + { + role: ROLE_CELL, + }, + ], + }, + ], + }; + + testAccessibleTree("table3", accTree); + + // /////////////////////////////////////////////////////////////////////// + // table4 (display: table-row) + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] } ], + }; + testAccessibleTree("table4", accTree); + + // /////////////////////////////////////////////////////////////////////// + // table5 (tbody with display: block should not get accessible) + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("table5", accTree); + + // /////////////////////////////////////////////////////////////////////// + // log table + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("logtable", accTree); + + // /////////////////////////////////////////////////////////////////////// + // display:block table + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("block_table", accTree); + + // /////////////////////////////////////////////////////////////////////// + // display:inline table + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("inline_table1", accTree); + + // /////////////////////////////////////////////////////////////////////// + // display:inline table + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree("table_containing_inlinetable", accTree); + + // /////////////////////////////////////////////////////////////////////// + // table with a cell that has display:block + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("table_containing_block_cell", accTree); + + // /////////////////////////////////////////////////////////////////////// + // A table with all elements being display:block, including a row group. + // This makes us fall back to the ARIAGridRowAccessible. + // Strange example from Gmail. + accTree = + { TABLE: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree("table_all_display_block", accTree); + + // /////////////////////////////////////////////////////////////////////// + // A table with a display:block tbody that has an aria role + // The tbody should get an accessible with the desired role. + accTree = + { TABLE: [ + { DIALOG: [ + { TEXT_CONTAINER: [ + { TEXT_CONTAINER: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree("table_with_block_tbody_and_role", accTree); + + // /////////////////////////////////////////////////////////////////////// + // A table with a display:block tbody that is focusable + // The tbody should get a grouping accessible. + accTree = + { TABLE: [ + { GROUPING: [ + { ROW: [ + { CELL: [ + { TEXT_LEAF: [ ] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree("table_with_focusable_block_tbody", accTree); + + // /////////////////////////////////////////////////////////////////////// + // Test that the CSS position property doesn't stop th elements from + // reporting the proper columnheader, rowheader roles. + accTree = + { TABLE: [ + { ROW: [ + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + { COLUMNHEADER: [ { TEXT_LEAF: [ ] } ] }, + ] }, + { ROW: [ + { ROWHEADER: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ ] }, + { ROWHEADER: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ ] }, + { ROWHEADER: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ ] }, + { ROWHEADER: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ ] }, + { ROWHEADER: [ { TEXT_LEAF: [ ] } ] }, + { CELL: [ ] }, + ] }, + ] }; + testAccessibleTree("table_containing_pos_styled_th", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="When a table has only one column per row and that column happens to be a column header its role is exposed wrong" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=529621"> + Mozilla Bug 529621 + </a> + <a target="_blank" + title="when div has display style table-row" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=727722"> + Mozilla Bug 727722 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <table id="table"> + <thead> + <tr> + <th>col1</th><th>col2</th> + </tr> + </thead> + <caption>caption</caption> + <tbody> + <tr> + <td>cell1</td><td>cell2</td> + </tr> + </tbody> + <tr> + <td>cell3</td><td>cell4</td> + </tr> + <caption>caption2</caption> + <tfoot> + <tr> + <td>cell5</td><td>cell6</td> + </tr> + </tfoot> + </table> + + <table id="table_caption_empty"> + <caption></caption> + <tr> + <td>cell1</td><td>cell2</td> + </tr> + </table> + + <table id="table_caption_firstempty"> + <caption></caption> + <tr> + <td>cell1</td><td>cell2</td> + </tr> + <caption>caption</caption> + </table> + + <table id="table_caption_intheend"> + <tr> + <td>cell1</td><td>cell2</td> + </tr> + <caption>caption</caption> + </table> + + <table id="table_caption_collapsed"> + <caption style="width: 0; height: 0">caption</caption> + <tr> + <td>cell1</td><td>cell2</td> + </tr> + </table> + <table id="table2"> + <thead> + <tr> + <th>colheader</th> + </tr> + </thead> + <tbody> + <tr> + <td>bla</td> + </tr> + </tbody> + </table> + + <table id="table3"> + <tr> + <th>rowheader</th> + <td>cell</td> + </tr> + </table> + + <table id="table4"> + <div style="display: table-row"> + <td>cell1</td> + </div> + </table> + + <table id="table5"> + <tbody style="display:block;"> + <tr> + <td>bla</td> + </tr> + </tbody> + </table> + + <table id="logtable" role="log"><tr><td>blah</td></tr></table> + + <table id="block_table" style="display: block;"> + <tr> + <td>bla</td> + </tr> + </table> + + <table id="inline_table1" border="1" style="display:inline"> + <tr> + <td>table1 cell1</td> + <td>table1 cell2</td> + </tr> + </table> + + <table id="table_containing_inlinetable"><tr><td> + <table id="inline_table2" border="1" style="display:inline"> + <tr id="tr_in_inline_table2"> + <td id="td_in_inline_table2">cell</td> + </tr> + </table> + </td></tr></table> + + <table id="table_containing_block_cell"> + <tr> + <td>Normal cell</td> + <td style="display: block;">Block cell</td> + </tr> + </table> + <table id="table_all_display_block" style="display:block;"> + <tbody style="display:block;"> + <tr style="display:block;"> + <td style="display:block;">text</td> + </tr> + </tbody> + </table> + + <table id="table_with_block_tbody_and_role"> + <tbody style="display:block;" role="dialog"> + <tr> + <td>text</td> + </tr> + </tbody> + </table> + + <table id="table_with_focusable_block_tbody"> + <tbody style="display:block;" tabindex="0"> + <tr> + <td>text</td> + </tr> + </tbody> + </table> + + <table id="table_containing_pos_styled_th"> + <tr> + <th style="position: static">static colheader</th> + <th style="position: relative">relative colheader</th> + <th style="position: absolute">absolute colheader</th> + <th style="position: fixed">fixed colheader</th> + <th style="position: sticky">sticky colheader</th> + </tr> + <tr> + <th style="position: static">static rowheader</th> + <td/> + <th style="position: relative">relative rowheader</th> + <td/> + <th style="position: absolute">absolute rowheader</th> + <td/> + <th style="position: fixed">fixed rowheader</th> + <td/> + <th style="position: sticky">sticky rowheader</th> + <td/> + </tr> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_table_2.html b/accessible/tests/mochitest/tree/test_table_2.html new file mode 100644 index 0000000000..c0faece7e5 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_table_2.html @@ -0,0 +1,242 @@ +<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + +<style> +.responsive-table { + width: 100%; + margin-bottom: 1.5em; +} +.responsive-table thead { + position: absolute; + clip: rect(1px 1px 1px 1px); + /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + padding: 0; + border: 0; + height: 1px; + width: 1px; + overflow: hidden; +} +.responsive-table thead th { + background-color: #1d96b2; + border: 1px solid #1d96b2; + font-weight: normal; + text-align: center; + color: white; +} +.responsive-table thead th:first-of-type { + text-align: left; +} +.responsive-table tbody, +.responsive-table tr, +.responsive-table th, +.responsive-table td { + display: block; + padding: 0; + text-align: left; + white-space: normal; +} +.responsive-table th, +.responsive-table td { + padding: .5em; + vertical-align: middle; +} +.responsive-table caption { + margin-bottom: 1em; + font-size: 1em; + font-weight: bold; + text-align: center; +} +.responsive-table tfoot { + font-size: .8em; + font-style: italic; +} +.responsive-table tbody tr { + margin-bottom: 1em; + border: 2px solid #1d96b2; +} +.responsive-table tbody tr:last-of-type { + margin-bottom: 0; +} +.responsive-table tbody th[scope="row"] { + background-color: #1d96b2; + color: white; +} +.responsive-table tbody td[data-type=currency] { + text-align: right; +} +.responsive-table tbody td[data-title]:before { + content: attr(data-title); + float: left; + font-size: .8em; + color: rgba(94, 93, 82, 0.75); +} +.responsive-table tbody td { + text-align: right; + border-bottom: 1px solid #1d96b2; +} + +.responsive-table { + font-size: .9em; +} +.responsive-table thead { + position: relative; + clip: auto; + height: auto; + width: auto; + overflow: auto; +} +.responsive-table tr { + display: table-row; +} +.responsive-table th, +.responsive-table td { + display: table-cell; + padding: .5em; +} + +.responsive-table caption { + font-size: 1.5em; +} +.responsive-table tbody { + display: table-row-group; +} +.responsive-table tbody tr { + display: table-row; + border-width: 1px; +} +.responsive-table tbody tr:nth-of-type(even) { + background-color: rgba(94, 93, 82, 0.1); +} +.responsive-table tbody th[scope="row"] { + background-color: transparent; + color: #5e5d52; + text-align: left; +} +.responsive-table tbody td { + text-align: center; +} +.responsive-table tbody td[data-title]:before { + content: none; +} +</style> + +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script type="application/javascript" + src="../common.js"></script> +<script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../table.js"></script> + +<script type="application/javascript"> + +const COLHEADER = ROLE_COLUMNHEADER; +const ROWHEADER = ROLE_ROWHEADER; +const CELL = ROLE_CELL; +const TEXT_LEAF = ROLE_TEXT_LEAF; + +function doTest() { + let accTree = + { TABLE: [ + { CAPTION: [ + { + role: ROLE_TEXT_LEAF, + name: "Top 10 Grossing Animated Films of All Time", + }, + ] }, + { TEXT_CONTAINER: [ + { ROW: [ + { role: COLHEADER, name: "Film Title" }, + { role: COLHEADER, name: "Released" }, + { role: COLHEADER, name: "Studio" }, + { role: COLHEADER, name: "Worldwide Gross" }, + { role: COLHEADER, name: "Domestic Gross" }, + { role: COLHEADER, name: "Foreign Gross" }, + { role: COLHEADER, name: "Budget" }, + ] }, + ] }, + { ROW: [ + { role: CELL }, + ] }, + { ROW: [ + { role: ROWHEADER, name: "Toy Story 3" }, + { CELL: [ { role: TEXT_LEAF, name: "2010" }] }, + { CELL: [ { role: TEXT_LEAF, name: "Disney Pixar" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$1,063,171,911" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$415,004,880" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$648,167,031" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$200,000,000" }] }, + ] }, + { ROW: [ + { role: ROWHEADER, name: "Shrek Forever After" }, + { CELL: [ { role: TEXT_LEAF, name: "2010" }] }, + { CELL: [ { role: TEXT_LEAF, name: "Dreamworks" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$752,600,867" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$238,736,787" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$513,864,080" }] }, + { CELL: [ { role: TEXT_LEAF, name: "$165,000,000" }] }, + ] }, + ] }; + + testAccessibleTree("table", accTree); + + SimpleTest.finish(); +} +SimpleTest.waitForExplicitFinish(); +addA11yLoadEvent(doTest); +</script> +</head> + +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <table class="responsive-table" id="table"> + <caption>Top 10 Grossing Animated Films of All Time</caption> + <thead> + <tr> + <th scope="col">Film Title</th> + <th scope="col">Released</th> + <th scope="col">Studio</th> + <th scope="col">Worldwide Gross</th> + <th scope="col">Domestic Gross</th> + <th scope="col">Foreign Gross</th> + <th scope="col">Budget</th> + </tr> + </thead> + <tfoot> + <tr> + <td colspan="7">Sources: <a href="http://en.wikipedia.org/wiki/List_of_highest-grossing_animated_films" rel="external">Wikipedia</a> & <a href="http://www.boxofficemojo.com/genres/chart/?id=animation.htm" rel="external">Box Office Mojo</a>. Data is current as of March 12, 2014</td> + </tr> + </tfoot> + <tbody> + <tr> + <th scope="row">Toy Story 3</th> + <td data-title="Released">2010</td> + <td data-title="Studio">Disney Pixar</td> + <td data-title="Worldwide Gross" data-type="currency">$1,063,171,911</td> + <td data-title="Domestic Gross" data-type="currency">$415,004,880</td> + <td data-title="Foreign Gross" data-type="currency">$648,167,031</td> + <td data-title="Budget" data-type="currency">$200,000,000</td> + </tr> + <tr> + <th scope="row">Shrek Forever After</th> + <td data-title="Released">2010</td> + <td data-title="Studio">Dreamworks</td> + <td data-title="Worldwide Gross" data-type="currency">$752,600,867</td> + <td data-title="Domestic Gross" data-type="currency">$238,736,787</td> + <td data-title="Foreign Gross" data-type="currency">$513,864,080</td> + <td data-title="Budget" data-type="currency">$165,000,000</td> + </tr> + </tbody> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_table_3.html b/accessible/tests/mochitest/tree/test_table_3.html new file mode 100644 index 0000000000..60af4d7f82 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_table_3.html @@ -0,0 +1,244 @@ +<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en"> +<html> +<head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> +<link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + +<style> +.responsive-table { + width: 100%; + margin-bottom: 1.5em; +} +.responsive-table thead { + position: absolute; + clip: rect(1px 1px 1px 1px); + /* IE6, IE7 */ + clip: rect(1px, 1px, 1px, 1px); + padding: 0; + border: 0; + height: 1px; + width: 1px; + overflow: hidden; +} +.responsive-table thead th { + background-color: #1d96b2; + border: 1px solid #1d96b2; + font-weight: normal; + text-align: center; + color: white; +} +.responsive-table thead th:first-of-type { + text-align: left; +} +.responsive-table tbody, +.responsive-table tr, +.responsive-table th, +.responsive-table td { + display: block; + padding: 0; + text-align: left; + white-space: normal; +} +.responsive-table th, +.responsive-table td { + padding: .5em; + vertical-align: middle; +} +.responsive-table caption { + margin-bottom: 1em; + font-size: 1em; + font-weight: bold; + text-align: center; +} +.responsive-table tfoot { + font-size: .8em; + font-style: italic; +} +.responsive-table tbody tr { + margin-bottom: 1em; + border: 2px solid #1d96b2; +} +.responsive-table tbody tr:last-of-type { + margin-bottom: 0; +} +.responsive-table tbody th[scope="row"] { + background-color: #1d96b2; + color: white; +} +.responsive-table tbody td[data-type=currency] { + text-align: right; +} +.responsive-table tbody td[data-title]:before { + content: attr(data-title); + float: left; + font-size: .8em; + color: #1d96b2; + font-weight: bold; +} +.responsive-table tbody td { + text-align: right; + border-bottom: 1px solid #1d96b2; +} + +/* float everything */ +.responsive-table tbody tr { + float: left; + width: 48%; + margin-left: 2%; +} +</style> + +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script type="application/javascript" + src="../common.js"></script> +<script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../table.js"></script> + +<script type="application/javascript"> + +const COLHEADER = ROLE_COLUMNHEADER; +const ROWHEADER = ROLE_ROWHEADER; +const CELL = ROLE_CELL; +const STATICTEXT = ROLE_STATICTEXT; +const TEXT_LEAF = ROLE_TEXT_LEAF; +const GROUPING = ROLE_GROUPING; + +function doTest() { + let accTree = + { TABLE: [ + { CAPTION: [ + { + role: ROLE_TEXT_LEAF, + name: "Top 10 Grossing Animated Films of All Time", + }, + ] }, + { TEXT_CONTAINER: [ + { ROW: [ + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Film Title" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Released" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Studio" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Worldwide Gross" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Domestic Gross" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Foreign Gross" } ] }, + { COLUMNHEADER: [ { role: TEXT_LEAF, name: "Budget" } ] }, + ] }, + ] }, + { ROW: [ + { role: CELL }, + ] }, + { ROW: [ + { ROWHEADER: [ { role: TEXT_LEAF, name: "Toy Story 3" } ] }, + { CELL: [ + { role: STATICTEXT, name: "Released" }, + { role: TEXT_LEAF, name: "2010" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Studio" }, + { role: TEXT_LEAF, name: "Disney Pixar" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Worldwide Gross" }, + { role: TEXT_LEAF, name: "$1,063,171,911" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Domestic Gross" }, + { role: TEXT_LEAF, name: "$415,004,880" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Foreign Gross" }, + { role: TEXT_LEAF, name: "$648,167,031" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Budget" }, + { role: TEXT_LEAF, name: "$200,000,000" }, + ]}, + ] }, + { ROW: [ + { ROWHEADER: [ { role: TEXT_LEAF, name: "Shrek Forever After" } ] }, + { CELL: [ + { role: STATICTEXT, name: "Released" }, + { role: TEXT_LEAF, name: "2010" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Studio" }, + { role: TEXT_LEAF, name: "Dreamworks" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Worldwide Gross" }, + { role: TEXT_LEAF, name: "$752,600,867" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Domestic Gross" }, + { role: TEXT_LEAF, name: "$238,736,787" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Foreign Gross" }, + { role: TEXT_LEAF, name: "$513,864,080" }, + ] }, + { CELL: [ + { role: STATICTEXT, name: "Budget" }, + { role: TEXT_LEAF, name: "$165,000,000" }, + ] }, + ] }, + ] }; + + testAccessibleTree("table", accTree); + + SimpleTest.finish(); +} +SimpleTest.waitForExplicitFinish(); +addA11yLoadEvent(doTest); +</script> +</head> + +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <table class="responsive-table" id="table"> + <caption>Top 10 Grossing Animated Films of All Time</caption> + <thead> + <tr> + <th scope="col">Film Title</th> + <th scope="col">Released</th> + <th scope="col">Studio</th> + <th scope="col">Worldwide Gross</th> + <th scope="col">Domestic Gross</th> + <th scope="col">Foreign Gross</th> + <th scope="col">Budget</th> + </tr> + </thead> + <tfoot> + <tr> + <td colspan="7">Sources: <a href="http://en.wikipedia.org/wiki/List_of_highest-grossing_animated_films" rel="external">Wikipedia</a> & <a href="http://www.boxofficemojo.com/genres/chart/?id=animation.htm" rel="external">Box Office Mojo</a>. Data is current as of March 12, 2014</td> + </tr> + </tfoot> + <tbody> + <tr> + <th scope="row">Toy Story 3</th> + <td data-title="Released">2010</td> + <td data-title="Studio">Disney Pixar</td> + <td data-title="Worldwide Gross" data-type="currency">$1,063,171,911</td> + <td data-title="Domestic Gross" data-type="currency">$415,004,880</td> + <td data-title="Foreign Gross" data-type="currency">$648,167,031</td> + <td data-title="Budget" data-type="currency">$200,000,000</td> + </tr> + <tr> + <th scope="row">Shrek Forever After</th> + <td data-title="Released">2010</td> + <td data-title="Studio">Dreamworks</td> + <td data-title="Worldwide Gross" data-type="currency">$752,600,867</td> + <td data-title="Domestic Gross" data-type="currency">$238,736,787</td> + <td data-title="Foreign Gross" data-type="currency">$513,864,080</td> + <td data-title="Budget" data-type="currency">$165,000,000</td> + </tr> + </tbody> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_tree.xhtml b/accessible/tests/mochitest/tree/test_tree.xhtml new file mode 100644 index 0000000000..e22b3faa9d --- /dev/null +++ b/accessible/tests/mochitest/tree/test_tree.xhtml @@ -0,0 +1,182 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL tree hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../treeview.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Accessible tree testers + + function getTreeItemAccTree(aTableRole, acolumnCount) + { + var treeItemRole; + switch (aTableRole) { + case ROLE_LIST: + treeItemRole = ROLE_LISTITEM; + break; + case ROLE_OUTLINE: + treeItemRole = ROLE_OUTLINEITEM; + break; + case ROLE_TABLE: case ROLE_TREE_TABLE: + treeItemRole = ROLE_ROW; + break; + } + + var accTree = { + role: treeItemRole, + children: [] + }; + + if (aTableRole == ROLE_TABLE || aTableRole == ROLE_TREE_TABLE) { + for (var idx = 0; idx < acolumnCount; idx++) { + var cellAccTree = { + role: ROLE_GRID_CELL, + children: [] + }; + accTree.children.push(cellAccTree); + } + } + + return accTree; + } + + function testAccessibleTreeFor(aTree, aRole) + { + var accTreeForColumns = { + role: ROLE_LIST, + children: [] + }; + + var accTreeForTree = { + role: aRole, + children: [ + accTreeForColumns + ] + }; + + var view = aTree.view; + var columnCount = aTree.columns.count; + + for (let idx = 0; idx < columnCount; idx++) + accTreeForColumns.children.push({ COLUMNHEADER: [ ] }); + if (!aTree.hasAttribute("hidecolumnpicker")) { + accTreeForColumns.children.push({ PUSHBUTTON: [ ] }); + accTreeForColumns.children.push({ MENUPOPUP: [ ] }); + } + + for (let idx = 0; idx < view.rowCount; idx++) + accTreeForTree.children.push(getTreeItemAccTree(aRole, columnCount)); + + testAccessibleTree(aTree, accTreeForTree); + } + + /** + * Event queue invoker object to test accessible tree for XUL tree element. + */ + function treeChecker(aID, aView, aRole) + { + this.DOMNode = getNode(aID); + + this.invoke = function invoke() + { + this.DOMNode.view = aView; + } + this.check = function check(aEvent) + { + testAccessibleTreeFor(this.DOMNode, aRole); + } + this.getID = function getID() + { + return "Tree testing of " + aID; + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "debug"; + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(EVENT_REORDER); + + gQueue.push(new treeChecker("list", new nsTableTreeView(3), ROLE_LIST)); + gQueue.push(new treeChecker("tree", new nsTreeTreeView(), ROLE_OUTLINE)); + gQueue.push(new treeChecker("table", new nsTableTreeView(3), ROLE_TABLE)); + gQueue.push(new treeChecker("treetable", new nsTreeTreeView(), ROLE_TREE_TABLE)); + + gQueue.invoke(); // Will call SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727" + title="Reorganize implementation of XUL tree accessibility"> + Mozilla Bug 503727 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <tree id="list" flex="1" hidecolumnpicker="true"> + <treecols> + <treecol id="col" flex="1" hideheader="true"/> + </treecols> + <treechildren/> + </tree> + + <tree id="tree" flex="1"> + <treecols> + <treecol id="col" flex="1" primary="true" label="column"/> + </treecols> + <treechildren/> + </tree> + + <tree id="table" flex="1"> + <treecols> + <treecol id="col1" flex="1" label="column"/> + <treecol id="col2" flex="1" label="column 2"/> + </treecols> + <treechildren/> + </tree> + + <tree id="treetable" flex="1"> + <treecols> + <treecol id="col1" flex="1" primary="true" label="column"/> + <treecol id="col2" flex="1" label="column 2"/> + </treecols> + <treechildren/> + </tree> + + <vbox id="debug"/> + </vbox> + </hbox> + +</window> diff --git a/accessible/tests/mochitest/tree/test_txtcntr.html b/accessible/tests/mochitest/tree/test_txtcntr.html new file mode 100644 index 0000000000..4c44701a41 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_txtcntr.html @@ -0,0 +1,234 @@ +<!DOCTYPE html> +<html> + +<head> + <title>HTML text containers tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + var accTree = { + role: ROLE_SECTION, + children: [ + { // text child + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("c1", accTree); + testAccessibleTree("c2", accTree); + + accTree = { + role: ROLE_SECTION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "Hello1", + }, + { + role: ROLE_WHITESPACE, + }, + { + role: ROLE_TEXT_LEAF, + name: "Hello2", + }, + { + role: ROLE_SEPARATOR, + }, + { + role: ROLE_TEXT_LEAF, + name: "Hello3 ", + }, + { + role: ROLE_PARAGRAPH, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "Hello4 ", + }, + ], + }, + ], + }; + + testAccessibleTree("c3", accTree); + + // contentEditable div + accTree = { + role: ROLE_SECTION, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "helllo ", + }, + { + role: ROLE_PARAGRAPH, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "blabla", + }, + ], + }, + { + role: ROLE_TEXT_LEAF, + name: "hello ", + }, + ], + }; + + testAccessibleTree("c4", accTree); + + // blockquote + accTree = { + role: ROLE_SECTION, + children: [ + { // block quote + role: ROLE_BLOCKQUOTE, + children: [ + { // text child + role: ROLE_TEXT_LEAF, + name: "Hello", + children: [], + }, + ], + }, + ], + }; + + testAccessibleTree("c5", accTree); + + // abbreviation tag + accTree = { + role: ROLE_SECTION, + children: [ + { // text leaf + role: ROLE_TEXT_LEAF, + name: "This ", + children: [], + }, + { // abbr tag + role: ROLE_TEXT, + name: "accessibility", + children: [ + { // text leaf with actual text + role: ROLE_TEXT_LEAF, + name: "a11y", + children: [], + }, + ], + }, + { // text leaf + role: ROLE_TEXT_LEAF, + name: " test", + children: [], + }, + ], + }; + + testAccessibleTree("c6", accTree); + + // acronym tag + accTree = { + role: ROLE_SECTION, + children: [ + { // text leaf + role: ROLE_TEXT_LEAF, + name: "This ", + children: [], + }, + { // acronym tag + role: ROLE_TEXT, + name: "personal computer", + children: [ + { // text leaf with actual text + role: ROLE_TEXT_LEAF, + name: "PC", + children: [], + }, + ], + }, + { // text leaf + role: ROLE_TEXT_LEAF, + name: " is broken", + children: [], + }, + ], + }; + + testAccessibleTree("c7", accTree); + + // only whitespace between images should be exposed + accTree = { + SECTION: [ + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + ], + }; + testAccessibleTree("c8", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="overflowed content doesn't expose child text accessibles" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306"> + Mozilla Bug 489306</a> + <a target="_blank" + title="Create child accessibles for text controls from native anonymous content" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824"> + Mozilla Bug 542824</a> + <a target="_blank" + title="Update accessible tree on content insertion after layout" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=498015"> + Mozilla Bug 498015</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1" style="width: 100px; height: 100px; overflow: auto;"> + 1hellohello 2hellohello 3hellohello 4hellohello 5hellohello 6hellohello 7hellohello + </div> + <div id="c2"> + 1hellohello 2hellohello 3hellohello 4hellohello 5hellohello 6hellohello 7hellohello + </div> + <div id="c3"> + Hello1<br> + Hello2<hr> + Hello3 + <p> + Hello4 + </p> + </div> + <div id="c4" contentEditable="true"> + helllo <p>blabla</p> hello + </div> + <div id="c5"><blockquote>Hello</blockquote></div> + <div id="c6">This <abbr title="accessibility">a11y</abbr> test</div> + <div id="c7">This <acronym title="personal computer">PC</acronym> is broken</div> + + <!-- Whitespace between images should be exposed. Whitespace between the + div and img tags will be inconsistent depending on the image cache + state and what optimizations layout was able to apply. --> + <div id="c8"><img src="../moz.png"> <img src="../moz.png"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_txtctrl.html b/accessible/tests/mochitest/tree/test_txtctrl.html new file mode 100644 index 0000000000..ee66fbf801 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_txtctrl.html @@ -0,0 +1,171 @@ +<!DOCTYPE html> +<html> + +<head> + <title>HTML text controls tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script type="application/javascript"> + function doTest() { + // editable div + var accTree = { + role: ROLE_SECTION, + children: [ + { // text child + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("txc1", accTree); + + // input@type="text", value + accTree = { + role: ROLE_ENTRY, + children: [ + { // text child + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("txc2", accTree); + + // input@type="text", no value + accTree = + { ENTRY: [ ] }; + + testAccessibleTree("txc3", accTree); + + // textarea + accTree = { + role: ROLE_ENTRY, + children: [ + { + role: ROLE_TEXT_LEAF, // hello1\nhello2 text + }, + ], + }; + + testAccessibleTree("txc4", accTree); + + // input@type="password" + accTree = { + role: ROLE_PASSWORD_TEXT, + children: [ + { + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("txc5", accTree); + + // input@type="tel", value + accTree = { + role: ROLE_ENTRY, + children: [ + { // text child + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("txc6", accTree); + + // input@type="email", value + accTree = { + role: ROLE_EDITCOMBOBOX, // Because of list attribute + children: [ + { // text child + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("txc7", accTree); + + // input@type="search", value + accTree = { + role: ROLE_EDITCOMBOBOX, // Because of list attribute + children: [ + { // text child + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("txc8", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="overflowed content doesn't expose child text accessibles" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=489306"> + Mozilla Bug 489306 + </a><br> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824" + title="Create child accessibles for text controls from native anonymous content"> + Mozilla Bug 542824 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652" + title="Make sure accessible tree is correct when rendered text is changed"> + Mozilla Bug 625652 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="txc1" contentEditable="true"> + 1hellohello + </div> + <input id="txc2" value="hello"> + <input id="txc3"> + <textarea id="txc4"> + hello1 + hello2 + </textarea> + <input id="txc5" type="password" value="hello"> + <input id="txc6" type="tel" value="4167771234"> + + Email Address: + <input id="txc7" type="email" list="contacts" value="xyzzy"> + <datalist id="contacts"> + <option>xyzzy@plughs.com</option> + <option>nobody@mozilla.org</option> + </datalist> + + </br>Search for: + <input id="txc8" type="search" list="searchhisty" value="Gamma"> + <datalist id="searchhisty"> + <option>Gamma Rays</option> + <option>Gamma Ray Bursts</option> + </datalist> + +</body> +</html> diff --git a/accessible/tests/mochitest/tree/test_txtctrl.xhtml b/accessible/tests/mochitest/tree/test_txtctrl.xhtml new file mode 100644 index 0000000000..ff3d4977f0 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_txtctrl.xhtml @@ -0,0 +1,86 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL textbox and textarea hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //gA11yEventDumpToConsole = true; + //enableLogging("tree,verbose"); // debug stuff + + //////////////////////////////////////////////////////////////////////////// + // Test + + function doTest() + { + ////////////////////////////////////////////////////////////////////////// + // search textbox + let accTree = + { GROUPING: [ + { ENTRY: [ { TEXT_LEAF: [] } ] }, + ] }; + testAccessibleTree("txc_search", accTree); + + ////////////////////////////////////////////////////////////////////////// + // search textbox with search button + + if (MAC) { + accTree = + { GROUPING: [ + { ENTRY: [ { TEXT_LEAF: [] } ] }, + ] }; + } else { + accTree = + { GROUPING: [ + { ENTRY: [ { TEXT_LEAF: [] } ] }, + { PUSHBUTTON: [] }, + ] }; + } + + testAccessibleTree("txc_search_searchbutton", accTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=542824" + title="Create child accessibles for text controls from native anonymous content"> + Mozilla Bug 542824 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <box role="group" id="txc_search"> + <search-textbox value="hello"/> + </box> + <box role="group" id="txc_search_searchbutton"> + <search-textbox searchbutton="true" value="hello"/> + </box> + </vbox> + </hbox> + +</window> diff --git a/accessible/tests/mochitest/tree/wnd.xhtml b/accessible/tests/mochitest/tree/wnd.xhtml new file mode 100644 index 0000000000..3b87cb5e0d --- /dev/null +++ b/accessible/tests/mochitest/tree/wnd.xhtml @@ -0,0 +1,8 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Empty Window"> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/a11y.ini b/accessible/tests/mochitest/treeupdate/a11y.ini new file mode 100644 index 0000000000..a40ef0ce05 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/a11y.ini @@ -0,0 +1,46 @@ +[DEFAULT] +support-files = + !/accessible/tests/mochitest/*.js + !/accessible/tests/mochitest/letters.gif + !/accessible/tests/mochitest/moz.png + +[test_ariadialog.html] +[test_ariahidden.html] +[test_ariaowns.html] +[test_bug852150.xhtml] +[test_bug883708.xhtml] +[test_bug884251.xhtml] +[test_bug895082.html] +[test_bug1040735.html] +[test_bug1175913.html] +[test_bug1189277.html] +[test_bug1276857.html] +support-files = test_bug1276857_subframe.html +[test_canvas.html] +[test_contextmenu.xhtml] +[test_cssoverflow.html] +[test_deck.xhtml] +[test_delayed_removal.html] +[test_doc.html] +[test_gencontent.html] +[test_general.html] +[test_hidden.html] +[test_imagemap.html] +[test_inert.html] +[test_inner_reorder.html] +[test_list.html] +[test_list_editabledoc.html] +[test_list_style.html] +[test_listbox.xhtml] +[test_menu.xhtml] +[test_menubutton.xhtml] +[test_optgroup.html] +[test_recreation.html] +[test_select.html] +[test_shadow_slots.html] +[test_shutdown.xhtml] +[test_table.html] +[test_textleaf.html] +[test_tooltip.xhtml] +[test_visibility.html] +[test_whitespace.html] diff --git a/accessible/tests/mochitest/treeupdate/test_ariadialog.html b/accessible/tests/mochitest/treeupdate/test_ariadialog.html new file mode 100644 index 0000000000..0b7f4bbb56 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_ariadialog.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Table creation in ARIA dialog test</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + function showARIADialog(aID) { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.node), + ]; + + this.invoke = function showARIADialog_invoke() { + getNode("dialog").style.display = "block"; + getNode("table").style.visibility = "visible"; + getNode("a").textContent = "link"; + getNode("input").value = "hello"; + getNode("input").focus(); + }; + + this.finalCheck = function showARIADialog_finalCheck() { + var tree = { + role: ROLE_DIALOG, + children: [ + { + role: ROLE_PUSHBUTTON, + children: [ { role: ROLE_TEXT_LEAF } ], + }, + { + role: ROLE_ENTRY, + }, + ], + }; + testAccessibleTree(aID, tree); + }; + + this.getID = function showARIADialog_getID() { + return "show ARIA dialog"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() { + // enableLogging("tree"); + gQueue = new eventQueue(); + + // make the accessible an inaccessible + gQueue.push(new showARIADialog("dialog")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Rework accessible tree update code" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> + Mozilla Bug 570275 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="dialog" role="dialog" style="display: none;"> + <table id="table" role="presentation" + style="display: block; position: absolute; top: 88px; left: 312.5px; z-index: 10010; visibility: hidden;"> + <tbody> + <tr> + <td role="presentation"> + <div role="presentation"> + <a id="a" role="button">text</a> + </div> + <input id="input"> + </td> + </tr> + </tbody> + </table> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_ariahidden.html b/accessible/tests/mochitest/treeupdate/test_ariahidden.html new file mode 100644 index 0000000000..302465b59f --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_ariahidden.html @@ -0,0 +1,118 @@ +<html> + +<head> + <title>aria-hidden tree update tests</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function t1_setARIAHidden() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "t1"), + ]; + + this.invoke = function t1_setARIAHidden_invoke() { + getNode("t1_child").setAttribute("aria-hidden", "true"); + }; + + this.finalCheck = function t1_setARIAHidden_finalCheck() { + ok(!isAccessible("t1_child"), "No accessible for aria-hidden"); + }; + + this.getID = function t1_setARIAHidden_getID() { + return "aria-hidden set to true"; + }; + } + + function t1_removeARIAHidden() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "t1"), + ]; + + this.invoke = function t1_removeARIAHidden_invoke() { + getNode("t1_child").removeAttribute("aria-hidden"); + }; + + this.finalCheck = function t1_removeARIAHidden_finalCheck() { + ok(isAccessible("t1_child"), "No aria-hidden, has to be accessible"); + }; + + this.getID = function t1_removeARIAHidden_getID() { + return "remove aria-hidden"; + }; + } + + function t2_setARIAHidden() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "t2"), + ]; + + this.invoke = function t2_setARIAHidden_invoke() { + getNode("t2_child").setAttribute("aria-hidden", "true"); + }; + + this.finalCheck = function t2_setARIAHidden_finalCheck() { + testAccessibleTree("t2", { SECTION: []}); + }; + + this.getID = function t2_setARIAHidden_getID() { + return "t2: set aria-hidden"; + }; + } + + function t2_insertUnderARIAHidden() { + this.eventSeq = [ + new unexpectedInvokerChecker(EVENT_REORDER, "t2"), + ]; + + this.invoke = function t2_insertUnderARIAHidden_invoke() { + getNode("t2_child").innerHTML = "<input>"; + }; + + this.finalCheck = function t2_insertUnderARIAHidden_finalCheck() { + testAccessibleTree("t2", { SECTION: []}); + }; + + this.getID = function t2_insertUnderARIAHidden_getID() { + return "t2: insert under aria-hidden"; + }; + } + + // gA11yEventDumpToConsole = true; + function doTests() { + ok(!isAccessible("t1_child"), "No accessible for aria-hidden"); + + const gQueue = new eventQueue(); + gQueue.push(new t1_removeARIAHidden()); + gQueue.push(new t1_setARIAHidden()); + gQueue.push(new t2_setARIAHidden()); + gQueue.push(new t2_insertUnderARIAHidden()); + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="t1"><div id="t1_child" aria-hidden="true">Hi</div><div>there</div></div> + <div id="t2"> + <span id="t2_child">hoho</span> + </div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_ariaowns.html b/accessible/tests/mochitest/treeupdate/test_ariaowns.html new file mode 100644 index 0000000000..89d49efd5f --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html @@ -0,0 +1,851 @@ +<!DOCTYPE html> +<html> + +<head> + <title>@aria-owns attribute testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + // ////////////////////////////////////////////////////////////////////////// + + function changeARIAOwns() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_button")), + // no hide for t1_subdiv because it is contained by hidden t1_checkbox + new invokerChecker(EVENT_HIDE, getNode("t1_checkbox")), + new invokerChecker(EVENT_SHOW, getNode("t1_checkbox")), + new invokerChecker(EVENT_SHOW, getNode("t1_button")), + new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")), + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + ]; + + this.invoke = function setARIAOwns_invoke() { + // children are swapped by ARIA owns + var tree = + { SECTION: [ + { CHECKBUTTON: [ + { SECTION: [] }, + ] }, + { PUSHBUTTON: [ ] }, + ] }; + testAccessibleTree("t1_container", tree); + + getNode("t1_container"). + setAttribute("aria-owns", "t1_button t1_subdiv"); + }; + + this.finalCheck = function setARIAOwns_finalCheck() { + // children are swapped again, button and subdiv are appended to + // the children. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // checkbox, native order + { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own + { SECTION: [ ] }, // subdiv from the subtree, ARIA owned + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function setARIAOwns_getID() { + return "Change @aria-owns attribute"; + }; + } + + function removeARIAOwns() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_button")), + new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")), + new orderChecker(), + new asyncInvokerChecker(EVENT_SHOW, getNode("t1_button")), + new asyncInvokerChecker(EVENT_SHOW, getNode("t1_subdiv")), + new orderChecker(), + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + new unexpectedInvokerChecker(EVENT_REORDER, getNode("t1_checkbox")), + ]; + + this.invoke = function removeARIAOwns_invoke() { + getNode("t1_container").removeAttribute("aria-owns"); + }; + + this.finalCheck = function removeARIAOwns_finalCheck() { + // children follow the DOM order + var tree = + { SECTION: [ + { PUSHBUTTON: [ ] }, + { CHECKBUTTON: [ + { SECTION: [] }, + ] }, + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function removeARIAOwns_getID() { + return "Remove @aria-owns attribute"; + }; + } + + function setARIAOwns() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_button")), + new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")), + new invokerChecker(EVENT_SHOW, getNode("t1_button")), + new invokerChecker(EVENT_SHOW, getNode("t1_subdiv")), + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + ]; + + this.invoke = function setARIAOwns_invoke() { + getNode("t1_container"). + setAttribute("aria-owns", "t1_button t1_subdiv"); + }; + + this.finalCheck = function setARIAOwns_finalCheck() { + // children are swapped again, button and subdiv are appended to + // the children. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // checkbox + { PUSHBUTTON: [ ] }, // button, rearranged by ARIA own + { SECTION: [ ] }, // subdiv from the subtree, ARIA owned + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function setARIAOwns_getID() { + return "Set @aria-owns attribute"; + }; + } + + function addIdToARIAOwns() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_group")), + new invokerChecker(EVENT_SHOW, getNode("t1_group")), + new invokerChecker(EVENT_REORDER, document), + ]; + + this.invoke = function addIdToARIAOwns_invoke() { + getNode("t1_container"). + setAttribute("aria-owns", "t1_button t1_subdiv t1_group"); + }; + + this.finalCheck = function addIdToARIAOwns_finalCheck() { + // children are swapped again, button and subdiv are appended to + // the children. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // t1_checkbox + { PUSHBUTTON: [ ] }, // button, t1_button + { SECTION: [ ] }, // subdiv from the subtree, t1_subdiv + { GROUPING: [ ] }, // group from outside, t1_group + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function addIdToARIAOwns_getID() { + return "Add id to @aria-owns attribute value"; + }; + } + + function appendEl() { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getNode, "t1_child3"), + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + ]; + + this.invoke = function appendEl_invoke() { + var div = document.createElement("div"); + div.setAttribute("id", "t1_child3"); + div.setAttribute("role", "radio"); + getNode("t1_container").appendChild(div); + }; + + this.finalCheck = function appendEl_finalCheck() { + // children are invalidated, they includes aria-owns swapped kids and + // newly inserted child. + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // existing explicit, t1_checkbox + { RADIOBUTTON: [ ] }, // new explicit, t1_child3 + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + { SECTION: [ ] }, // ARIA owned, t1_subdiv + { GROUPING: [ ] }, // ARIA owned, t1_group + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function appendEl_getID() { + return "Append child under @aria-owns element"; + }; + } + + function removeEl() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_subdiv")), + new invokerChecker(EVENT_REORDER, getNode("t1_container")), + ]; + + this.invoke = function removeEl_invoke() { + // remove a container of t1_subdiv + getNode("t1_span").remove(); + }; + + this.finalCheck = function removeEl_finalCheck() { + // subdiv should go away + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // explicit, t1_checkbox + { RADIOBUTTON: [ ] }, // explicit, t1_child3 + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + { GROUPING: [ ] }, // ARIA owned, t1_group + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function removeEl_getID() { + return "Remove a container of ARIA owned element"; + }; + } + + function removeId() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_group")), + new invokerChecker(EVENT_SHOW, getNode("t1_group")), + new invokerChecker(EVENT_REORDER, document), + ]; + + this.invoke = function removeId_invoke() { + getNode("t1_group").removeAttribute("id"); + }; + + this.finalCheck = function removeId_finalCheck() { + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, + { RADIOBUTTON: [ ] }, + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function removeId_getID() { + return "Remove ID from ARIA owned element"; + }; + } + + function setId() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("t1_grouptmp")), + new invokerChecker(EVENT_SHOW, getNode("t1_grouptmp")), + new invokerChecker(EVENT_REORDER, document), + ]; + + this.invoke = function setId_invoke() { + getNode("t1_grouptmp").setAttribute("id", "t1_group"); + }; + + this.finalCheck = function setId_finalCheck() { + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, + { RADIOBUTTON: [ ] }, + { PUSHBUTTON: [ ] }, // ARIA owned, t1_button + { GROUPING: [ ] }, // ARIA owned, t1_group, previously t1_grouptmp + ] }; + testAccessibleTree("t1_container", tree); + }; + + this.getID = function setId_getID() { + return "Set ID that is referred by ARIA owns"; + }; + } + + /** + * Remove an accessible DOM element containing an element referred by + * ARIA owns. + */ + function removeA11eteiner() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t2_container1")), + ]; + + this.invoke = function removeA11eteiner_invoke() { + var tree = + { SECTION: [ + { CHECKBUTTON: [ ] }, // ARIA owned, 't2_owned' + ] }; + testAccessibleTree("t2_container1", tree); + + getNode("t2_container2").removeChild(getNode("t2_container3")); + }; + + this.finalCheck = function removeA11eteiner_finalCheck() { + var tree = + { SECTION: [ + ] }; + testAccessibleTree("t2_container1", tree); + }; + + this.getID = function removeA11eteiner_getID() { + return "Remove an accessible DOM element containing an element referred by ARIA owns"; + }; + } + + /** + * Attempt to steal an element from other ARIA owns element. This should + * not be possible. The only child that will get owned into this + * container is a previously not aria-owned one. + */ + function stealFromOtherARIAOwns() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t3_container3")), + ]; + + this.invoke = function stealFromOtherARIAOwns_invoke() { + getNode("t3_container3").setAttribute("aria-owns", "t3_child t3_child2"); + }; + + this.finalCheck = function stealFromOtherARIAOwns_finalCheck() { + var tree = + { SECTION: [ + { CHECKBUTTON: [ + ] }, + ] }; + testAccessibleTree("t3_container1", tree); + + tree = + { SECTION: [ + ] }; + testAccessibleTree("t3_container2", tree); + + tree = + { SECTION: [ + { CHECKBUTTON: [ + ] }, + ] }; + testAccessibleTree("t3_container3", tree); + }; + + this.getID = function stealFromOtherARIAOwns_getID() { + return "Steal an element from other ARIA owns element"; + }; + } + + function appendElToRecacheChildren() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t3_container3")), + ]; + + this.invoke = function appendElToRecacheChildren_invoke() { + var div = document.createElement("div"); + div.setAttribute("role", "radio"); + getNode("t3_container3").appendChild(div); + }; + + this.finalCheck = function appendElToRecacheChildren_finalCheck() { + var tree = + { SECTION: [ + ] }; + testAccessibleTree("t3_container2", tree); + + tree = + { SECTION: [ + { RADIOBUTTON: [ ] }, + { CHECKBUTTON: [ ] }, // ARIA owned + ] }; + testAccessibleTree("t3_container3", tree); + }; + + this.getID = function appendElToRecacheChildren_getID() { + return "Append a child under @aria-owns element to trigger children recache"; + }; + } + + function showHiddenElement() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode("t4_container1")), + ]; + + this.invoke = function showHiddenElement_invoke() { + var tree = + { SECTION: [ + { RADIOBUTTON: [] }, + ] }; + testAccessibleTree("t4_container1", tree); + + getNode("t4_child1").style.display = "block"; + }; + + this.finalCheck = function showHiddenElement_finalCheck() { + var tree = + { SECTION: [ + { CHECKBUTTON: [] }, + { RADIOBUTTON: [] }, + ] }; + testAccessibleTree("t4_container1", tree); + }; + + this.getID = function showHiddenElement_getID() { + return "Show hidden ARIA owns referred element"; + }; + } + + function rearrangeARIAOwns(aContainer, aAttr, aIdList, aRoleList) { + this.eventSeq = []; + for (let id of aIdList) { + this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id))); + } + + for (let id of aIdList) { + this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id))); + } + this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer))); + + this.invoke = function rearrangeARIAOwns_invoke() { + getNode(aContainer).setAttribute("aria-owns", aAttr); + }; + + this.finalCheck = function rearrangeARIAOwns_finalCheck() { + var tree = { SECTION: [ ] }; + for (var role of aRoleList) { + var ch = {}; + ch[role] = []; + tree.SECTION.push(ch); + } + testAccessibleTree(aContainer, tree); + }; + + this.getID = function rearrangeARIAOwns_getID() { + return `Rearrange @aria-owns attribute to '${aAttr}'`; + }; + } + + function removeNotARIAOwnedEl(aContainer, aChild) { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer), + ]; + + this.invoke = function removeNotARIAOwnedEl_invoke() { + var tree = { + SECTION: [ + { TEXT_LEAF: [ ] }, + { GROUPING: [ ] }, + ], + }; + testAccessibleTree(aContainer, tree); + + getNode(aContainer).removeChild(getNode(aChild)); + }; + + this.finalCheck = function removeNotARIAOwnedEl_finalCheck() { + var tree = { + SECTION: [ + { GROUPING: [ ] }, + ], + }; + testAccessibleTree(aContainer, tree); + }; + + this.getID = function removeNotARIAOwnedEl_getID() { + return `remove not ARIA owned child`; + }; + } + + function setARIAOwnsOnElToRemove(aParent, aChild) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getAccessible(aParent)), + ]; + + this.invoke = function setARIAOwnsOnElToRemove_invoke() { + getNode(aChild).setAttribute("aria-owns", "no_id"); + getNode(aParent).removeChild(getNode(aChild)); + getNode(aParent).remove(); + }; + + this.getID = function setARIAOwnsOnElToRemove_getID() { + return `set ARIA owns on an element, and then remove it, and then remove its parent`; + }; + } + + /** + * Set ARIA owns on inaccessible span element that contains + * accessible children. This will move children from the container for + * the span. + */ + function test8() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "t8_container"), + ]; + + this.invoke = function test8_invoke() { + var tree = + { SECTION: [ + { PUSHBUTTON: [] }, + { ENTRY: [] }, + { ENTRY: [] }, + { ENTRY: [] }, + ] }; + testAccessibleTree("t8_container", tree); + + getNode(t8_container).setAttribute("aria-owns", "t8_span t8_button"); + }; + + this.finalCheck = function test8_finalCheck() { + var tree = + { SECTION: [ + { TEXT: [ + { ENTRY: [] }, + { ENTRY: [] }, + { ENTRY: [] }, + ] }, + { PUSHBUTTON: [] }, + ] }; + testAccessibleTree("t8_container", tree); + }; + + this.getID = function test8_getID() { + return `Set ARIA owns on inaccessible span element that contains accessible children`; + }; + } + + function test9_prepare() { + this.eventSeq = [ + new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, () => { + return getNode("t9_container").contentDocument; + }), + ]; + + this.invoke = () => { + // The \ before the final /script avoids the script from being terminated + // by the html parser. + getNode("t9_container").src = `data:text/html, + <html><body></body> + <script> + let el = document.createElement('div'); + el.id = 'container'; + el.innerHTML = "<input id='input'>"; + document.documentElement.appendChild(el); + <\/script></html>`; + }; + + this.finalCheck = () => { + var tree = + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { SECTION: [ + { ENTRY: [] }, + ] }, + ] }, + ] }; + testAccessibleTree("t9_container", tree); + }; + + this.getID = () => { + return `Set ARIA owns on a document (part1)`; + }; + } + + function test9_setARIAOwns() { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, () => { + let doc = getNode("t9_container").contentDocument; + return doc && doc.getElementById("input"); + }), + ]; + + this.invoke = () => { + let doc = getNode("t9_container").contentDocument; + doc.body.setAttribute("aria-owns", "input"); + }; + + this.finalCheck = () => { + var tree = + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { SECTION: [] }, + { ENTRY: [] }, + ] }, + ] }; + testAccessibleTree("t9_container", tree); + }; + + this.getID = () => { + return `Set ARIA owns on a document (part2)`; + }; + } + + function test9_finish() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, () => { + return getNode("t9_container").contentDocument; + }), + ]; + + this.invoke = () => { + // trigger a tree update. + let doc = getNode("t9_container").contentDocument; + doc.body.appendChild(doc.createElement("p")); + }; + + this.finalCheck = () => { + var tree = + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { PARAGRAPH: [] }, + { SECTION: [] }, + { ENTRY: [] }, + ] }, + ] }; + testAccessibleTree("t9_container", tree); + }; + + this.getID = () => { + return `Set ARIA owns on a document (part3)`; + }; + } + + /** + * Put ARIA owned child back when ARIA owner removed. + */ + function test10_removeARIAOwner() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getAccessible("t10_owner")), + ]; + + this.invoke = () => { + let tree = + { SECTION: [ // t10_container + { SECTION: [ // t10_owner + { ENTRY: [] }, // t10_child + ] }, + ] }; + testAccessibleTree("t10_container", tree); + + getNode("t10_owner").remove(); + }; + + this.getID = () => { + return "Put aria owned child back when aria owner removed"; + }; + } + + function test10_finishTest() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "t10_container"), + ]; + + this.invoke = () => { + // trigger a tree update. + getNode("t10_container").append(document.createElement("p")); + }; + + this.finalCheck = () => { + let tree = + { SECTION: [ // t10_container + // { ENTRY: [] }, // t10_child + { PARAGRAPH: [] }, + ] }; + testAccessibleTree("t10_container", tree); + todo(false, "Input accessible has be moved back in the tree"); + }; + + this.getID = () => { + return `Put aria owned child back when aria owner removed (finish test)`; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + // ////////////////////////////////////////////////////////////////////////// + + // gA11yEventDumpToConsole = true; + // enableLogging("tree,eventTree,verbose"); // debug stuff + + var gQueue = null; + + async function doTest() { + let PromEvents = {}; + Services.scriptloader.loadSubScript( + "chrome://mochitests/content/a11y/accessible/tests/mochitest/promisified-events.js", + PromEvents); + + gQueue = new eventQueue(); + let queueFinished = new Promise(resolve => { + gQueue.onFinish = function() { + resolve(); + return DO_NOT_FINISH_TEST; + }; + }); + + // test1 + gQueue.push(new changeARIAOwns()); + gQueue.push(new removeARIAOwns()); + gQueue.push(new setARIAOwns()); + gQueue.push(new addIdToARIAOwns()); + gQueue.push(new appendEl()); + gQueue.push(new removeEl()); + gQueue.push(new removeId()); + gQueue.push(new setId()); + + // test2 + gQueue.push(new removeA11eteiner()); + + // test3 + gQueue.push(new stealFromOtherARIAOwns()); + gQueue.push(new appendElToRecacheChildren()); + + // test4 + gQueue.push(new showHiddenElement()); + + // test5 + gQueue.push(new rearrangeARIAOwns( + "t5_container", "t5_checkbox t5_radio t5_button", + [ "t5_checkbox", "t5_radio", "t5_button" ], + [ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ])); + gQueue.push(new rearrangeARIAOwns( + "t5_container", "t5_radio t5_button t5_checkbox", + [ "t5_radio", "t5_button" ], + [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ])); + + gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span")); + + gQueue.push(new setARIAOwnsOnElToRemove("t7_parent", "t7_child")); + + gQueue.push(new test8()); + gQueue.push(new test9_prepare()); + gQueue.push(new test9_setARIAOwns()); + gQueue.push(new test9_finish()); + + gQueue.push(new test10_removeARIAOwner()); + gQueue.push(new test10_finishTest()); + + gQueue.invoke(); + await queueFinished; + + let owned = document.createElement('div'); + owned.id = 't11_child'; + owned.textContent = 'owned'; + let evtPromise = PromEvents.waitForEvent(EVENT_SHOW, "t11_child"); + getNode("t11_container").append(owned); + let evt = await evtPromise; + is(evt.accessible.parent.name, "t11_owner"); + + // Test owning an ancestor which isn't created yet. + testAccessibleTree("t12_container", { SECTION: [ // t12_container + { SECTION: [ // t12b + { SECTION: [] }, // t12c + ] }, + { SECTION: [] }, // t12d + ] }); + // Owning t12a would create a cycle, so we expect it to do nothing. + // We own t12d so we get an event when aria-owns relocation is complete. + evtPromise = PromEvents.waitForEvent(EVENT_SHOW, "t12d"); + getNode("t12c").setAttribute("aria-owns", "t12a t12d"); + await evtPromise; + testAccessibleTree("t12_container", { SECTION: [ // t12_container + { SECTION: [ // t12b + { SECTION: [ // t12c + { SECTION: [] }, // t12d + ] }, + ] }, + ] }); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + + </script> +</head> + +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="t1_container" aria-owns="t1_checkbox t1_button"> + <div role="button" id="t1_button"></div> + <div role="checkbox" id="t1_checkbox"> + <span id="t1_span"> + <div id="t1_subdiv"></div> + </span> + </div> + </div> + <div id="t1_group" role="group"></div> + <div id="t1_grouptmp" role="group"></div> + + <div id="t2_container1" aria-owns="t2_owned"></div> + <div id="t2_container2"> + <div id="t2_container3"><div id="t2_owned" role="checkbox"></div></div> + </div> + + <div id="t3_container1" aria-owns="t3_child"></div> + <div id="t3_child" role="checkbox"></div> + <div id="t3_container2"> + <div id="t3_child2" role="checkbox"></div> + </div> + <div id="t3_container3"></div> + + <div id="t4_container1" aria-owns="t4_child1 t4_child2"></div> + <div id="t4_container2"> + <div id="t4_child1" style="display:none" role="checkbox"></div> + <div id="t4_child2" role="radio"></div> + </div> + + <div id="t5_container"> + <div role="button" id="t5_button"></div> + <div role="checkbox" id="t5_checkbox"></div> + <div role="radio" id="t5_radio"></div> + </div> + + <div id="t6_container" aria-owns="t6_fake"> + <span id="t6_span">hey</span> + </div> + <div id="t6_fake" role="group"></div> + + <div id="t7_container"> + <div id="t7_parent"> + <div id="t7_child"></div> + </div> + </div> + + <div id="t8_container"> + <input id="t8_button" type="button"><span id="t8_span"><input><input><input></span> + </div> + + <iframe id="t9_container"></iframe> + + <div id="t10_container"> + <div id="t10_owner" aria-owns="t10_child"></div> + <input id="t10_child"> + </div> + + <div id="t11_container" aria-label="t11_container"> + <div aria-owns="t11_child" aria-label="t11_owner"></div> + </div> + + <div id="t12_container"> + <span id="t12a"> + <div id="t12b" aria-owns="t12c"></div> + </span> + <div id="t12c"></div> + <div id="t12d"></div> + </div> +</body> + +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1040735.html b/accessible/tests/mochitest/treeupdate/test_bug1040735.html new file mode 100644 index 0000000000..b7d0e472d0 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1040735.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html> +<head> + <title>Adopt DOM node from anonymous subtree</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="../common.js"></script> + + <script type="application/javascript"> + function doTest() { + document.body.appendChild(document.getElementById("mw_a")); + setTimeout(function() { ok(true, "no crash and assertions"); SimpleTest.finish(); }, 0); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1040735" + title="Bug 1040735 - DOM node reinsertion under anonymous content may trigger a11y child adoption"> + Bug 1040735</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <marquee> + <div id="mw_a" style="visibility: hidden;"> + <div style="visibility: visible;" id="mw_inside"></div> + </div> + </marquee> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1175913.html b/accessible/tests/mochitest/treeupdate/test_bug1175913.html new file mode 100644 index 0000000000..1fe2720434 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1175913.html @@ -0,0 +1,95 @@ +<html> + +<head> + <title>Test hide/show events on event listener changes</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + function dummyListener() {} + + function testAddListener() { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getNode("parent")), + ]; + + this.invoke = function testAddListener_invoke() { + is(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that parent is not accessible."); + is(getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), null, "Check that child is not accessible."); + getNode("parent").addEventListener("click", dummyListener); + }; + + this.finalCheck = function testAddListener_finalCheck() { + var tree = { TEXT: [] }; + testAccessibleTree("parent", tree); + }; + + this.getID = function testAddListener_getID() { + return "Test that show event is sent when click listener is added"; + }; + } + + function testRemoveListener() { + this.eventSeq = [ + new unexpectedInvokerChecker(EVENT_HIDE, getNode("parent")), + ]; + + this.invoke = function testRemoveListener_invoke() { + getNode("parent").removeEventListener("click", dummyListener); + }; + + this.finalCheck = function testRemoveListener_finalCheck() { + ok(getAccessible("parent", null, null, DONOTFAIL_IF_NO_ACC), + "Parent stays accessible after click event listener is removed"); + ok(!getAccessible("child", null, null, DONOTFAIL_IF_NO_ACC), + "Child stays inaccessible"); + }; + + this.getID = function testRemoveListener_getID() { + return "Test that hide event is sent when click listener is removed"; + }; + } + + var gQueue = null; + function doTest() { + gQueue = new eventQueue(); + gQueue.push(new testAddListener()); + gQueue.push(new testRemoveListener()); + gQueue.invoke(); // SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1175913" + title="Crash in mozilla::a11y::DocAccessibleParent::RemoveAccessible(ProxyAccessible* aAccessible)"> + Mozilla Bug 1175913 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <span id="parent"> + <span id="child"> + </span> + </span> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1189277.html b/accessible/tests/mochitest/treeupdate/test_bug1189277.html new file mode 100644 index 0000000000..95efe5135a --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1189277.html @@ -0,0 +1,82 @@ +<html> + +<head> + <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../name.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function runTest() { + this.containerNode = getNode("outerDiv"); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode("child")), + new invokerChecker(EVENT_HIDE, getNode("childDoc")), + new invokerChecker(EVENT_SHOW, "newChildDoc"), + new invokerChecker(EVENT_REORDER, this.containerNode), + ]; + + this.invoke = function runTest_invoke() { + this.containerNode.removeChild(getNode("child")); + + var docContainer = getNode("docContainer"); + var iframe = document.createElement("iframe"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + iframe.setAttribute("src", "http://example.com"); + iframe.setAttribute("id", "newChildDoc"); + + docContainer.removeChild(getNode("childDoc")); + docContainer.appendChild(iframe); + }; + + this.getID = function runTest_getID() { + return "check show events are not incorrectly coalesced"; + }; + } + + // enableLogging("tree"); + gA11yEventDumpToConsole = true; + var gQueue = null; + function doTest() { + gQueue = new eventQueue(); + gQueue.push(new runTest()); + gQueue.invoke(); // SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1189277" + title="content process crash caused by missing show event"> + Mozilla Bug 1189277 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="outerDiv"> + <div id="child">foo</div> + <div id="docContainer"> + <iframe id="childDoc" src="about:blank"> + </iframe> + </div> + </div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857.html b/accessible/tests/mochitest/treeupdate/test_bug1276857.html new file mode 100644 index 0000000000..a164247534 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html @@ -0,0 +1,131 @@ +<!DOCTYPE html> +<html> + +<head> + <title>DOM mutations test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function runTest() { + let iframe = document.getElementById("iframe"); + + // children change will recreate the table + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, () => { + let doc = getNode("iframe").contentDocument; + return doc && doc.getElementById("c1"); + }), + ]; + + this.invoke = function runTest_invoke() { + var tree = { + SECTION: [ // c1 + { TEXT_LEAF: [] }, // Some text + { TEXT_CONTAINER: [ + { TEXT_LEAF: [] }, // something with .. + ] }, + { TEXT_LEAF: [] }, // More text + ], + }; + testAccessibleTree(iframe.contentDocument.getElementById("c1"), tree); + + iframe.contentDocument.getElementById("c1_t").querySelector("span").remove(); + }; + + this.finalCheck = function runTest_finalCheck() { + var tree = { + SECTION: [ // c1 + { TEXT_LEAF: [] }, // Some text + { TEXT_LEAF: [] }, // More text + ], + }; + testAccessibleTree(iframe.contentDocument.getElementById("c1"), tree); + }; + + this.getID = function runTest_getID() { + return "child DOM node is removed before the layout notifies the a11y about parent removal/show"; + }; + } + + function runShadowTest() { + // children change will recreate the table + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, () => { + let doc = getNode("iframe").contentDocument; + return doc && doc.getElementById("c2"); + }), + ]; + + this.invoke = function runShadowTest_invoke() { + var tree = { + SECTION: [ // c2 + { TEXT_LEAF: [] }, // Some text + { TEXT_CONTAINER: [ + { TEXT_LEAF: [] }, // something with .. + ] }, + { TEXT_LEAF: [] }, // More text + ], + }; + const iframe = document.getElementById("iframe"); + testAccessibleTree(iframe.contentDocument.getElementById("c2"), tree); + + var shadowRoot = iframe.contentDocument.getElementById("c2_c").shadowRoot; + shadowRoot.firstElementChild.querySelector("span").remove(); + // bug 1487312 + shadowRoot.firstElementChild.offsetTop; + shadowRoot.appendChild(document.createElement("button")); + }; + + this.finalCheck = function runShadowTest_finalCheck() { + var tree = { + SECTION: [ // c2 + { TEXT_LEAF: [] }, // Some text + { TEXT_LEAF: [] }, // More text + { PUSHBUTTON: [] }, // The button we appended. + ], + }; + const iframe = document.getElementById("iframe"); + testAccessibleTree(iframe.contentDocument.getElementById("c2"), tree); + }; + + this.getID = function runShadowTest_getID() { + return "child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM"; + }; + } + + // enableLogging("tree"); + // gA11yEventDumpToConsole = true; + + var gQueue = null; + function doTest() { + gQueue = new eventQueue(); + gQueue.push(new runTest()); + gQueue.push(new runShadowTest()); + gQueue.invoke(); // will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + + window.onload = () => { + let iframe = document.createElement("iframe"); + iframe.id = "iframe"; + iframe.src = "test_bug1276857_subframe.html"; + addA11yLoadEvent(doTest, iframe.contentWindow); + document.body.appendChild(iframe); + }; + </script> + +</head> +<body> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html b/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html new file mode 100644 index 0000000000..869c9ebe6c --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html @@ -0,0 +1,33 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>DOM mutations test</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + <script type="application/javascript" src="../role.js"></script> +</head> +<body> + <div id="c1"> + <div id="c1_t" style="display: table" role="presentation"> + Some text + <span style="display: table-cell">something with accessibles goes here</span> + More text + </div> + </div> + + <template id="tmpl"> + <div style="display: table" role="presentation"> + Some text + <span style="display: table-cell">something with accessibles goes here</span> + More text + </div> + </template> + + <div id="c2"><div id="c2_c" role="presentation"></div></div> + + <script> + var gShadowRoot = document.getElementById("c2_c").attachShadow({mode: "open"}); + var tmpl = document.getElementById("tmpl"); + gShadowRoot.appendChild(document.importNode(tmpl.content, true)); + </script> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml b/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml new file mode 100644 index 0000000000..51a3c39047 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug852150.xhtml @@ -0,0 +1,57 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <title>Canvas subdom mutation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + + <script> + <![CDATA[ + function doTest() { + var the_displayNone = getNode("the_displaynone"); + var the_table = getNode("the_table"); + var the_row = getNode("the_row"); + ok(isAccessible(the_table), "table should be accessible"); + the_displayNone.appendChild(the_table); + ok(!isAccessible(the_table), "table in display none tree shouldn't be accessible"); + + setTimeout(function() { + document.body.removeChild(the_row); + // make sure no accessibles have stuck around. + ok(!isAccessible(the_row), "row shouldn't be accessible"); + ok(!isAccessible(the_table), "table shouldn't be accessible"); + ok(!isAccessible(the_displayNone), "display none things shouldn't be accessible"); + SimpleTest.finish(); + }, 0); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> +</head> +<body> + + <a target="_blank" + title="test accessible removal when reframe root isn't accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=852150"> + Mozilla Bug 852150 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="the_displaynone" style="display: none;"></div> + <table id="the_table"></table> + <tr id="the_row"></tr> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml b/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml new file mode 100644 index 0000000000..5d9e813f3a --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug883708.xhtml @@ -0,0 +1,31 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script> + +function boom() { + var newSpan = document.createElementNS("http://www.w3.org/1999/xhtml", "span"); + document.getElementById("c").insertBefore(newSpan, document.getElementById("d")); + document.getElementById("a").style.visibility = "visible"; + ok(true, "test didn't crash or assert"); + SimpleTest.finish(); +} + +</script> +</head> + +<body onload="boom();"> + <a target="_blank" + title="test reparenting accessible subtree when inaccessible element becomes accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=883708"> + Mozilla Bug 883708 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + +<div style="visibility: collapse;" id="a"><div style="float: right; visibility: visible;"><div id="c"><td id="d"></td></div></div></div></body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml b/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml new file mode 100644 index 0000000000..7e3cf14fac --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug884251.xhtml @@ -0,0 +1,19 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + +<script> + +function boom() { + document.getElementById("k").removeAttribute("href"); + ok(true, "changing iframe contents doesn't cause assertions"); + SimpleTest.finish(); +} + +</script> +</head> + +<body onload="boom();"> +<iframe src="data:text/html,1"><link id="k" href="data:text/html,2" /></iframe> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_bug895082.html b/accessible/tests/mochitest/treeupdate/test_bug895082.html new file mode 100644 index 0000000000..8332c5206e --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_bug895082.html @@ -0,0 +1,49 @@ +<!DOCTYPE html> +<html> +<head> +<title>Replace body test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> +function doTest() { + var y = document.getElementById("y"); + var oldBody = document.body; + var newBody = document.createElement("body"); + document.documentElement.insertBefore(newBody, oldBody); + setTimeout(function() { + document.documentElement.removeChild(oldBody); + newBody.appendChild(y); + ok(true, "we didn't assert"); + SimpleTest.finish(); + }, 0); +} + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=895082" + title="Bug 895082 - replacing body element asserts"> + Bug 895082</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + +<div><div id="y"></div></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_canvas.html b/accessible/tests/mochitest/treeupdate/test_canvas.html new file mode 100644 index 0000000000..229bf4f2e3 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_canvas.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Canvas subdom mutation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + function addSubtree(aID) { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.node), + ]; + + this.invoke = function addSubtree_invoke() { + // ensure we start with no subtree + testAccessibleTree("canvas", { CANVAS: [] }); + getNode("dialog").style.display = "block"; + }; + + this.finalCheck = function addSubtree_finalCheck() { + testAccessibleTree("dialog", { DIALOG: [] }); + }; + + this.getID = function addSubtree_getID() { + return "show canvas subdom"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() { + gQueue = new eventQueue(); + + // make the subdom come alive! + gQueue.push(new addSubtree("dialog")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Expose content in Canvas element" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=495912"> + Mozilla Bug 495912 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <canvas id="canvas"> + <div id="dialog" role="dialog" style="display: none;"> + </div> + </canvas> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml b/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml new file mode 100644 index 0000000000..f81d77332d --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml @@ -0,0 +1,315 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="menu tree and events"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../events.js" /> + <script type="application/javascript" + src="../role.js" /> + + <script type="application/javascript"> + <![CDATA[ + + function openMenu(aID, aTree) + { + this.eventSeq = [ + new invokerChecker(EVENT_MENUPOPUP_START, getNode(aID)) + ]; + + this.invoke = function openMenu_invoke() + { + var button = getNode("button"); + getNode(aID).openPopup(button, "after_start", 0, 0, true, false); + } + + this.finalCheck = function openMenu_finalCheck(aEvent) + { + testAccessibleTree(aID, aTree); + } + + this.getID = function openMenu_getID() + { + return "open menu " + prettyName(aID); + } + } + + function selectNextMenuItem(aID) + { + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getNode(aID)) + ]; + + this.invoke = function selectMenuItem_invoke() + { + synthesizeKey("KEY_ArrowDown"); + } + + this.getID = function selectMenuItem_getID() + { + return "select menuitem " + prettyName(aID); + } + } + + function openSubMenu(aSubMenuID, aItemID, aMenuID, aTree) + { + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getNode(aItemID)), + ]; + + this.invoke = function openSubMenu_invoke() + { + synthesizeKey("KEY_Enter"); + } + + this.finalCheck = function openSubMenu_finalCheck(aEvent) + { + testAccessibleTree(aMenuID, aTree); + } + + this.getID = function openSubMenu_getID() + { + return "open submenu " + prettyName(aSubMenuID) + " focusing item " + prettyName(aItemID); + } + } + + function closeSubMenu(aSubMenuID, aItemID) + { + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, getNode(aItemID)), + ]; + + this.invoke = function closeSubMenu_invoke() + { + synthesizeKey("KEY_Escape"); + } + + this.getID = function closeSubMenu_getID() + { + return "close submenu " + prettyName(aSubMenuID) + " focusing item " + prettyName(aItemID); + } + } + + function closeMenu(aID) + { + this.eventSeq = [ + new invokerChecker(EVENT_MENUPOPUP_END, getNode(aID)) + ]; + + this.invoke = function closeMenu_invoke() + { + synthesizeKey("KEY_Escape"); + } + + this.getID = function closeMenu_getID() + { + return "close menu " + prettyName(aID); + } + } + + //gA11yEventDumpToConsole = true; + //enableLogging("tree,verbose"); + + var gQueue = null; + var gContextTree = {}; + + // Linux and Windows menu trees discrepancy: bug 527646. + + /** + * Return the context menu tree before submenus were open. + */ + function getMenuTree1() + { + if (LINUX || SOLARIS) { + let tree = { + role: ROLE_MENUPOPUP, + children: [ + { + name: "item0", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item1", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item2", + role: ROLE_PARENT_MENUITEM, + children: [ ] + } + ] + }; + return tree; + } + + // Windows + let tree = { + role: ROLE_MENUPOPUP, + children: [ + { + name: "item0", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item1", + role: ROLE_MENUITEM, + children: [] + }, + { + name: "item2", + role: ROLE_PARENT_MENUITEM, + children: [ + { + name: "item2", + role: ROLE_MENUPOPUP, + children: [ ] + } + ] + } + ] + }; + return tree; + } + + /** + * Return context menu tree when submenu was open. + */ + function getMenuTree2() + { + var tree = getMenuTree1(); + if (LINUX || SOLARIS) { + let submenuTree = + { + name: "item2.0", + role: ROLE_PARENT_MENUITEM, + children: [ ] + }; + tree.children[2].children.push(submenuTree); + return tree; + } + + // Windows + let submenuTree = + { + name: "item2.0", + role: ROLE_PARENT_MENUITEM, + children: [ + { + name: "item2.0", + role: ROLE_MENUPOPUP, + children: [ ] + } + ] + }; + + tree.children[2].children[0].children.push(submenuTree); + return tree; + } + + /** + * Return context menu tree when subsub menu was open. + */ + function getMenuTree3() + { + var tree = getMenuTree2(); + var subsubmenuTree = + { + name: "item2.0.0", + role: ROLE_MENUITEM, + children: [] + }; + + if (LINUX || SOLARIS) + tree.children[2].children[0].children.push(subsubmenuTree); + else + tree.children[2].children[0].children[0].children[0].children.push(subsubmenuTree); + + return tree; + } + + + function doTests() + { + gQueue = new eventQueue(); + + // Check initial empty tree + testAccessibleTree("context", { MENUPOPUP: [] }); + + // Open context menu and check that menu item accesibles are created. + gQueue.push(new openMenu("context", getMenuTree1())); + + // Select items and check focus event on them. + gQueue.push(new selectNextMenuItem("item0")); + gQueue.push(new selectNextMenuItem("item1")); + gQueue.push(new selectNextMenuItem("item2")); + + // Open sub menu and check menu accessible tree and focus event. + gQueue.push(new openSubMenu("submenu2", "item2.0", + "context", getMenuTree2())); + gQueue.push(new openSubMenu("submenu2.0", "item2.0.0", + "context", getMenuTree3())); + + // Close submenus and check that focus goes to parent. + gQueue.push(new closeSubMenu("submenu2.0", "item2.0")); + gQueue.push(new closeSubMenu("submenu2", "item2")); + + gQueue.push(new closeMenu("context")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630194" + title="Update accessible tree when opening the menu popup"> + Mozilla Bug 630194 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children."> + Mozilla Bug 630486 + </a> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + + <menupopup id="context"> + <menuitem id="item0" label="item0"/> + <menuitem id="item1" label="item1"/> + <menu id="item2" label="item2"> + <menupopup id="submenu2"> + <menu id="item2.0" label="item2.0"> + <menupopup id="submenu2.0"> + <menuitem id="item2.0.0" label="item2.0.0"/> + </menupopup> + </menu> + </menupopup> + </menu> + </menupopup> + + <button context="context" id="button">btn</button> + </vbox> + </hbox> +</window> diff --git a/accessible/tests/mochitest/treeupdate/test_cssoverflow.html b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html new file mode 100644 index 0000000000..6b60fce975 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_cssoverflow.html @@ -0,0 +1,149 @@ +<html> + +<head> + <title>Testing HTML scrollable frames (css overflow style)</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + // ////////////////////////////////////////////////////////////////////////// + + /** + * Change scroll range to not empty size and inserts a child into container + * to trigger tree update of the container. Prior to bug 677154 not empty + * size resulted to accessible creation for scroll area, container tree + * update picked up that accessible unattaching scroll area accessible + * subtree. + */ + function changeScrollRange(aContainerID, aScrollAreaID) { + this.containerNode = getNode(aContainerID); + this.container = getAccessible(this.containerNode); + this.scrollAreaNode = getNode(aScrollAreaID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function changeScrollRange_invoke() { + this.scrollAreaNode.style.width = "20px"; + this.containerNode.appendChild(document.createElement("input")); + }; + + this.finalCheck = function changeScrollRange_finalCheck() { + var accTree = + { SECTION: [ // container + { SECTION: [ // scroll area + { ENTRY: [] }, // child content + ] }, + { ENTRY: [] }, // inserted input + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function changeScrollRange_getID() { + return "change scroll range for " + prettyName(aScrollAreaID); + }; + } + + /** + * Change scrollbar styles from visible to auto to make the scroll area focusable. + * That causes us to create an accessible for it. + * Make sure the tree stays intact. + * The scroll area has no ID on purpose to make it inaccessible initially. + */ + function makeFocusableByScrollbarStyles(aContainerID) { + this.container = getAccessible(aContainerID); + this.scrollAreaNode = getNode(aContainerID).firstChild; + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAccessible, this.scrollAreaNode), + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function makeFocusableByScrollbarStyles_invoke() { + var accTree = + { SECTION: [ // container + { PARAGRAPH: [ // paragraph + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree(this.container, accTree); + + this.scrollAreaNode.style.overflow = "auto"; + }; + + this.finalCheck = function makeFocusableByScrollbarStyles_finalCheck() { + var accTree = + { SECTION: [ // container + { role: ROLE_SECTION, // focusable scroll area + states: STATE_FOCUSABLE, + children: [ + { PARAGRAPH: [ // paragraph + { TEXT_LEAF: [] }, // text leaf + ] }, + ], + }, // focusable scroll area + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function makeFocusableByScrollbarStyles_getID() { + return "make div focusable through scrollbar styles " + + prettyName(aContainerID); + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Do tests + // ////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + function doTests() { + gQueue = new eventQueue(); + + gQueue.push(new changeScrollRange("container", "scrollarea")); + gQueue.push(new makeFocusableByScrollbarStyles("container2")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=677154" + title="Detached document accessibility tree"> + Mozilla Bug 677154</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="eventdump"></div> + + <div id="container"><div id="scrollarea" style="overflow:auto;"><input></div></div> + <div id="container2"><div style="height: 1px;"><p>foo</p></div></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_deck.xhtml b/accessible/tests/mochitest/treeupdate/test_deck.xhtml new file mode 100644 index 0000000000..979996a66c --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_deck.xhtml @@ -0,0 +1,154 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Tree update on XUL deck panel switching"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../states.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + function switchDeckPanel(aContainerID, aDeckID) + { + this.panelIndex = 0; + + this.container = getAccessible(aContainerID); + this.deckNode = getNode(aDeckID); + this.prevPanel = getAccessible(this.deckNode.selectedPanel); + this.panelNode = this.deckNode.childNodes[this.panelIndex]; + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.prevPanel), + new invokerChecker(EVENT_SHOW, this.panelNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function switchDeckPanel_invoke() + { + var tree = + { GROUPING: [ // role="group" + { GROUPING: [ // groupbox, a selected panel #2 + { PUSHBUTTON: [ ] } // button + ] } + ] }; + testAccessibleTree(this.container, tree); + + this.deckNode.selectedIndex = this.panelIndex; + } + + this.finalCheck = function switchDeckPanel_finalCheck() + { + var tree = + { GROUPING: [ // role="group" + { LABEL: [ // description, a selected panel #1 + { TEXT_LEAF: [] } // text leaf, a description value + ] } + ] }; + testAccessibleTree(this.container, tree); + } + + this.getID = function switchDeckPanel_getID() + { + return "switch deck panel"; + } + } + + function showDeckPanel(aContainerID, aPanelID) + { + this.container = getAccessible(aContainerID); + this.deckNode = getNode(aPanelID); + var tree = + { GROUPING: [ // role="group" + { GROUPING: [ // grouping of panel 2 + { PUSHBUTTON: [] } // push button in panel 2 + ] } + ] }; + + + this.unexpectedEventSeq = [ + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function showDeckPanel_invoke() + { + // This stops the refreh driver from doing its regular ticks, and leaves + // us in control. 100 is an arbitrary positive number to advance the clock + // it is not checked or used anywhere. + window.windowUtils.advanceTimeAndRefresh(100); + + testAccessibleTree(this.container, tree); + this.deckNode.style.display = "-moz-box"; + + // This flushes our DOM mutations and forces any pending mutation events. + window.windowUtils.advanceTimeAndRefresh(100); + } + + this.finalCheck = function showDeckPanel_finalCheck() + { + testAccessibleTree(this.container, tree); + + // Return to regular refresh driver ticks. + window.windowUtils.restoreNormalRefresh(); + } + + this.getID = function showDeckPanel_getID() + { + return "show deck panel"; + } + } + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new showDeckPanel("container", "hidden")); + gQueue.push(new switchDeckPanel("container", "deck")); + gQueue.invoke(); // will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=814836" + title=" xul:deck element messes up screen reader"> + Mozilla Bug 814836 + </a> + + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1" id="container" role="group"> + + <deck id="deck" selectedIndex="1"> + <description>This is the first page</description> + <groupbox> + <button label="This is the second page"/> + </groupbox> + <hbox id="hidden" style="display: none;"><label>This is the third page</label></hbox> + </deck> + + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_delayed_removal.html b/accessible/tests/mochitest/treeupdate/test_delayed_removal.html new file mode 100644 index 0000000000..3f421f0c5b --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_delayed_removal.html @@ -0,0 +1,500 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test accessible delayed removal</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + .gentext:before { + content: "START" + } + .gentext:after { + content: "END" + } + </style> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + + async function hideDivFromInsideSpan() { + let msg = "hideDivFromInsideSpan"; + info(msg); + let events = waitForOrderedEvents([ + [EVENT_HIDE, "div1"], [EVENT_TEXT_REMOVED, "span1"], + [EVENT_REORDER, "span1"] + ], msg); + document.body.offsetTop; // Flush layout. + getNode("div1").style.display = "none"; + await events; + + testAccessibleTree("c1", { SECTION: [ { REGION: [] }, ] }); + } + + async function showDivFromInsideSpan() { + let msg = "showDivFromInsideSpan"; + info(msg); + let events = waitForOrderedEvents( + [[EVENT_SHOW, "div2"], [EVENT_REORDER, "span2"]], msg); + document.body.offsetTop; // Flush layout. + getNode("div2").style.display = "block"; + await events; + + testAccessibleTree("c2", + { SECTION: [ { REGION: [{ SECTION: [ { TEXT_LEAF: [] } ] }] }, ] }); + } + + async function removeDivFromInsideSpan() { + let msg = "removeDivFromInsideSpan"; + info(msg); + let events = waitForOrderedEvents([ + [EVENT_HIDE, getNode("div3")], [EVENT_TEXT_REMOVED, "span3"], + [EVENT_REORDER, "span3"] + ], msg); + document.body.offsetTop; // Flush layout. + getNode("div3").remove(); + await events; + + testAccessibleTree("c3", { SECTION: [ { REGION: [] }, ] }); + } + + // Test to see that generated content is inserted + async function addCSSGeneratedContent() { + let msg = "addCSSGeneratedContent"; + let c4_child = getAccessible("c4_child"); + info(msg); + let events = waitForOrderedEvents([ + [EVENT_SHOW, evt => evt.accessible == c4_child.firstChild], + [EVENT_SHOW, evt => evt.accessible == c4_child.lastChild], + [EVENT_REORDER, c4_child]], msg); + document.body.offsetTop; // Flush layout. + getNode("c4_child").classList.add('gentext'); + await events; + + testAccessibleTree("c4", { SECTION: [ // container + { SECTION: [ // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ] }, + ] }); + } + + // Test to see that generated content gets removed + async function removeCSSGeneratedContent() { + let msg = "removeCSSGeneratedContent"; + let c5_child = getAccessible("c5_child"); + info(msg); + let events = waitForEvents([ + [EVENT_HIDE, c5_child.firstChild], + [EVENT_HIDE, c5_child.lastChild], + [EVENT_REORDER, c5_child]], msg); + document.body.offsetTop; // Flush layout. + getNode("c5_child").classList.remove('gentext'); + await events; + + testAccessibleTree("c5",{ SECTION: [ // container + { SECTION: [ // inserted node + { TEXT_LEAF: [] }, // primary text + ] }, + ] }); + } + + // Test to see that a non-accessible intermediate container gets its accessible + // descendants removed and inserted correctly. + async function intermediateNonAccessibleContainers() { + let msg = "intermediateNonAccessibleContainers"; + info(msg); + + testAccessibleTree("c6",{ SECTION: [ + { SECTION: [ + { role: ROLE_PUSHBUTTON, name: "Hello" }, + ] }, + ] }); + + let events = waitForOrderedEvents( + [[EVENT_HIDE, "b1"], [EVENT_SHOW, "b2"], [EVENT_REORDER, "scrollarea"]], msg); + document.body.offsetTop; // Flush layout. + getNode("scrollarea").style.overflow = "auto"; + document.querySelector("#scrollarea > div > div:first-child").style.display = "none"; + document.querySelector("#scrollarea > div > div:last-child").style.display = "block"; + await events; + + testAccessibleTree("c6",{ SECTION: [ + { SECTION: [ + { role: ROLE_PUSHBUTTON, name: "Goodbye" }, + ] }, + ] }); + } + + // Test to see that the button gets reparented into the new accessible container. + async function intermediateNonAccessibleContainerBecomesAccessible() { + let msg = "intermediateNonAccessibleContainerBecomesAccessible"; + info(msg); + + testAccessibleTree("c7",{ SECTION: [ + { role: ROLE_PUSHBUTTON, name: "Hello" }, + { TEXT_LEAF: [] } + ] }); + + let events = waitForOrderedEvents( + [[EVENT_HIDE, "b3"], + // b3 show event coalesced into its new container + [EVENT_SHOW, evt => evt.DOMNode.classList.contains('intermediate')], + [EVENT_REORDER, "c7"]], msg); + document.body.offsetTop; // Flush layout. + document.querySelector("#c7 > div").style.display = "block"; + await events; + + testAccessibleTree("c7",{ SECTION: [ + { SECTION: [ { role: ROLE_PUSHBUTTON, name: "Hello" } ] } + ] }); + } + + // Test to ensure that relocated accessibles are removed when a DOM + // ancestor is hidden. + async function removeRelocatedWhenDomAncestorHidden() { + info("removeRelocatedWhenDomAncestorHidden"); + + testAccessibleTree("c8",{ SECTION: [ + { EDITCOMBOBOX: [ // c8_owner + { COMBOBOX_LIST: [] }, // c8_owned + ]}, + { SECTION: [] }, // c8_owned_container + ] }); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, "c8_owned_container"], + [EVENT_HIDE, "c8_owned"], + [EVENT_REORDER, "c8"], + ], "removeRelocatedWhenDomAncestorHidden"); + document.body.offsetTop; // Flush layout. + getNode("c8_owned_container").hidden = true; + await events; + + testAccessibleTree("c8",{ SECTION: [ + { EDITCOMBOBOX: [] }, // c8_owner + ] }); + } + + // Bug 1572829 + async function removeShadowRootHost() { + info("removeShadowRootHost"); + document.body.offsetTop; // Flush layout. + + let event = waitForEvent(EVENT_REORDER, "c9", "removeShadowRootHost"); + getNode("c9").firstElementChild.attachShadow({mode: "open"}); + getNode("c9").firstElementChild.replaceWith(""); + + await event; + } + + function listItemReframe() { + testAccessibleTree("li",{ LISTITEM: [ + { LISTITEM_MARKER: [] }, + { TEXT_LEAF: [] }, + ] }); + + getNode("li").style.listStylePosition = "inside"; + document.body.offsetTop; // Flush layout. + window.windowUtils.advanceTimeAndRefresh(100); + + testAccessibleTree("li",{ LISTITEM: [ + { LISTITEM_MARKER: [] }, + { TEXT_LEAF: [] }, + ] }); + + window.windowUtils.restoreNormalRefresh(); + } + + // Check to see that a reframed body gets its children pruned correctly. + async function bodyReframe(argument) { + // Load sub-document in iframe. + let event = waitForEvent(EVENT_REORDER, "iframe", "bodyReframe"); + getNode("iframe").src = + `data:text/html,<div>Hello</div><div style="display: none">World</div>`; + await event; + + // Initial tree should have one section leaf. + testAccessibleTree("c10",{ SECTION: [ + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { SECTION: [ + { role: ROLE_TEXT_LEAF, name: "Hello" } + ] } + ]} + ] } + ] }); + + + let iframeDoc = getNode("iframe").contentWindow.document; + + // Trigger coalesced reframing. Both the body node and its children + // will need reframing. + event = waitForEvent(EVENT_REORDER, iframeDoc, "bodyReframe"); + iframeDoc.body.style.display = "inline-block"; + iframeDoc.querySelector("div:first-child").style.display = "none"; + iframeDoc.querySelector("div:last-child").style.display = "block"; + + await event; + + // Only the second section should be showing + testAccessibleTree("c10",{ SECTION: [ + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { SECTION: [ + { role: ROLE_TEXT_LEAF, name: "World" } + ] } + ]} + ] } + ] }); + } + + // Ensure that embed elements recreate their Accessible if they started + // without an src and then an src is set later. + async function embedBecomesOuterDoc() { + let msg = "embedBecomesOuterDoc"; + info(msg); + + testAccessibleTree("c12", { SECTION: [ + { TEXT: [] } + ] }); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, "embed"], + [EVENT_SHOW, "embed"], + [EVENT_REORDER, "c12"], + ], msg); + getNode("embed").src = "data:text/html,"; + await events; + + testAccessibleTree("c12", { SECTION: [ + { INTERNAL_FRAME: [ + { DOCUMENT: [] } + ] } + ] }); + } + + // Test that we get a text removed event when removing generated content from a button + async function testCSSGeneratedContentRemovedFromButton() { + let msg = "testCSSGeneratedContentRemovedFromButton"; + info(msg); + + testAccessibleTree("c13", { SECTION: [ + { role: ROLE_PUSHBUTTON, name: "beforego", + children: [{ STATICTEXT: [] }, { TEXT_LEAF: [] }] } + ] }); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, evt => evt.accessible.name == "before"], + [EVENT_TEXT_REMOVED, evt => evt.accessible.role == ROLE_PUSHBUTTON], + [EVENT_SHOW, evt => evt.DOMNode.tagName == "HR"], + [EVENT_REORDER, "c13"], + ], msg); + getNode("b13").click(); + await events; + + testAccessibleTree("c13", { SECTION: [ + { role: ROLE_PUSHBUTTON, name: "go", + children: [{ TEXT_LEAF: [] }] }, + { SEPARATOR: [] } + ] }); + } + + // Slack seems to often restyle containers and change children + // simultaneously, this results in an insertion queue filled with + // redundant insertions and unparented nodes. + // This test duplicates some of this. + async function testSlack() { + let msg = "testSlack"; + info(msg); + + window.windowUtils.advanceTimeAndRefresh(100); + let event = waitForEvent(EVENT_REORDER, "c14", "testSlack"); + + let keyContainer = document.querySelector("#c14 .intermediate"); + keyContainer.style.display = "inline-block"; + document.body.offsetTop; // Flush layout. + + let one = document.querySelector("#c14 [aria-label='one']"); + let three = document.querySelector("#c14 [aria-label='three']"); + one.remove(); + three.remove(); + // insert one first + keyContainer.firstChild.before(one.cloneNode()); + // insert three last + keyContainer.lastChild.after(three.cloneNode()); + + keyContainer.style.display = "flex"; + document.body.offsetTop; // Flush layout. + + window.windowUtils.restoreNormalRefresh(); + + await event; + + is(getAccessible("c14").name, "one two three", "subtree has correct order"); + } + + // Ensure that a node is removed when visibility: hidden is set but the + // layout frame is reconstructed; e.g. because of position: fixed. Also + // ensure that visible children aren't clobbered. + async function visibilityHiddenWithReframe() { + let msg = "visibilityHiddenWithReframe"; + info(msg); + + testAccessibleTree("c15", { SECTION: [ // c15 + { SECTION: [ // c15_inner + { TEXT_LEAF: [] }, // Text + { PARAGRAPH: [ + { TEXT_LEAF: [] } // Para + ] }, + { HEADING: [ // c15_visible + { TEXT_LEAF: [] } // Visible + ] }, // c15_visible + ] } // c15_inner + ] }); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, "c15_inner"], + [EVENT_SHOW, "c15_visible"], + [EVENT_REORDER, "c15"], + ], msg); + getNode("c15_inner").style = "visibility: hidden; position: fixed;"; + await events; + + testAccessibleTree("c15", { SECTION: [ // c15 + { HEADING: [ // c15_visible + { TEXT_LEAF: [] } // Visible + ] }, // c15_visible + ] }); + } + + async function doTest() { + await hideDivFromInsideSpan(); + + await showDivFromInsideSpan(); + + await removeDivFromInsideSpan(); + + await addCSSGeneratedContent(); + + await removeCSSGeneratedContent(); + + await intermediateNonAccessibleContainers(); + + await intermediateNonAccessibleContainerBecomesAccessible(); + + await removeRelocatedWhenDomAncestorHidden(); + + await removeShadowRootHost(); + + listItemReframe(); + + await bodyReframe(); + + await embedBecomesOuterDoc(); + + await testCSSGeneratedContentRemovedFromButton(); + + await testSlack(); + + await visibilityHiddenWithReframe(); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1"> + <span role="region" id="span1" aria-label="region"><div id="div1">hello</div></span> + </div> + + <div id="c2"> + <span role="region" id="span2" aria-label="region"><div id="div2" style="display: none">hello</div></span> + </div> + + <div id="c3"> + <span role="region" id="span3" aria-label="region"><div id="div3">hello</div></span> + </div> + + <div id="c4"><div id="c4_child">text</div></div> + + <div id="c5"><div id="c5_child" class="gentext">text</div></div> + + <div id="c6"> + <div id="scrollarea" style="overflow:hidden;"> + <div><div role="none"><button id="b1">Hello</button></div><div role="none" style="display: none"><button id="b2">Goodbye</button></div></div> + </div> + </div> + + <div id="c7"> + <div style="display: inline;" class="intermediate"> + <button id="b3">Hello</button> + </div> + </div> + + <div id="c8"> + <div id="c8_owner" role="combobox" aria-owns="c8_owned"></div> + <div id="c8_owned_container"> + <div id="c8_owned" role="listbox"></div> + </div> + </div> + + <div id="c9"> + <div><dir>a</dir></div> + </div> + + <div id="c11"> + <ul> + <li id="li">Test</li> + </ul> + </div> + + <div id="c12"><embed id="embed"></embed></div> + + <div id="c10"> + <iframe id="iframe"></iframe> + </div> + + <div id="c13"> + <style> + .before::before { content: 'before' } + </style> + <button id="b13" class="before" onclick="this.className = ''; this.insertAdjacentElement('afterend', document.createElement('hr'))">go</button> + </div> + + <div role="heading" id="c14" data-qa="virtual-list-item"> + <div class="intermediate"> + <div role="img" aria-label="one"></div> two <div role="img" + aria-label="three"></div> + </div> + </div> + + <div id="c15"><div id="c15_inner"> + Text + <p>Para</p> + <h1 id="c15_visible" style="visibility: visible;">Visible</h1> + </div></div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_doc.html b/accessible/tests/mochitest/treeupdate/test_doc.html new file mode 100644 index 0000000000..6bb2863df4 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_doc.html @@ -0,0 +1,415 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test document root content mutations</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Helpers + + function getDocNode(aID) { + return getNode(aID).contentDocument; + } + function getDocChildNode(aID) { + return getDocNode(aID).body.firstChild; + } + + function rootContentReplaced(aID, aTextName, aRootContentRole) { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getDocChildNode, aID), + new invokerChecker(EVENT_REORDER, getDocNode, aID), + ]; + + this.finalCheck = function rootContentReplaced_finalCheck() { + var tree = { + role: aRootContentRole || ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: aTextName, + }, + ], + }; + testAccessibleTree(getDocNode(aID), tree); + }; + } + + function rootContentRemoved(aID) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, null), + new invokerChecker(EVENT_REORDER, getDocNode, aID), + ]; + + this.preinvoke = function rootContentRemoved_preinvoke() { + // Set up target for hide event before we invoke. + var text = getAccessible(getAccessible(getDocNode(aID)).firstChild); + this.eventSeq[0].target = text; + }; + + this.finalCheck = function rootContentRemoved_finalCheck() { + var tree = { + role: ROLE_DOCUMENT, + children: [ ], + }; + testAccessibleTree(getDocNode(aID), tree); + }; + } + + function rootContentInserted(aID, aTextName) { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getDocChildNode, aID), + new invokerChecker(EVENT_REORDER, getDocNode, aID), + ]; + + this.finalCheck = function rootContentInserted_finalCheck() { + var tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_TEXT_LEAF, + name: aTextName, + }, + ], + }; + testAccessibleTree(getDocNode(aID), tree); + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + function writeIFrameDoc(aID) { + this.__proto__ = new rootContentReplaced(aID, "hello"); + + this.invoke = function writeIFrameDoc_invoke() { + var docNode = getDocNode(aID); + + // We can't use open/write/close outside of iframe document because of + // security error. + var script = docNode.createElement("script"); + script.textContent = "document.open(); document.write('hello'); document.close();"; + docNode.body.appendChild(script); + }; + + this.getID = function writeIFrameDoc_getID() { + return "write document"; + }; + } + + /** + * Replace HTML element. + */ + function replaceIFrameHTMLElm(aID) { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getDocChildNode, aID), + new invokerChecker(EVENT_REORDER, getDocNode, aID), + ]; + + this.invoke = function replaceIFrameHTMLElm_invoke() { + var docNode = getDocNode(aID); + var newHTMLNode = docNode.createElement("html"); + newHTMLNode.innerHTML = `<body><p>New Wave</p></body`; + docNode.replaceChild(newHTMLNode, docNode.documentElement); + }; + + this.finalCheck = function replaceIFrameHTMLElm_finalCheck() { + var tree = { + role: ROLE_DOCUMENT, + children: [ + { + role: ROLE_PARAGRAPH, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "New Wave", + }, + ], + }, + ], + }; + testAccessibleTree(getDocNode(aID), tree); + }; + + this.getID = function replaceIFrameHTMLElm_getID() { + return "replace HTML element"; + }; + } + + /** + * Replace HTML body on new body having ARIA role. + */ + function replaceIFrameBody(aID) { + this.__proto__ = new rootContentReplaced(aID, "New Hello"); + + this.invoke = function replaceIFrameBody_invoke() { + var docNode = getDocNode(aID); + var newBodyNode = docNode.createElement("body"); + var newTextNode = docNode.createTextNode("New Hello"); + newBodyNode.appendChild(newTextNode); + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + }; + + this.getID = function replaceIFrameBody_getID() { + return "replace body"; + }; + } + + /** + * Replace HTML body on new body having ARIA role. + */ + function replaceIFrameBodyOnARIARoleBody(aID) { + this.__proto__ = new rootContentReplaced(aID, "New Hello", + ROLE_APPLICATION); + + this.invoke = function replaceIFrameBodyOnARIARoleBody_invoke() { + var docNode = getDocNode(aID); + var newBodyNode = docNode.createElement("body"); + var newTextNode = docNode.createTextNode("New Hello"); + newBodyNode.appendChild(newTextNode); + newBodyNode.setAttribute("role", "application"); + docNode.documentElement.replaceChild(newBodyNode, docNode.body); + }; + + this.getID = function replaceIFrameBodyOnARIARoleBody_getID() { + return "replace body on body having ARIA role"; + }; + } + + /** + * Open/close document pair. + */ + function openIFrameDoc(aID) { + this.__proto__ = new rootContentRemoved(aID); + + this.invoke = function openIFrameDoc_invoke() { + this.preinvoke(); + + // Open document. + var docNode = getDocNode(aID); + var script = docNode.createElement("script"); + script.textContent = "function closeMe() { document.write('Works?'); document.close(); } window.closeMe = closeMe; document.open();"; + docNode.body.appendChild(script); + }; + + this.getID = function openIFrameDoc_getID() { + return "open document"; + }; + } + + function closeIFrameDoc(aID) { + this.__proto__ = new rootContentInserted(aID, "Works?"); + + this.invoke = function closeIFrameDoc_invoke() { + // Write and close document. + getDocNode(aID).write("Works?"); getDocNode(aID).close(); + }; + + this.getID = function closeIFrameDoc_getID() { + return "close document"; + }; + } + + /** + * Remove/insert HTML element pair. + */ + function removeHTMLFromIFrameDoc(aID) { + this.__proto__ = new rootContentRemoved(aID); + + this.invoke = function removeHTMLFromIFrameDoc_invoke() { + this.preinvoke(); + + // Remove HTML element. + var docNode = getDocNode(aID); + docNode.firstChild.remove(); + }; + + this.getID = function removeHTMLFromIFrameDoc_getID() { + return "remove HTML element"; + }; + } + + function insertHTMLToIFrameDoc(aID) { + this.__proto__ = new rootContentInserted(aID, "Haha"); + + this.invoke = function insertHTMLToIFrameDoc_invoke() { + // Insert HTML element. + var docNode = getDocNode(aID); + var html = docNode.createElement("html"); + var body = docNode.createElement("body"); + var text = docNode.createTextNode("Haha"); + body.appendChild(text); + html.appendChild(body); + docNode.appendChild(html); + }; + + this.getID = function insertHTMLToIFrameDoc_getID() { + return "insert HTML element document"; + }; + } + + /** + * Remove/insert HTML body pair. + */ + function removeBodyFromIFrameDoc(aID) { + this.__proto__ = new rootContentRemoved(aID); + + this.invoke = function removeBodyFromIFrameDoc_invoke() { + this.preinvoke(); + + // Remove body element. + var docNode = getDocNode(aID); + docNode.documentElement.removeChild(docNode.body); + }; + + this.getID = function removeBodyFromIFrameDoc_getID() { + return "remove body element"; + }; + } + + function insertElmUnderDocElmWhileBodyMissed(aID) { + this.docNode = null; + this.inputNode = null; + + function getInputNode() { return this.inputNode; } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getInputNode.bind(this)), + new invokerChecker(EVENT_REORDER, getDocNode, aID), + ]; + + this.invoke = function invoke() { + this.docNode = getDocNode(aID); + this.inputNode = this.docNode.createElement("input"); + this.docNode.documentElement.appendChild(this.inputNode); + }; + + this.finalCheck = function finalCheck() { + var tree = + { DOCUMENT: [ + { ENTRY: [ ] }, + ] }; + testAccessibleTree(this.docNode, tree); + + // Remove aftermath of this test before next test starts. + this.docNode.documentElement.removeChild(this.inputNode); + }; + + this.getID = function getID() { + return "Insert element under document element while body is missed."; + }; + } + + function insertBodyToIFrameDoc(aID) { + this.__proto__ = new rootContentInserted(aID, "Yo ho ho i butylka roma!"); + + this.invoke = function insertBodyToIFrameDoc_invoke() { + // Insert body element. + var docNode = getDocNode(aID); + var body = docNode.createElement("body"); + var text = docNode.createTextNode("Yo ho ho i butylka roma!"); + body.appendChild(text); + docNode.documentElement.appendChild(body); + }; + + this.getID = function insertBodyToIFrameDoc_getID() { + return "insert body element"; + }; + } + + function changeSrc(aID) { + this.containerNode = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.containerNode), + ]; + + this.invoke = function changeSrc_invoke() { + this.containerNode.src = "data:text/html,<html><input></html>"; + }; + + this.finalCheck = function changeSrc_finalCheck() { + var tree = + { INTERNAL_FRAME: [ + { DOCUMENT: [ + { ENTRY: [ ] }, + ] }, + ] }; + testAccessibleTree(this.containerNode, tree); + }; + + this.getID = function changeSrc_getID() { + return "change src on iframe"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpToConsole = true; + // enableLogging('tree,verbose'); + + var gQueue = null; + + function doTest() { + gQueue = new eventQueue(); + + gQueue.push(new writeIFrameDoc("iframe")); + gQueue.push(new replaceIFrameHTMLElm("iframe")); + gQueue.push(new replaceIFrameBody("iframe")); + gQueue.push(new openIFrameDoc("iframe")); + gQueue.push(new closeIFrameDoc("iframe")); + gQueue.push(new removeHTMLFromIFrameDoc("iframe")); + gQueue.push(new insertHTMLToIFrameDoc("iframe")); + gQueue.push(new removeBodyFromIFrameDoc("iframe")); + gQueue.push(new insertElmUnderDocElmWhileBodyMissed("iframe")); + gQueue.push(new insertBodyToIFrameDoc("iframe")); + gQueue.push(new changeSrc("iframe")); + gQueue.push(new replaceIFrameBodyOnARIARoleBody("iframe")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Update accessible tree when root element is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=606082">Mozilla Bug 606082</a> + <a target="_blank" + title="Elements inserted outside the body aren't accessible" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a> + <a target="_blank" + title="Reorder event for document must be fired after document initial tree creation" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=669263">Mozilla Bug 669263</a> + <a target="_blank" + title="Changing the HTML body doesn't pick up ARIA role" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=818407">Mozilla Bug 818407</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <iframe id="iframe"></iframe> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_gencontent.html b/accessible/tests/mochitest/treeupdate/test_gencontent.html new file mode 100644 index 0000000000..9a0c107133 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_gencontent.html @@ -0,0 +1,187 @@ +<html> + +<head> + <title>Elements with CSS generated content</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <style> + .gentext:before { + content: "START" + } + .gentext:after { + content: "END" + } + </style> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + // ////////////////////////////////////////////////////////////////////////// + + /** + * Insert new node with CSS generated content style applied to container. + */ + function insertNodeHavingGenContent(aContainerID) { + this.containerNode = getNode(aContainerID); + this.container = getAccessible(this.containerNode); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getFirstChild, this.container), + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function insertNodeHavingGenContent_invoke() { + var node = document.createElement("div"); + node.textContent = "text"; + node.setAttribute("class", "gentext"); + this.containerNode.appendChild(node); + }; + + this.finalCheck = function insertNodeHavingGenContent_finalCheck() { + var accTree = + { SECTION: [ // container + { SECTION: [ // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function insertNodeHavingGenContent_getID() { + return "insert node having generated content to " + prettyName(aContainerID); + }; + } + + /** + * Add CSS generated content to the given node contained by container node. + */ + function addGenContent(aContainerID, aNodeID) { + this.container = getAccessible(aContainerID); + this.nodeAcc = getAccessible(aNodeID); + this.node = getNode(aNodeID); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getFirstChild, this.nodeAcc), + new invokerChecker(EVENT_SHOW, getLastChild, this.nodeAcc), + new invokerChecker(EVENT_REORDER, this.nodeAcc), + ]; + + this.invoke = function addGenContent_invoke() { + this.node.classList.add("gentext"); + }; + + this.finalCheck = function insertNodeHavingGenContent_finalCheck() { + var accTree = + { SECTION: [ // container + { SECTION: [ // inserted node + { STATICTEXT: [] }, // :before + { TEXT_LEAF: [] }, // primary text + { STATICTEXT: [] }, // :after + ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function addGenContent_getID() { + return "add generated content to" + prettyName(aNodeID); + }; + } + + /** + * Remove CSS generated content from the given node contained by container node. + */ + function removeGenContent(aContainerID, aNodeID) { + this.container = getAccessible(aContainerID); + this.nodeAcc = getAccessible(aNodeID); + this.node = getNode(aNodeID); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.nodeAcc.lastChild), + new invokerChecker(EVENT_HIDE, this.nodeAcc.firstChild), + new invokerChecker(EVENT_REORDER, this.nodeAcc), + ]; + + this.invoke = function removeGenContent_invoke() { + this.node.classList.remove("gentext"); + }; + + this.finalCheck = function removeGenContent_finalCheck() { + var accTree = + { SECTION: [ // container + { SECTION: [ // inserted node + { TEXT_LEAF: [] }, // primary text + ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function addGenContent_getID() { + return "remove generated content from" + prettyName(aNodeID); + }; + } + /** + * Target getters. + */ + function getFirstChild(aAcc) { + try { return aAcc.firstChild; } catch (e) { return null; } + } + + function getLastChild(aAcc) { + try { return aAcc.lastChild; } catch (e) { return null; } + } + + // ////////////////////////////////////////////////////////////////////////// + // Do tests + // ////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + function doTests() { + gQueue = new eventQueue(); + + gQueue.push(new insertNodeHavingGenContent("container1")); + gQueue.push(new addGenContent("container2", "container2_child")); + gQueue.push(new removeGenContent("container3", "container3_child")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=646350" + title="Add a test for dynamic chnages of CSS generated content"> + Mozilla Bug 646350</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="eventdump"></div> + + <div id="container1"></div> + <div id="container2"><div id="container2_child">text</div></div> + <div id="container3"><div id="container3_child" class="gentext">text</div></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_general.html b/accessible/tests/mochitest/treeupdate/test_general.html new file mode 100644 index 0000000000..8129cae98a --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_general.html @@ -0,0 +1,174 @@ +<html> + +<head> + <title>Testing the tree updates</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + // ////////////////////////////////////////////////////////////////////////// + + function prependAppend(aContainer) { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer), + ]; + + this.invoke = function prependAppend_invoke() { + var checkbox = document.createElement("input"); + checkbox.setAttribute("type", "checkbox"); + getNode(aContainer).insertBefore(checkbox, getNode(aContainer).firstChild); + + var button = document.createElement("input"); + button.setAttribute("type", "button"); + getNode(aContainer).appendChild(button); + }; + + this.finalCheck = function prependAppend_finalCheck() { + var accTree = + { SECTION: [ // container + { CHECKBUTTON: [ ] }, + { ENTRY: [ ] }, + { PUSHBUTTON: [ ] }, + ] }; + testAccessibleTree(aContainer, accTree); + }; + + this.getID = function prependAppend_getID() { + return "prepends a child and appends a child"; + }; + } + + function removeRemove(aContainer) { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer), + ]; + + this.invoke = function removeRemove_invoke() { + getNode(aContainer).firstChild.remove(); + }; + + this.finalCheck = function removeRemove_finalCheck() { + var accTree = + { SECTION: [ // container + { PUSHBUTTON: [ ] }, + ] }; + testAccessibleTree(aContainer, accTree); + }; + + this.getID = function removeRemove_getID() { + return "remove first and second children"; + }; + } + + function insertInaccessibleAccessibleSiblings() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "c3"), + ]; + + this.invoke = function insertInaccessibleAccessibleSiblings_invoke() { + getNode("c3").appendChild(document.createElement("span")); + getNode("c3").appendChild(document.createElement("input")); + }; + + this.finalCheck = function insertInaccessibleAccessibleSiblings_finalCheck() { + var accTree = + { SECTION: [ // container + { PUSHBUTTON: [ + { TEXT_LEAF: [] }, + ] }, + { ENTRY: [ ] }, + ] }; + testAccessibleTree("c3", accTree); + }; + + this.getID = function insertInaccessibleAccessibleSiblings_getID() { + return "insert inaccessible and then accessible siblings"; + }; + } + + // Test for bug 1500416. + function displayContentsInsertion() { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, "c4"), + ]; + + this.invoke = function displayContentsInsertion_invoke() { + document.body.offsetTop; // Flush layout. + + let list = document.createElement("ul"); + list.style.display = "contents"; + list.appendChild(document.createElement("li")); + list.firstChild.appendChild(document.createTextNode("Text")); + getNode("c4").appendChild(list); + }; + + this.finalCheck = function displayContentsInsertion_finalCheck() { + var accTree = + { SECTION: [ // container + { LIST: [ + { LISTITEM: [ + { LISTITEM_MARKER: [] }, + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree("c4", accTree); + }; + + this.getID = function displayContentsInsertion_getID() { + return "insert accessible display: contents element."; + }; + } + + + // ////////////////////////////////////////////////////////////////////////// + // Do tests + // ////////////////////////////////////////////////////////////////////////// + + var gQueue = null; + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + function doTests() { + gQueue = new eventQueue(); + + gQueue.push(new prependAppend("c1")); + gQueue.push(new removeRemove("c2")); + gQueue.push(new insertInaccessibleAccessibleSiblings()); + gQueue.push(new displayContentsInsertion()); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTests); + </script> +</head> + +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1"><input></div> + <div id="c2"><span><input type="checkbox"><input></span><input type="button"></div> + + <div id="c3"><input type="button" value="button"></div> + <div id="c4"></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_hidden.html b/accessible/tests/mochitest/treeupdate/test_hidden.html new file mode 100644 index 0000000000..e687fc97c9 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_hidden.html @@ -0,0 +1,125 @@ +<!DOCTYPE html> +<html> + +<head> + <title>@hidden attribute testing</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + // ////////////////////////////////////////////////////////////////////////// + + /** + * Set @hidden attribute + */ + function setHiddenAttr(aContainerID, aChildID) { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function setHiddenAttr_invoke() { + var tree = + { SECTION: [ + { ENTRY: [ + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aChildID).setAttribute("hidden", "true"); + }; + + this.finalCheck = function setHiddenAttr_finalCheck() { + var tree = + { SECTION: [ + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function setHiddenAttr_getID() { + return "Set @hidden attribute on input and test accessible tree for div"; + }; + } + + /** + * Remove @hidden attribute + */ + function removeHiddenAttr(aContainerID, aChildID) { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function removeHiddenAttr_invoke() { + var tree = + { SECTION: [ + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aChildID).removeAttribute("hidden"); + }; + + this.finalCheck = function removeHiddenAttr_finalCheck() { + var tree = + { SECTION: [ + { ENTRY: [ + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function removeHiddenAttr_getID() { + return "Remove @hidden attribute on input and test accessible tree for div"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + // ////////////////////////////////////////////////////////////////////////// + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() { + gQueue = new eventQueue(); + + gQueue.push(new setHiddenAttr("container", "child")); + gQueue.push(new removeHiddenAttr("container", "child")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + + </script> + +</head> + +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container"><input id="child"></div> + + <div id="eventdump"></div> + +</body> + +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_imagemap.html b/accessible/tests/mochitest/treeupdate/test_imagemap.html new file mode 100644 index 0000000000..7befef6905 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_imagemap.html @@ -0,0 +1,402 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML img map accessible tree update tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function insertArea(aImageMapID, aMapID) { + this.imageMap = getAccessible(aImageMapID); + this.mapNode = getNode(aMapID); + + function getInsertedArea(aThisObj) { + return aThisObj.imageMap.firstChild; + } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getInsertedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap), + ]; + + this.invoke = function insertArea_invoke() { + var areaElm = document.createElement("area"); + areaElm.setAttribute("href", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://www.bbc.co.uk/radio4/atoz/index.shtml#a"); + areaElm.setAttribute("coords", "0,0,13,14"); + areaElm.setAttribute("alt", "a"); + areaElm.setAttribute("shape", "rect"); + + this.mapNode.insertBefore(areaElm, this.mapNode.firstChild); + }; + + this.finalCheck = function insertArea_finalCheck() { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "a", + children: [ ], + }, + { + role: ROLE_LINK, + name: "b", + children: [ ], + }, + ] }; + testAccessibleTree(this.imageMap, accTree); + }; + + this.getID = function insertArea_getID() { + return "insert area element"; + }; + } + + function appendArea(aImageMapID, aMapID) { + this.imageMap = getAccessible(aImageMapID); + this.mapNode = getNode(aMapID); + + function getAppendedArea(aThisObj) { + return aThisObj.imageMap.lastChild; + } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAppendedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap), + ]; + + this.invoke = function appendArea_invoke() { + var areaElm = document.createElement("area"); + areaElm.setAttribute("href", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://www.bbc.co.uk/radio4/atoz/index.shtml#c"); + areaElm.setAttribute("coords", "34,0,47,14"); + areaElm.setAttribute("alt", "c"); + areaElm.setAttribute("shape", "rect"); + + this.mapNode.appendChild(areaElm); + }; + + this.finalCheck = function appendArea_finalCheck() { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "a", + children: [ ], + }, + { + role: ROLE_LINK, + name: "b", + children: [ ], + }, + { + role: ROLE_LINK, + name: "c", + children: [ ], + }, + ] }; + testAccessibleTree(this.imageMap, accTree); + }; + + this.getID = function appendArea_getID() { + return "append area element"; + }; + } + + function removeArea(aImageMapID, aMapID) { + this.imageMap = getAccessible(aImageMapID); + this.area = null; + this.mapNode = getNode(aMapID); + + function getRemovedArea(aThisObj) { + return aThisObj.area; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getRemovedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap), + ]; + + this.invoke = function removeArea_invoke() { + this.area = this.imageMap.firstChild; + this.mapNode.removeChild(this.mapNode.firstElementChild); + }; + + this.finalCheck = function removeArea_finalCheck() { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "b", + children: [ ], + }, + { + role: ROLE_LINK, + name: "c", + children: [ ], + }, + ] }; + testAccessibleTree(this.imageMap, accTree); + }; + + this.getID = function removeArea_getID() { + return "remove area element"; + }; + } + + function removeNameOnMap(aImageMapContainerID, aImageMapID, aMapID) { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = getAccessible(aImageMapID); + this.imgNode = this.imageMap.DOMNode; + this.mapNode = getNode(aMapID); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.imageMap), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function removeNameOnMap_invoke() { + this.mapNode.removeAttribute("name"); + }; + + this.finalCheck = function removeNameOnMap_finalCheck() { + var accTree = + { SECTION: [ + { GRAPHIC: [ ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function removeNameOnMap_getID() { + return "remove @name on map element"; + }; + } + + function restoreNameOnMap(aImageMapContainerID, aImageMapID, aMapID) { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = null; + this.imgNode = getNode(aImageMapID); + this.mapNode = getNode(aMapID); + + function getImageMap(aThisObj) { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function restoreNameOnMap_invoke() { + this.imageMap = getAccessible(aImageMapID); + this.mapNode.setAttribute("name", "atoz_map"); + + // XXXhack: force repainting of the image (see bug 745788 for details). + waveOverImageMap(aImageMapID); + }; + + this.finalCheck = function removeNameOnMap_finalCheck() { + var accTree = + { SECTION: [ + { IMAGE_MAP: [ + { LINK: [ ] }, + { LINK: [ ] }, + ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function removeNameOnMap_getID() { + return "restore @name on map element"; + }; + } + + function removeMap(aImageMapContainerID, aImageMapID, aMapID) { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = null; + this.imgNode = getNode(aImageMapID); + this.mapNode = getNode(aMapID); + + function getImageMap(aThisObj) { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function removeMap_invoke() { + this.imageMap = getAccessible(aImageMapID); + this.mapNode.remove(); + }; + + this.finalCheck = function removeMap_finalCheck() { + var accTree = + { SECTION: [ + { GRAPHIC: [ ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function removeMap_getID() { + return "remove map element"; + }; + } + + function insertMap(aImageMapContainerID, aImageID) { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.image = null; + this.imgMapNode = getNode(aImageID); + + function getImage(aThisObj) { + return aThisObj.image; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImage, this), + new invokerChecker(EVENT_SHOW, this.imgMapNode), + new invokerChecker(EVENT_REORDER, this.container), + ]; + + this.invoke = function insertMap_invoke() { + this.image = getAccessible(aImageID); + + var map = document.createElement("map"); + map.setAttribute("name", "atoz_map"); + map.setAttribute("id", "map"); + + var area = document.createElement("area"); + area.setAttribute("href", + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://www.bbc.co.uk/radio4/atoz/index.shtml#b"); + area.setAttribute("coords", "17,0,30,14"); + area.setAttribute("alt", "b"); + area.setAttribute("shape", "rect"); + + map.appendChild(area); + + this.containerNode.appendChild(map); + }; + + this.finalCheck = function insertMap_finalCheck() { + var accTree = + { SECTION: [ + { IMAGE_MAP: [ + { LINK: [ ] }, + ] }, + ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function insertMap_getID() { + return "insert map element"; + }; + } + + function hideImageMap(aContainerID, aImageID) { + this.container = getAccessible(aContainerID); + this.imageMap = null; + this.imageMapNode = getNode(aImageID); + + function getImageMap(aThisObj) { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_REORDER, aContainerID), + ]; + + this.invoke = function hideImageMap_invoke() { + this.imageMap = getAccessible(this.imageMapNode); + this.imageMapNode.style.display = "none"; + }; + + this.finalCheck = function hideImageMap_finalCheck() { + var accTree = + { SECTION: [ ] }; + testAccessibleTree(this.container, accTree); + }; + + this.getID = function hideImageMap_getID() { + return "display:none image"; + }; + } + + // gA11yEventDumpToConsole = true; // debug stuff + function doPreTest() { + waitForImageMap("imgmap", doTest); + } + + var gQueue = null; + function doTest() { + gQueue = new eventQueue(); + + gQueue.push(new insertArea("imgmap", "map")); + gQueue.push(new appendArea("imgmap", "map")); + gQueue.push(new removeArea("imgmap", "map")); + gQueue.push(new removeNameOnMap("container", "imgmap", "map")); + gQueue.push(new restoreNameOnMap("container", "imgmap", "map")); + gQueue.push(new removeMap("container", "imgmap", "map")); + gQueue.push(new insertMap("container", "imgmap")); + gQueue.push(new hideImageMap("container", "imgmap")); + + // enableLogging("tree"); // debug stuff + // gQueue.onFinish = function() { disableLogging("tree"); } + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doPreTest); + </script> + +</head> +<body> + + <a target="_blank" + title="Image map accessible tree is not updated when image map is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=732389"> + Mozilla Bug 732389 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <map name="atoz_map" id="map"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b" + coords="17,0,30,14" alt="b" shape="rect"> + </map> + + <div id="container"> + <img id="imgmap" width="447" height="15" + usemap="#atoz_map" + src="../letters.gif"><!-- + Important: no whitespace between the <img> and the </div>, so we + don't end up with textframes there, because those would be reflected + in our accessible tree in some cases. + --></div> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_inert.html b/accessible/tests/mochitest/treeupdate/test_inert.html new file mode 100644 index 0000000000..0364f6612b --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_inert.html @@ -0,0 +1,138 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Inert tree update test</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script src="../common.js"></script> + <script src="../promisified-events.js"></script> + <script src="../role.js"></script> + + <script> + async function doTest() { + if (SpecialPowers.getBoolPref("html5.inert.enabled")) { + const inertContainer = getAccessible("inertContainer"); + const inertTree = { SECTION: [ // inertContainer + { PARAGRAPH: [{ TEXT_LEAF: [] }] }, // before + { PARAGRAPH: [{ TEXT_LEAF: [] }] }, // after + ]}; + testAccessibleTree(inertContainer, inertTree); + + info("Unsetting inert"); + let reordered = waitForEvent(EVENT_REORDER, inertContainer); + const inertNode = getNode("inert"); + inertNode.inert = false; + await reordered; + testAccessibleTree(inertContainer, { SECTION: [ // inertContainer + { PARAGRAPH: [{ TEXT_LEAF: [] }] }, // before + { SECTION: [ // inert + { TEXT_LEAF: [] }, + { PUSHBUTTON: [] }, + ] }, + { PARAGRAPH: [{ TEXT_LEAF: [] }] }, // after + ]}); + + info("Setting inert"); + reordered = waitForEvent(EVENT_REORDER, inertContainer); + inertNode.inert = true; + await reordered; + testAccessibleTree(inertContainer, inertTree); + } else { + // It's too late to flip the pref now. We'd need to load a new document + // to pick up the change. + todo(false, "Inert not enabled, skipping inert tests"); + } + + const noDialogTree = { SECTION: [ // dialogContainer + { TEXT_LEAF: [] } + ] }; + testAccessibleTree("dialogContainer", noDialogTree); + + info("Showing modal dialog"); + let reordered = waitForEvent(EVENT_REORDER, document); + const dialogNode = getNode("dialog"); + dialogNode.showModal(); + await reordered; + // The dialog makes everything else in the document inert. + testAccessibleTree(document, { DOCUMENT: [ + { DIALOG: [ + { PUSHBUTTON: [] } + ] } + ] }); + + info("Closing dialog"); + reordered = waitForEvent(EVENT_REORDER, document); + dialogNode.close(); + await reordered; + testAccessibleTree("dialogContainer", noDialogTree); + + const fullscreenTree = { SECTION: [ // fullscreen + { PUSHBUTTON: [] } + ] }; + const notFullscreenTree = { SECTION: [ // fullscreenContainer + { SECTION: [ + { PUSHBUTTON: [] } // requestFullscreen + ] }, + fullscreenTree, + ] }; + testAccessibleTree("fullscreenContainer", notFullscreenTree); + + info("Requesting fullscreen"); + reordered = waitForEvent(EVENT_REORDER, document); + // Fullscreen must be requested by a user input event. + synthesizeMouseAtCenter(getNode("requestFullscreen"), {}); + await reordered; + // Fullscreen makes everything else in the document inert. + testAccessibleTree(document, { DOCUMENT: [ + fullscreenTree + ] }); + + info("Exiting fullscreen"); + reordered = waitForEvent(EVENT_REORDER, document); + document.exitFullscreen(); + await reordered; + testAccessibleTree("fullscreenContainer", notFullscreenTree); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="inertContainer"> + <p>before</p> + <div id="inert" inert>inert <button></button></div> + <p>after</p> + </div> + + <div id="dialogContainer"> + dialogContainer + <dialog id="dialog"><button></button></dialog> + </div> + + <div id="fullscreenContainer"> + <div> + <button id="requestFullscreen" + onclick="document.getElementById('fullscreen').requestFullscreen();"> + </button> + </div> + <div id="fullscreen"><button></button></div> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_inner_reorder.html b/accessible/tests/mochitest/treeupdate/test_inner_reorder.html new file mode 100644 index 0000000000..b4411833d7 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_inner_reorder.html @@ -0,0 +1,148 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test accessible delayed removal</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + + async function testInnerReorder() { + window.windowUtils.advanceTimeAndRefresh(100); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, "c1.1.1"], + [EVENT_SHOW, "c1.1.1"], + [EVENT_INNER_REORDER, "c1.1"], + [EVENT_REORDER, "c1"], + ], "events yay"); + + let child = getNode("c1.1.1"); + child.remove(); + getNode("c1").appendChild(child); + + window.windowUtils.restoreNormalRefresh(); + + await events; + } + + async function testInnerReorderEntry() { + window.windowUtils.advanceTimeAndRefresh(100); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, e => e.accessible.name == "hello"], + [EVENT_HIDE, "c2.2"], + [EVENT_INNER_REORDER, "c2.1"], + [EVENT_REORDER, "c2"], + [EVENT_TEXT_VALUE_CHANGE, "c2.1"], + ], "events yay"); + + getNode("c2.1.1").remove(); + getNode("c2.2").remove(); + + window.windowUtils.restoreNormalRefresh(); + + await events; + } + + async function testInnerReorderAriaOwns() { + let events = waitForOrderedEvents([ + [EVENT_HIDE, "c3.1.1"], + [EVENT_SHOW, "c3.1.1"], + [EVENT_INNER_REORDER, "c3.1"], + [EVENT_REORDER, "c3"], + ], "events yay"); + + getNode("c3").setAttribute("aria-owns", "c3.1.1"); + + await events; + + events = waitForOrderedEvents([ + [EVENT_HIDE, "c3.1.1"], + [EVENT_SHOW, "c3.1.1"], + [EVENT_INNER_REORDER, "c3.1"], + [EVENT_REORDER, "c3"], + ], "events yay"); + + getNode("c3").removeAttribute("aria-owns"); + + await events; + } + + async function testInnerContainerRemoved() { + window.windowUtils.advanceTimeAndRefresh(100); + + let events = waitForOrderedEvents([ + [EVENT_HIDE, "c4.1"], + [EVENT_SHOW, "c4.1.1"], + [EVENT_REORDER, "c4"], + ], "events yay"); + + let child = getNode("c4.1.1"); + child.remove(); + getNode("c1").appendChild(child); + getNode("c4.1").remove(); + + window.windowUtils.restoreNormalRefresh(); + + await events; + } + + + async function doTest() { + await testInnerReorder(); + + await testInnerReorderEntry(); + + await testInnerReorderAriaOwns(); + + await testInnerContainerRemoved(); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="c1"> + <div id="c1.1"><div id="c1.1.1">hello</div></div> + </div> + + <div id="c2"> + <div role="textbox" contenteditable="true" id="c2.1"> + <span id="c2.1.1">hello</span> + </div> + <input type="submit" id="c2.2"> + </div> + + <div id="c3"> + <div id="c3.1"><div id="c3.1.1"></div></div> + </div> + + <div id="c4"> + <div id="c4.1"><div id="c4.1.1">hello</div></div> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_list.html b/accessible/tests/mochitest/treeupdate/test_list.html new file mode 100644 index 0000000000..06c308e422 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_list.html @@ -0,0 +1,139 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test HTML li and listitem bullet accessible cache</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Helpers + + function testLiAccessibleTree() { + // Test accessible tree. + var accTree = { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_LISTITEM_MARKER, + children: [], + }, + { + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }; + + testAccessibleTree("li", accTree); + } + + // ////////////////////////////////////////////////////////////////////////// + // Sequence item processors + + function hideProcessor() { + this.liNode = getNode("li"); + this.li = getAccessible(this.liNode); + this.bullet = this.li.firstChild; + + this.process = function hideProcessor_process() { + this.liNode.style.display = "none"; + }; + + this.onProcessed = function hideProcessor_onProcessed() { + window.setTimeout( + function(aLiAcc, aLiNode, aBulletAcc) { + testDefunctAccessible(aLiAcc, aLiNode); + testDefunctAccessible(aBulletAcc); + + gSequence.processNext(); + }, + 0, this.li, this.liNode, this.bullet + ); + }; + } + + function showProcessor() { + this.liNode = getNode("li"); + + this.process = function showProcessor_process() { + this.liNode.style.display = "list-item"; + }; + + this.onProcessed = function showProcessor_onProcessed() { + testLiAccessibleTree(); + gSequence.processNext(); + }; + } + + function textReplaceProcessor() { + this.liNode = getNode("li"); + + this.process = function textReplaceProcessor_process() { + this.liNode.textContent = "hey"; + }; + + this.onProcessed = function textReplaceProcessor_onProcessed() { + var tree = { + LISTITEM: [ + { LISTITEM_MARKER: [] }, + { TEXT_LEAF: [] }, + ], + }; + testAccessibleTree(this.liNode, tree); + SimpleTest.finish(); + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpToConsole = true; + + var gSequence = null; + function doTest() { + testLiAccessibleTree(); + + gSequence = new sequence(); + + gSequence.append(new hideProcessor(), EVENT_HIDE, getAccessible("li"), + "hide HTML li"); + gSequence.append(new showProcessor(), EVENT_SHOW, getNode("li"), + "show HTML li"); + gSequence.append(new textReplaceProcessor(), EVENT_REORDER, getNode("li"), + "change text of HTML li"); + + gSequence.processNext(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="setParent shouldn't be virtual" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=496783">Mozilla Bug 496783</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ul> + <li id="li">item1</li> + </ul> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html b/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html new file mode 100644 index 0000000000..59b9dc9c53 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_list_editabledoc.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test HTML li and listitem bullet accessible insertion into editable document</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + function addLi(aID) { + this.listNode = getNode(aID); + this.liNode = document.createElement("li"); + this.liNode.textContent = "item"; + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAccessible, this.liNode), + new invokerChecker(EVENT_REORDER, this.listNode), + ]; + + this.invoke = function addLi_invoke() { + this.listNode.appendChild(this.liNode); + }; + + this.finalCheck = function addLi_finalCheck() { + var tree = { + role: ROLE_LIST, + children: [ + { + role: ROLE_LISTITEM, + children: [ + { + role: ROLE_LISTITEM_MARKER, + name: "1. ", + children: [], + }, + { + role: ROLE_TEXT_LEAF, + children: [], + }, + ], + }, + ], + }; + testAccessibleTree(aID, tree); + }; + + this.getID = function addLi_getID() { + return "add li"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "eventdump"; // debug stuff + + var gQueue = null; + + function doTest() { + gQueue = new eventQueue(); + + gQueue.push(new addLi("list")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body contentEditable="true"> + + <a target="_blank" + title="Wrong list bullet text of accessible for the first numbered HTML:li in CKEditor" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=557795">Mozilla Bug 557795</a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ol id="list"> + </ol> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_list_style.html b/accessible/tests/mochitest/treeupdate/test_list_style.html new file mode 100644 index 0000000000..1d9e1c00ca --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_list_style.html @@ -0,0 +1,181 @@ +<html> + +<head> + <title>Test hide/show events for HTMLListBulletAccessibles on list restyle</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../name.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + /** + * Change list style type to none. + */ + async function hideBullet() { + info("Hide bullet by setting style to none"); + + let liAcc = getAccessible("list_element"); + let bullet = liAcc.firstChild; + + let events = waitForEvents([ + [EVENT_HIDE, bullet], + [EVENT_REORDER, liAcc] + ]); + + getNode("list").style.setProperty("list-style-type", "none"); + + await events; + + is(liAcc.name, "list element", + "Check that first child of LI is not a bullet."); + + dumpTree("list"); + } + + /** + * Change list style type to circles. + */ + async function showBullet() { + info("Show bullet by setting style to circle"); + let liAcc = getAccessible("list_element"); + + let events = waitForEvents([ + [EVENT_SHOW, evt => evt.accessible.parent == liAcc], + [EVENT_REORDER, liAcc] + ]); + + getNode("list").style.setProperty("list-style-type", "circle"); + + await events; + + is(liAcc.name, "â—¦ list element", + "Check that first child of LI is a circle bullet."); + + dumpTree("list"); + } + + /** + * Change list style position. + */ + async function changeBulletPosition() { + info("Change list style position"); + let liAcc = getAccessible("list_element"); + + let events = waitForEvents([ + [EVENT_HIDE, evt => evt.accessible.role == ROLE_LISTITEM_MARKER], + [EVENT_SHOW, evt => evt.accessible.role == ROLE_LISTITEM_MARKER], + [EVENT_REORDER, liAcc] + ]); + + getNode("list").style.setProperty("list-style-position", "inside"); + + await events; + + is(liAcc.name, "â—¦ list element", + "Check that first child of LI is a circle bullet."); + } + + async function changeBulletPositionAndType() { + let events = waitForEvents([ + [EVENT_HIDE, evt => evt.accessible.role == ROLE_LISTITEM_MARKER], + [EVENT_REORDER, evt => evt.accessible.role == ROLE_LISTITEM] + ]); + + let list = getNode("inside-marker-list"); + + // Bug 1513447 - This changes the list type to "none" and the + // position implicitly to "outside". + list.style.setProperty("list-style", "none"); + list.offsetLeft + list.style.setProperty("list-style-type", "telugu"); + + await events; + } + + async function doTest() { + + testAccessibleTree("list", { LIST: [ // ol + { LISTITEM: [ // li + { LISTITEM_MARKER: [ ] }, + { TEXT_LEAF: [] }, + ] }, + ] } ); + + await hideBullet(); + + testAccessibleTree("list", { LIST: [ // ol + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + ] } ); + + await showBullet(); + + testAccessibleTree("list", { LIST: [ // ol + { LISTITEM: [ // li + { LISTITEM_MARKER: [ ] }, + { TEXT_LEAF: [] }, + ] }, + ] } ); + + await changeBulletPosition(); + + testAccessibleTree("list", { LIST: [ // ol + { LISTITEM: [ // li + { LISTITEM_MARKER: [ ] }, + { TEXT_LEAF: [] }, + ] }, + ] } ); + + testAccessibleTree("unmarked-list", { LIST: [ // ol + { LISTITEM: [ // li + { TEXT_LEAF: [] }, + ] }, + ] } ); + + await changeBulletPositionAndType(); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> + +</head> + +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100602" + title="[e10s] crash in mozilla::a11y::ProxyAccessible::Shutdown()"> + Mozilla Bug 1100602 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <ol id="list" style="list-style-type: circle;"> + <li id="list_element">list element</li> + </ol> + + <ol id="unmarked-list" style="list-style: none;"> + <li>list element</li> + </ol> + + <ol id="inside-marker-list" style="list-style-position: inside;"> + <li>list element</li> + </ol> + +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_listbox.xhtml b/accessible/tests/mochitest/treeupdate/test_listbox.xhtml new file mode 100644 index 0000000000..629c4b0915 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_listbox.xhtml @@ -0,0 +1,181 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL listbox hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Test + + function insertListitem(aListboxID) + { + this.listboxNode = getNode(aListboxID); + + this.listitemNode = document.createXULElement("richlistitem"); + var label = document.createXULElement("label"); + label.setAttribute("value", "item1"); + this.listitemNode.appendChild(label); + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, this.listitemNode), + new invokerChecker(EVENT_REORDER, this.listboxNode) + ]; + + this.invoke = function insertListitem_invoke() + { + this.listboxNode.insertBefore(this.listitemNode, + this.listboxNode.firstChild); + } + + this.finalCheck = function insertListitem_finalCheck() + { + var tree = + { LISTBOX: [ + { + role: ROLE_RICH_OPTION, + name: "item1" + }, + { + role: ROLE_RICH_OPTION, + name: "item2" + }, + { + role: ROLE_RICH_OPTION, + name: "item3" + }, + { + role: ROLE_RICH_OPTION, + name: "item4" + } + ] }; + testAccessibleTree(this.listboxNode, tree); + } + + this.getID = function insertListitem_getID() + { + return "insert listitem "; + } + } + + function removeListitem(aListboxID) + { + this.listboxNode = getNode(aListboxID); + this.listitemNode = null; + this.listitem; + + function getListitem(aThisObj) + { + return aThisObj.listitem; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getListitem, this), + new invokerChecker(EVENT_REORDER, this.listboxNode) + ]; + + this.invoke = function removeListitem_invoke() + { + this.listitemNode = this.listboxNode.firstChild; + this.listitem = getAccessible(this.listitemNode); + + this.listboxNode.removeChild(this.listitemNode); + } + + this.finalCheck = function removeListitem_finalCheck() + { + var tree = + { LISTBOX: [ + { + role: ROLE_RICH_OPTION, + name: "item2" + }, + { + role: ROLE_RICH_OPTION, + name: "item3" + }, + { + role: ROLE_RICH_OPTION, + name: "item4" + } + ] }; + testAccessibleTree(this.listboxNode, tree); + } + + this.getID = function removeListitem_getID() + { + return "remove listitem "; + } + } + + //gA11yEventDumpToConsole = true; // debug stuff + + var gQueue = null; + function doTest() + { + var tree = + { LISTBOX: [ + { + role: ROLE_RICH_OPTION, + name: "item2" + }, + { + role: ROLE_RICH_OPTION, + name: "item3" + }, + { + role: ROLE_RICH_OPTION, + name: "item4" + } + ] }; + testAccessibleTree("listbox", tree); + + gQueue = new eventQueue(); + gQueue.push(new insertListitem("listbox")); + gQueue.push(new removeListitem("listbox")); + gQueue.invoke(); // Will call SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=656225" + title="XUL listbox accessible tree doesn't get updated"> + Mozilla Bug 656225 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <richlistbox id="listbox"> + <richlistitem><label value="item2"/></richlistitem> + <richlistitem><label value="item3"/></richlistitem> + <richlistitem><label value="item4"/></richlistitem> + </richlistbox> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_menu.xhtml b/accessible/tests/mochitest/treeupdate/test_menu.xhtml new file mode 100644 index 0000000000..44042cc9e7 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_menu.xhtml @@ -0,0 +1,127 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL menu hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function openMenu(aID) + { + this.menuNode = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_FOCUS, this.menuNode) + ]; + + this.invoke = function openMenu_invoke() + { + var tree; + if (LINUX || SOLARIS) { + tree = + { PARENT_MENUITEM: [ ] }; + + } else { + tree = + { PARENT_MENUITEM: [ + { MENUPOPUP: [ ] } + ] }; + } + testAccessibleTree(aID, tree); + + // Show menu. + this.menuNode.open = true; + } + + this.finalCheck = function openMenu_finalCheck() + { + var tree; + if (LINUX || SOLARIS) { + tree = + { PARENT_MENUITEM: [ + { MENUITEM: [ ] }, + { MENUITEM: [ ] } + ] }; + + } else { + tree = + { PARENT_MENUITEM: [ + { MENUPOPUP: [ + { MENUITEM: [ ] }, + { MENUITEM: [ ] } + ] } + ] }; + } + testAccessibleTree(aID, tree); + } + + this.getID = function openMenu_getID() + { + return "open menu " + prettyName(aID); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + gQueue.push(new openMenu("menu")); + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu'"> + Mozilla Bug 249292 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children."> + Mozilla Bug 630486 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <menubar> + <menu id="menu" label="menu"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </menu> + </menubar> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_menubutton.xhtml b/accessible/tests/mochitest/treeupdate/test_menubutton.xhtml new file mode 100644 index 0000000000..d3eeb29f78 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_menubutton.xhtml @@ -0,0 +1,141 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL button hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + + //////////////////////////////////////////////////////////////////////////// + // Invokers + + function openMenu(aButtonID, aIsTree) + { + var menuItemRole = aIsTree ? ROLE_CHECK_MENU_ITEM : ROLE_MENUITEM; + this.button = getAccessible(aButtonID); + this.menupopup = aIsTree ? this.button.nextSibling : this.button.firstChild; + + var checker = new invokerChecker(EVENT_REORDER, this.menupopup); + this.__proto__ = new synthClick(aButtonID, checker); + + let testButton = popup => { + var children = []; + if (!aIsTree) { + children.push(popup); + } + var tree = { PUSHBUTTON: children }; + testAccessibleTree(this.button, tree); + testAccessibleTree(this.menupop, popup); + } + + this.invoke = function openMenu_invoke() + { + testButton({ MENUPOPUP: [ ] }); + this.__proto__.invoke(); + } + + this.finalCheck = function openMenu_finalCheck() + { + testButton({ MENUPOPUP: [ + { role: menuItemRole, children: [ ] }, + { role: menuItemRole, children: [ ] } + ] }); + + synthesizeKey("KEY_Escape"); + } + + this.getID = function openMenu_getID() + { + return "open menu of the button " + prettyName(aButtonID); + } + } + + //////////////////////////////////////////////////////////////////////////// + // Do test + + gA11yEventDumpToConsole = true; // debug stuff + + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new openMenu("button1")); + gQueue.push(new openMenu("button3")); + + var columnPickerBtn = getAccessible("tree").firstChild.lastChild.previousSibling; + gQueue.push(new openMenu(columnPickerBtn, true)); + gQueue.invoke(); // SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=249292" + title="Ensure accessible children for toolbarbutton types 'menu'"> + Bug 249292 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=630486" + title="Don't force accessible creation for popup children"> + Bug 630486 + </a> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=722265" + title="Column header selection popup no longer exposed to accessibility APIs"> + Bug 722265 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <button id="button1" type="menu" label="button"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </button> + + <toolbarbutton id="button3" type="menu" label="toolbarbutton"> + <menupopup> + <menuitem label="menuitem"/> + <menuitem label="menuitem"/> + </menupopup> + </toolbarbutton> + + <tree id="tree" flex="1"> + <treecols> + <treecol id="col" flex="1" primary="true" label="column"/> + <treecol id="col2" flex="1" label="another column"/> + </treecols> + <treechildren/> + </tree> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_optgroup.html b/accessible/tests/mochitest/treeupdate/test_optgroup.html new file mode 100644 index 0000000000..943b73be43 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_optgroup.html @@ -0,0 +1,122 @@ +<!DOCTYPE html> +<html> +<head> + <title>Add and remove optgroup test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + function addOptGroup(aID) { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function addOptGroup_invoke() { + var optGroup = document.createElement("optgroup"); + for (let i = 0; i < 2; i++) { + var opt = document.createElement("option"); + opt.value = i; + opt.text = "Option: Value " + i; + + optGroup.appendChild(opt); + } + + this.selectNode.add(optGroup, null); + var option = document.createElement("option"); + this.selectNode.add(option, null); + }; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList), + ]; + + this.finalCheck = function addOptGroup_finalCheck() { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { GROUPING: [ + { COMBOBOX_OPTION: [ ] }, + { COMBOBOX_OPTION: [ ] }, + ]}, + { COMBOBOX_OPTION: [] }, + ] }, + ] }; + testAccessibleTree(this.select, tree); + }; + + this.getID = function addOptGroup_getID() { + return "test optgroup's insertion into a select"; + }; + } + + function removeOptGroup(aID) { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function removeOptGroup_invoke() { + this.option1Node = this.selectNode.firstChild.firstChild; + this.selectNode.firstChild.remove(); + }; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList), + ]; + + this.finalCheck = function removeOptGroup_finalCheck() { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { COMBOBOX_OPTION: [] }, + ] }, + ] }; + testAccessibleTree(this.select, tree); + is(isAccessible(this.option1Node), false, "removed option shouldn't be accessible anymore!"); + }; + + this.getID = function removeOptGroup_getID() { + return "test optgroup's removal from a select"; + }; + } + + // gA11yEventDumpToConsole = true; + + function doTest() { + const gQueue = new eventQueue(); + + gQueue.push(new addOptGroup("select")); + gQueue.push(new removeOptGroup("select")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=616452" + title="Bug 616452 - Dynamically inserted select options aren't reflected in accessible tree"> + Bug 616452</a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <select id="select"></select> + + <div id="debug"/> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_recreation.html b/accessible/tests/mochitest/treeupdate/test_recreation.html new file mode 100644 index 0000000000..d403b0890e --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_recreation.html @@ -0,0 +1,93 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test accessible recreation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + + async function doTest() { + let events, msg; + + msg = "Assign a 'button' role to a span"; + events = waitForOrderedEvents( + [[EVENT_HIDE], [EVENT_SHOW, "span"], [EVENT_REORDER, "container"]], msg); + document.getElementById("span").setAttribute("role", "button"); + await events; + + msg = "Remove the 'button' role from a span"; + events = waitForOrderedEvents( + [[EVENT_HIDE, "span"], [EVENT_SHOW], [EVENT_REORDER, "container"]], msg); + document.getElementById("span").removeAttribute("role"); + await events; + + msg = "Assign a 'button' role to a div"; + events = waitForOrderedEvents( + [[EVENT_HIDE, "div1"], [EVENT_SHOW, "div1"], [EVENT_REORDER, "container"]], msg); + document.getElementById("div1").setAttribute("role", "button"); + await events; + + msg = "Change a password field to a text field"; + events = waitForOrderedEvents( + [[EVENT_HIDE, "password"], [EVENT_SHOW, "password"], [EVENT_REORDER, "container"]], msg); + document.getElementById("password").setAttribute("type", "text"); + await events; + + msg = "Change a text field to a password field"; + events = waitForOrderedEvents( + [[EVENT_HIDE, "text"], [EVENT_SHOW, "text"], [EVENT_REORDER, "container"]], msg); + document.getElementById("text").setAttribute("type", "password"); + await events; + + msg = "Add aria-label to a span"; + ok(!isAccessible("span2"), "span2 has no accessible"); + events = waitForOrderedEvents( + [[EVENT_SHOW, "span2"], [EVENT_REORDER, "container"]], msg); + document.getElementById("span2").setAttribute("aria-label", "label"); + await events; + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Rework accessible tree update code" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> + Mozilla Bug 570275 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container"> + <span id="span">span</span> + <div id="div1">div</div> + <a id="anchor">anchor</a> + <div id="div3" role="listbox">list</div> + <input type="password" id="password"/> + <input type="text" id="text"/> + <span id="span2"></span> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_select.html b/accessible/tests/mochitest/treeupdate/test_select.html new file mode 100644 index 0000000000..eabb64f80f --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_select.html @@ -0,0 +1,191 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML select options test</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + function addOptions(aID) { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function addOptions_invoke() { + for (let i = 0; i < 2; i++) { + var opt = document.createElement("option"); + opt.value = i; + opt.text = "Option: Value " + i; + + this.selectNode.add(opt, null); + } + }; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList), + ]; + + this.finalCheck = function addOptions_finalCheck() { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { COMBOBOX_OPTION: [ ] }, + { COMBOBOX_OPTION: [ ] }, + ] }, + ] }; + testAccessibleTree(this.select, tree); + }; + + this.getID = function addOptions_getID() { + return "test elements insertion into a select"; + }; + } + + function removeOptions(aID) { + this.selectNode = getNode(aID); + this.select = getAccessible(this.selectNode); + this.selectList = this.select.firstChild; + + this.invoke = function removeOptions_invoke() { + while (this.selectNode.length) + this.selectNode.remove(0); + }; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.selectList), + ]; + + this.finalCheck = function removeOptions_finalCheck() { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [] }, + ] }; + testAccessibleTree(this.select, tree); + }; + + this.getID = function removeptions_getID() { + return "test elements removal from a select"; + }; + } + + /** + * Setting role=option on option makes the accessible recreate. + */ + function setRoleOnOption() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, "s2_o"), + new invokerChecker(EVENT_SHOW, "s2_o"), + ]; + + this.invoke = function setRoleOnOption_setRole() { + getNode("s2_o").setAttribute("role", "option"); + }; + + this.finalCheck = function() { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { COMBOBOX_OPTION: [ ] }, + ] }, + ] }; + testAccessibleTree("s2", tree); + }; + + this.getID = function removeptions_getID() { + return "setting role=option on select option"; + }; + } + + /** + * Setting multiple on select makes the accessible recreate. + */ + function setMultipleOnSelect() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, "s3"), + new invokerChecker(EVENT_SHOW, "s3"), + ]; + + this.invoke = function setRoleOnOption_setRole() { + getNode("s3").multiple = true; + }; + + this.finalCheck = function() { + var tree = + { LISTBOX: [ + { OPTION: [ ] }, + ] }; + testAccessibleTree("s3", tree); + }; + + this.getID = function removeptions_getID() { + return "setting multiple on select element"; + }; + } + + + /** + * Removing size on select makes the accessible recreate. + */ + function removeSizeFromSelect() { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, "s4"), + new invokerChecker(EVENT_SHOW, "s4"), + ]; + + this.invoke = function setRoleOnOption_setRole() { + getNode("s4").removeAttribute("size"); + }; + + this.finalCheck = function() { + var tree = + { COMBOBOX: [ + { COMBOBOX_LIST: [ + { COMBOBOX_OPTION: [ ] }, + ] }, + ] }; + testAccessibleTree("s4", tree); + }; + + this.getID = function removeptions_getID() { + return "removing size from select element"; + }; + } + + function doTest() { + const gQueue = new eventQueue(); + + gQueue.push(new addOptions("select")); + gQueue.push(new removeOptions("select")); + gQueue.push(new setRoleOnOption()); + gQueue.push(new setMultipleOnSelect()); + gQueue.push(new removeSizeFromSelect()); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <select id="select"></select> + <select id="s2"><option id="s2_o"></option></select> + <select id="s3"><option></option></select> + <select id="s4" size="3"><option></option></select> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_shadow_slots.html b/accessible/tests/mochitest/treeupdate/test_shadow_slots.html new file mode 100644 index 0000000000..27baef0e4a --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_shadow_slots.html @@ -0,0 +1,554 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test shadow roots with slots</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../promisified-events.js"></script> + + <script type="application/javascript"> + async function _dynamicShadowTest(name, mutationFunc, expectedTree, reorder_targets) { + info(name); + + let container = getNode(name); + let host = container.querySelector('.host'); + + document.body.offsetTop; + let event = reorder_targets ? + waitForEvents(reorder_targets.map(target => [EVENT_REORDER, target, name])) : + waitForEvent(EVENT_REORDER, host, name); + + mutationFunc(container, host); + + await event; + + testAccessibleTree(container, expectedTree); + + return true; + } + + async function attachFlatShadow() { + await _dynamicShadowTest("attachFlatShadow", + (container, host) => { + host.attachShadow({ mode: "open" }) + .appendChild(container.querySelector('.shadowtree').content.cloneNode(true)); + }, { SECTION: [{ SECTION: [{ name: "red"} ] }] }); + } + + async function attachOneDeepShadow() { + await _dynamicShadowTest("attachOneDeepShadow", + (container, host) => { + host.attachShadow({ mode: "open" }) + .appendChild(container.querySelector('.shadowtree').content.cloneNode(true)); + }, { SECTION: [{ SECTION: [{ SECTION: [{ name: "red"} ] }] }] }); + } + + async function changeSlotFlat() { + await _dynamicShadowTest("changeSlotFlat", + (container, host) => { + container.querySelector('.red').removeAttribute('slot'); + container.querySelector('.green').setAttribute('slot', 'myslot'); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + async function changeSlotOneDeep() { + await _dynamicShadowTest("changeSlotOneDeep", + (container, host) => { + container.querySelector('.red').removeAttribute('slot'); + container.querySelector('.green').setAttribute('slot', 'myslot'); + }, { SECTION: [{ SECTION: [{ SECTION: [{ name: "green"} ] }] }] }, ["shadowdiv"]); + } + + // Nested roots and slots + async function changeSlotNested() { + await _dynamicShadowTest("changeSlotNested", + (container, host) => { + testAccessibleTree(getNode("changeSlotNested"), + { SECTION: [{ SECTION: [{ SECTION: [{ name: "red"} ] }] }] }); + container.querySelector('.red').removeAttribute('slot'); + container.querySelector('.green').setAttribute('slot', 'myslot'); + }, { SECTION: [{ SECTION: [{ SECTION: [{ name: "green"} ] }] }] }, ["shadowdiv"]); + } + + async function changeSlotSingleChild() { + await _dynamicShadowTest("changeSlotSingleChild", + (container, host) => { + container.querySelector('.red').setAttribute('slot', 'invalid'); + }, { SECTION: [{ SECTION: [] }] }); + } + + async function changeSlotNoShadow() { + await _dynamicShadowTest("changeSlotNoShadow", + (container, host) => { + // Make sure changing the slot attribute doesn't remove an element + // even when it remains in the flat tree. + container.querySelector('.red').setAttribute('slot', 'invalid'); + // We need a reorder to know when we're done here, so remove another + // child. + container.querySelector('.green').remove(); + }, { SECTION: [{ SECTION: [{ name: "red"} ] }] }); + } + + // Dynamic mutations to both shadow root and shadow host subtrees + // testing/web-platform/tests/css/css-scoping/shadow-assign-dynamic-001.html + async function assignSlotDynamic() { + await _dynamicShadowTest("assignSlotDynamic", + (container, host) => { + host.shadowRoot.appendChild(container.querySelector('.shadowtree').content.cloneNode(true)); + host.appendChild(container.querySelector('.lighttree').content.cloneNode(true)); + }, { SECTION: [{ SECTION: [{ name: "slot1"}, { name: "slot2" } ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-fallback-dynamic-001.html + async function shadowFallbackDynamic_1() { + await _dynamicShadowTest("shadowFallbackDynamic_1", + (container, host) => { + host.firstElementChild.remove(); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-fallback-dynamic-002.html + async function shadowFallbackDynamic_2() { + await _dynamicShadowTest("shadowFallbackDynamic_2", + (container, host) => { + host.firstElementChild.removeAttribute("slot"); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-fallback-dynamic-003.html + async function shadowFallbackDynamic_3() { + await _dynamicShadowTest("shadowFallbackDynamic_3", + (container, host) => { + host.appendChild(container.querySelector(".lighttree").content.cloneNode(true)); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-fallback-dynamic-004.html + async function shadowFallbackDynamic_4() { + await _dynamicShadowTest("shadowFallbackDynamic_4", + (container, host) => { + host.shadowRoot.insertBefore( + container.querySelector(".moreshadowtree"). + content.cloneNode(true), host.shadowRoot.firstChild); + }, { SECTION: [{ SECTION: [{ name: "slotparent2", children: [{ name: "green"} ] }, { name: "slotparent1" } ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-fallback-dynamic-004.html + // This tests a case when the the slotted element would remain in the same accessible container + async function shadowFallbackDynamic_4_1() { + await _dynamicShadowTest("shadowFallbackDynamic_4_1", + (container, host) => { + host.shadowRoot.insertBefore( + container.querySelector(".moreshadowtree"). + content.cloneNode(true), host.shadowRoot.firstChild); + }, { SECTION: [{ SECTION: [ { name: "green"}, { SEPARATOR: [] } ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-fallback-dynamic-005.html + async function shadowFallbackDynamic_5() { + await _dynamicShadowTest("shadowFallbackDynamic_5", + (container, host) => { + host.firstElementChild.setAttribute("slot", "myotherslot"); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-reassign-dynamic-002.html + async function shadowReassignDynamic_2() { + await _dynamicShadowTest("shadowReassignDynamic_2", + (container, host) => { + host.shadowRoot.querySelector("slot").setAttribute("name", "myslot"); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + // testing/web-platform/tests/css/css-scoping/shadow-reassign-dynamic-003.html + async function shadowReassignDynamic_3() { + await _dynamicShadowTest("shadowReassignDynamic_3", + (container, host) => { + testAccessibleTree(container, { SECTION: [{ SECTION: [{ name: "green"}, { name: "red", children: [ { PUSHBUTTON: [] }]} ] }] }); + host.shadowRoot.querySelector("slot[name]").removeAttribute("name"); + + }, { SECTION: [{ SECTION: [{ name: "green", children: [ { PUSHBUTTON: [] }]}, { name: "red"} ] }] }, + [evt => evt.accessible.name == "green", evt => evt.accessible.name == "red"]); + } + + // testing/web-platform/tests/css/css-scoping/shadow-reassign-dynamic-004.html + async function shadowReassignDynamic_4() { + await _dynamicShadowTest("shadowReassignDynamic_4", + (container, host) => { + host.shadowRoot.getElementById("slot").remove(); + }, { SECTION: [{ SECTION: [{ name: "green"} ] }] }); + } + + function shadowProcessInvalidation() { + testAccessibleTree("shadowProcessInvalidation", + { SECTION: [{ + SECTION: [{ + SECTION: [{ TEXT_LEAF: { name: "Hello "} }, + { TEXT: [{ TEXT_LEAF: { name: "World"} }] }, + { PUSHBUTTON: { name: "World"} }] + }] + }] + }); + } + + async function justAttachShadow() { + await _dynamicShadowTest("justAttachShadow", + (container, host) => { + host.attachShadow({ mode: "open" }); + }, { SECTION: [{ SECTION: [] }] }); + } + + async function doTest() { + await attachFlatShadow(); + + await attachOneDeepShadow(); + + await changeSlotFlat(); + + await changeSlotOneDeep(); + + await changeSlotNested(); + + await changeSlotSingleChild(); + + await changeSlotNoShadow(); + + await assignSlotDynamic(); + + await shadowFallbackDynamic_1(); + + await shadowFallbackDynamic_2(); + + await shadowFallbackDynamic_3(); + + await shadowFallbackDynamic_4(); + + await shadowFallbackDynamic_4_1(); + + await shadowFallbackDynamic_5(); + + await shadowReassignDynamic_2(); + + await shadowReassignDynamic_3(); + + await shadowReassignDynamic_4(); + + shadowProcessInvalidation(); + + await justAttachShadow(); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + <div id="attachFlatShadow"> + <template class="shadowtree"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <slot name="myslot">FAIL</slot> + </template> + <section class="host"> + <div style="background: green" aria-label="green"></div> + <div style="background: red" aria-label="red" slot="myslot"></div> + </section> + </div> + + <div id="attachOneDeepShadow"> + <template class="shadowtree"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <div id="shadowdiv"> + <slot name="myslot">FAIL</slot> + </div> + </template> + <section class="host"> + <div style="background: green" aria-label="green"></div> + <div style="background: red" aria-label="red" slot="myslot"></div> + </section> + </div> + + <div id="changeSlotFlat"> + <template class="shadowtree"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <slot name="myslot">FAIL</slot> + </template> + <section class="host"> + <div class="green" style="background: green" aria-label="green"></div> + <div class="red" style="background: red" aria-label="red" slot="myslot"></div> + </section> + <script> + document.querySelector("#changeSlotFlat > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#changeSlotFlat > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="changeSlotOneDeep"> + <template class="shadowtree"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <div id="shadowdiv"> + <slot name="myslot">FAIL</slot> + </div> + </template> + <section class="host"> + <div class="green" style="background: green" aria-label="green"></div> + <div class="red" style="background: red" aria-label="red" slot="myslot"></div> + </section> + <script> + document.querySelector("#changeSlotOneDeep > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#changeSlotOneDeep > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="changeSlotNested"> + <template class="shadowtree outer"> + <div id="shadowdiv"> + <slot name="myslot">FAIL</slot> + </div> + </template> + <template class="shadowtree inner"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <slot>FAIL</slot> + </template> + <section class="host"> + <div class="green" style="background: green" aria-label="green"></div> + <div class="red" style="background: red" aria-label="red" slot="myslot"></div> + </section> + <script> + (function foo() { + let outerShadow = + document.querySelector("#changeSlotNested > .host"). + attachShadow({ mode: "open" }); + outerShadow.appendChild( + document.querySelector("#changeSlotNested > .shadowtree.outer"). + content.cloneNode(true)); + let innerShadow = + outerShadow.querySelector("#shadowdiv"). + attachShadow({ mode: "open" }); + innerShadow.appendChild( + document.querySelector("#changeSlotNested > .shadowtree.inner"). + content.cloneNode(true)); + })(); + </script> + </div> + + <div id="changeSlotSingleChild"> + <template class="shadowtree"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <slot></slot> + </template> + <section class="host"> + <div class="red" style="background: red" aria-label="red"></div> + </section> + <script> + document.querySelector("#changeSlotSingleChild > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#changeSlotSingleChild > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="changeSlotNoShadow"> + <section class="host"> + <div class="red" style="background: red; width: 100px; height: 100px;" aria-label="red"></div> + <div class="green" style="background: green; width: 100px; height: 100px;" aria-label="green"></div> + </section> + </div> + + <div id="assignSlotDynamic"> + <template class="shadowtree"> + <style>::slotted(div) { width: 50px; height: 100px }</style> + <slot name="slot1">FAIL</slot> + <slot name="slot2">FAIL</slot> + </template> + <template class="lighttree"> + <div aria-label="slot1" slot="slot1"></div> + <div aria-label="slot2" slot="slot2"></div> + </template> + <section class="host"></section> + <script> + document.querySelector("#assignSlotDynamic > .host").attachShadow({ mode: "open" }); + </script> + </div> + + <div id="shadowFallbackDynamic_1"> + <template class="shadowtree"> + <slot name="myslot"> + <div aria-label="green" style="width: 100px; height: 100px; background: green"></div> + </slot> + </template> + <section class="host"><span slot="myslot">FAIL</span></section> + <script> + document.querySelector("#shadowFallbackDynamic_1 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowFallbackDynamic_1 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowFallbackDynamic_2"> + <template class="shadowtree"> + <slot name="myslot"> + <div aria-label="green" style="width: 100px; height: 100px; background: green"></div> + </slot> + </template> + <section class="host"><span slot="myslot">FAIL</span></section> + <script> + document.querySelector("#shadowFallbackDynamic_2 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowFallbackDynamic_2 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowFallbackDynamic_3"> + <template class="shadowtree"> + <slot name="myslot">FAIL</slot> + </template> + <template class="lighttree"> + <div aria-label="green" slot="myslot" style="width: 100px; height: 100px; background: green"></div> + </template> + <section class="host"></section> + <script> + document.querySelector("#shadowFallbackDynamic_3 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowFallbackDynamic_3 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowFallbackDynamic_4"> + <template class="shadowtree"> + <div aria-label="slotparent1"><slot name="myslot"></slot></div> + </template> + <template class="moreshadowtree"> + <div aria-label="slotparent2"><slot name="myslot">FAIL</slot></div> + </template> + <section class="host"> + <div slot="myslot" aria-label="green" style="width: 100px; height: 100px; background: green"></div> + </section> + <script> + document.querySelector("#shadowFallbackDynamic_4 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowFallbackDynamic_4 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowFallbackDynamic_4_1"> + <template class="shadowtree"> + <hr> + <slot name="myslot"></slot> + </template> + <template class="moreshadowtree"> + <slot name="myslot">FAIL</slot> + </template> + <section class="host"> + <div slot="myslot" aria-label="green" style="width: 100px; height: 100px; background: green"></div> + </section> + <script> + document.querySelector("#shadowFallbackDynamic_4_1 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowFallbackDynamic_4_1 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowFallbackDynamic_5"> + <template class="shadowtree"> + <slot name="myslot"></slot> + <slot name="myotherslot">FAIL</slot> + </template> + <section class="host"> + <div slot="myslot" aria-label="green" style="width: 100px; height: 100px; background: green"></div> + </section> + <script> + document.querySelector("#shadowFallbackDynamic_5 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowFallbackDynamic_5 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowReassignDynamic_2"> + <template class="shadowtree"> + <style>::slotted(div) { width: 100px; height: 100px }</style> + <slot>FAIL</slot> + </template> + <section class="host"> + <div slot="myslot" aria-label="green" style="width: 100px; height: 100px; background: green"></div> + </section> + <script> + document.querySelector("#shadowReassignDynamic_2 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowReassignDynamic_2 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowReassignDynamic_3"> + <template class="shadowtree"> + <div aria-label="green"><slot name="nomatch"></slot></div> + <div aria-label="red"><slot></slot></div> + </template> + <section class="host"> + <div role="button"></div> + </section> + <script> + document.querySelector("#shadowReassignDynamic_3 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowReassignDynamic_3 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowReassignDynamic_4"> + <template class="shadowtree"> + <style>::slotted(div),div { width: 100px; height: 100px }</style> + <slot id="slot"></slot> + <slot> + <div aria-label="red" style="background: red"></div> + </slot> + </template> + <section class="host"> + <div aria-label="green" style="background: green"></div> + </section> + <script> + document.querySelector("#shadowReassignDynamic_4 > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowReassignDynamic_4 > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="shadowProcessInvalidation"> + <template class="shadowtree"> + <div id="shadowdiv"> + <slot></slot> + </div> + </template> + <section class="host">Hello <span id="c">World</span><button aria-labelledby="c"></button></section> + <script> + document.querySelector("#shadowProcessInvalidation > .host") + .attachShadow({ mode: "open" }) + .appendChild(document.querySelector("#shadowProcessInvalidation > .shadowtree").content.cloneNode(true)); + </script> + </div> + + <div id="justAttachShadow"> + <section class="host"> + <button></button> + </section> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_shutdown.xhtml b/accessible/tests/mochitest/treeupdate/test_shutdown.xhtml new file mode 100644 index 0000000000..ad8aebf812 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_shutdown.xhtml @@ -0,0 +1,131 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL tree hierarchy tests"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + + <script type="application/javascript" + src="../treeview.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../states.js" /> + <script type="application/javascript" + src="../events.js" /> + + <script type="application/javascript"> + <![CDATA[ + function setXULTreeView(aTreeID, aTreeView) + { + this.treeNode = getNode(aTreeID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.treeNode) + ]; + + this.invoke = function loadXULTree_invoke() + { + this.treeNode.view = aTreeView; + }; + + this.getID = function loadXULTree_getID() + { + return "Load XUL tree " + prettyName(aTreeID); + }; + } + + function removeTree(aID) + { + this.tree = getAccessible(aID); + this.lastItem = null; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, document) + ]; + + this.invoke = function invoke() + { + this.lastItem = getAccessible(aID).lastChild; + this.lastCell = this.lastItem.lastChild; + getNode(aID).remove(); + }; + + this.check = function check(aEvent) + { + testIsDefunct(this.tree, aID); + testIsDefunct(this.lastItem, "last item of " + aID); + if (this.lastCell) { + testIsDefunct(this.lastCell, "last item cell of " + aID); + } + }; + + this.getID = function getID() + { + return "Remove tree from DOM"; + }; + } + + //////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "debug"; + var gQueue = null; + + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new setXULTreeView("tree", new nsTreeTreeView())); + gQueue.push(new removeTree("tree")); + + gQueue.push(new setXULTreeView("treetable", new nsTreeTreeView())); + gQueue.push(new removeTree("treetable")); + + gQueue.invoke(); // Will call SimpleTest.finish() + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727" + title="Reorganize implementation of XUL tree accessibility"> + Bug 503727 + </a><br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1"> + <tree id="tree" flex="1"> + <treecols> + <treecol id="col" flex="1" primary="true" label="column"/> + </treecols> + <treechildren/> + </tree> + + <tree id="treetable" flex="1"> + <treecols> + <treecol id="col1" flex="1" primary="true" label="column"/> + <treecol id="col2" flex="1" label="column 2"/> + </treecols> + <treechildren/> + </tree> + </vbox> + </hbox> + +</window> diff --git a/accessible/tests/mochitest/treeupdate/test_table.html b/accessible/tests/mochitest/treeupdate/test_table.html new file mode 100644 index 0000000000..50fac91757 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_table.html @@ -0,0 +1,74 @@ +<!DOCTYPE html> +<html> +<head> + <title>Table update tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + function appendCaption(aTableID) { + this.invoke = function appendCaption_invoke() { + // append a caption, it should appear as a first element in the + // accessible tree. + var caption = document.createElement("caption"); + caption.textContent = "table caption"; + getNode(aTableID).appendChild(caption); + }; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aTableID), + ]; + + this.finalCheck = function appendCaption_finalCheck() { + var tree = + { TABLE: [ + { CAPTION: [ + { TEXT_LEAF: [] }, + ] }, + { ROW: [ + { CELL: [ {TEXT_LEAF: [] }]}, + { CELL: [ {TEXT_LEAF: [] }]}, + ] }, + ] }; + testAccessibleTree(aTableID, tree); + }; + + this.getID = function appendCaption_getID() { + return "append caption"; + }; + } + + function doTest() { + const gQueue = new eventQueue(); + gQueue.push(new appendCaption("table")); + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <table id="table"> + <tr> + <td>cell1</td> + <td>cell2</td> + </tr> + </table> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_textleaf.html b/accessible/tests/mochitest/treeupdate/test_textleaf.html new file mode 100644 index 0000000000..f0181dd754 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html @@ -0,0 +1,167 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Test accessible recreation</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + function textLeafUpdate(aID, aIsTextLeafLinkable) { + this.node = getNode(aID); + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.node.parentNode), + ]; + + this.finalCheck = function textLeafUpdate_finalCheck() { + var textLeaf = getAccessible(this.node).firstChild; + is(textLeaf.actionCount, (aIsTextLeafLinkable ? 1 : 0), + "Wrong action numbers!"); + }; + } + + function setOnClickAttr(aID) { + var node = getNode(aID); + node.setAttribute("onclick", "alert(3);"); + var textLeaf = getAccessible(node).firstChild; + is(textLeaf.actionCount, 1, "setOnClickAttr: wrong action numbers!"); + } + + function removeOnClickAttr(aID) { + var node = getNode(aID); + node.removeAttribute("onclick"); + var textLeaf = getAccessible(node).firstChild; + is(textLeaf.actionCount, 0, + "removeOnClickAttr: wrong action numbers!"); + } + + function setOnClickNRoleAttrs(aID) { + this.__proto__ = new textLeafUpdate(aID, true); + + this.invoke = function setOnClickAttr_invoke() { + this.node.setAttribute("role", "link"); + this.node.setAttribute("onclick", "alert(3);"); + }; + + this.getID = function setOnClickAttr_getID() { + return "make " + prettyName(aID) + " linkable"; + }; + } + + function removeTextData(aID, aRole) { + this.containerNode = getNode(aID); + this.textNode = this.containerNode.firstChild; + + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, this.containerNode), + ]; + + this.invoke = function removeTextData_invoke() { + var tree = { + role: aRole, + children: [ + { + role: ROLE_TEXT_LEAF, + name: "text", + }, + ], + }; + testAccessibleTree(this.containerNode, tree); + + this.textNode.data = ""; + }; + + this.finalCheck = function removeTextData_finalCheck() { + var tree = { + role: aRole, + children: [], + }; + testAccessibleTree(this.containerNode, tree); + }; + + this.getID = function removeTextData_finalCheck() { + return "remove text data of text node inside '" + aID + "'."; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() { + // adds onclick on element, text leaf should inherit its action + setOnClickAttr("div"); + // remove onclick attribute, text leaf shouldn't have any action + removeOnClickAttr("div"); + + // Call rest of event tests. + gQueue = new eventQueue(); + + // set onclick attribute making span accessible, it's inserted into tree + // and adopts text leaf accessible, text leaf should have an action + gQueue.push(new setOnClickNRoleAttrs("span")); + + // text data removal of text node should remove its text accessible + gQueue.push(new removeTextData("p", ROLE_PARAGRAPH)); + gQueue.push(new removeTextData("pre", ROLE_TEXT_CONTAINER)); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Clean up the code of accessible initialization and binding to the tree" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=545465"> + Mozilla Bug 545465 + </a> + <a target="_blank" + title="Make sure accessible tree is correct when rendered text is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652"> + Mozilla Bug 625652 + </a> + <a target="_blank" + title="Remove text accesible getting no text inside a preformatted area" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=706335"> + Mozilla Bug 706335 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <div id="container"> + <div id="div">div</div> + <span id="span">span</span> + </div> + + <p id="p">text</p> + <pre id="pre">text</pre> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_tooltip.xhtml b/accessible/tests/mochitest/treeupdate/test_tooltip.xhtml new file mode 100644 index 0000000000..dba83b2267 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_tooltip.xhtml @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> + +<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="Accessible XUL tooltip test"> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" /> + + <script type="application/javascript" + src="../common.js" /> + <script type="application/javascript" + src="../role.js" /> + <script type="application/javascript" + src="../promisified-events.js" /> + + <script type="application/javascript"> + <![CDATA[ + + async function doTest() { + let tooltip = document.getElementById("tooltip"); + + testAccessibleTree("tooltip-container", { GROUPING: [ + ] }); + + let shown = waitForEvent(EVENT_SHOW, tooltip); + tooltip.openPopup(); + await shown; + + testAccessibleTree("tooltip-container", + { GROUPING: [ + { TOOLTIP: [] }, + { STATICTEXT: [] }, + ] }); + + let hidden = waitForEvent(EVENT_HIDE, tooltip); + tooltip.hidePopup(); + await hidden; + + testAccessibleTree("tooltip-container", { GROUPING: [] }); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + ]]> + </script> + + <hbox flex="1" style="overflow: auto;"> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=1652211" + title="Added anonymous tooltip to mochitest docs messes with text"> + Bug 1652211 + </a> + <br/> + <p id="display"></p> + <div id="content" style="display: none"> + </div> + <pre id="test"> + </pre> + </body> + + <vbox flex="1" role="group" id="tooltip-container"> + <tooltip id="tooltip"> + <description class="tooltip-label" value="hello world"/> + </tooltip> + </vbox> + </hbox> + +</window> + diff --git a/accessible/tests/mochitest/treeupdate/test_visibility.html b/accessible/tests/mochitest/treeupdate/test_visibility.html new file mode 100644 index 0000000000..4107832b3e --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_visibility.html @@ -0,0 +1,411 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Style visibility tree update test</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + /** + * Hide parent while child stays visible. + */ + function test1(aContainerID, aParentID, aChildID) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aParentID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function invoke() { + var tree = + { SECTION: [ + { SECTION: [ + { SECTION: [ + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aParentID).style.visibility = "hidden"; + }; + + this.finalCheck = function finalCheck() { + var tree = + { SECTION: [ + { SECTION: [ + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function getID() { + return "hide parent while child stays visible"; + }; + } + + /** + * Hide grand parent while its children stay visible. + */ + function test2(aContainerID, aGrandParentID, aChildID, aChild2ID) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aGrandParentID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_SHOW, getNode(aChild2ID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function invoke() { + var tree = + { SECTION: [ // container + { SECTION: [ // grand parent + { SECTION: [ + { SECTION: [ // child + { TEXT_LEAF: [] }, + ] }, + { SECTION: [ // child2 + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aGrandParentID).style.visibility = "hidden"; + }; + + this.finalCheck = function finalCheck() { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] }, + ] }, + { SECTION: [ // child2 + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function getID() { + return "hide grand parent while its children stay visible"; + }; + } + + /** + * Change container style, hide parents while their children stay visible. + */ + function test3(aContainerID, aParentID, aParent2ID, aChildID, aChild2ID) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aParentID)), + new invokerChecker(EVENT_HIDE, getNode(aParent2ID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_SHOW, getNode(aChild2ID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function invoke() { + var tree = + { SECTION: [ // container + { SECTION: [ // parent + { SECTION: [ // child + { TEXT_LEAF: [] }, + ] }, + ] }, + { SECTION: [ // parent2 + { SECTION: [ // child2 + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aContainerID).style.color = "red"; + getNode(aParentID).style.visibility = "hidden"; + getNode(aParent2ID).style.visibility = "hidden"; + }; + + this.finalCheck = function finalCheck() { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] }, + ] }, + { SECTION: [ // child2 + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function getID() { + return "change container style, hide parents while their children stay visible"; + }; + } + + /** + * Change container style and make visible child inside the table. + */ + function test4(aContainerID, aChildID) { + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_REORDER, getNode(aChildID).parentNode), + ]; + + this.invoke = function invoke() { + var tree = + { SECTION: [ + { TABLE: [ + { ROW: [ + { CELL: [ ] }, + ] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aContainerID).style.color = "red"; + getNode(aChildID).style.visibility = "visible"; + }; + + this.finalCheck = function finalCheck() { + var tree = + { SECTION: [ + { TABLE: [ + { ROW: [ + { CELL: [ + { SECTION: [ + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function getID() { + return "change container style, make visible child insdie the table"; + }; + } + + /** + * Hide subcontainer while child inside the table stays visible. + */ + function test5(aContainerID, aSubContainerID, aChildID) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function invoke() { + var tree = + { SECTION: [ // container + { SECTION: [ // subcontainer + { TABLE: [ + { ROW: [ + { CELL: [ + { SECTION: [ // child + { TEXT_LEAF: [] }, + ] }, + ] }, + ] }, + ] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + + getNode(aSubContainerID).style.visibility = "hidden"; + }; + + this.finalCheck = function finalCheck() { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] }, + ] }, + ] }; + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function getID() { + return "hide subcontainer while child inside the table stays visible"; + }; + } + + /** + * Hide subcontainer while its child and child inside the nested table stays visible. + */ + function test6(aContainerID, aSubContainerID, aChildID, aChild2ID) { + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getNode(aSubContainerID)), + new invokerChecker(EVENT_SHOW, getNode(aChildID)), + new invokerChecker(EVENT_SHOW, getNode(aChild2ID)), + new invokerChecker(EVENT_REORDER, getNode(aContainerID)), + ]; + + this.invoke = function invoke() { + var tree = + { SECTION: [ // container + { SECTION: [ // subcontainer + { TABLE: [ + { ROW: [ + { CELL: [ + { TABLE: [ // nested table + { ROW: [ + { CELL: [ + { SECTION: [ // child + { TEXT_LEAF: [] } ]} ]} ]} ]} ]} ]} ]}, + { SECTION: [ // child2 + { TEXT_LEAF: [] } ]} ]} ]}; + + testAccessibleTree(aContainerID, tree); + + // invoke + getNode(aSubContainerID).style.visibility = "hidden"; + }; + + this.finalCheck = function finalCheck() { + var tree = + { SECTION: [ // container + { SECTION: [ // child + { TEXT_LEAF: [] } ]}, + { SECTION: [ // child2 + { TEXT_LEAF: [] } ]} ]}; + + testAccessibleTree(aContainerID, tree); + }; + + this.getID = function getID() { + return "hide subcontainer while its child and child inside the nested table stays visible"; + }; + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() { + gQueue = new eventQueue(); + + gQueue.push(new test1("t1_container", "t1_parent", "t1_child")); + gQueue.push(new test2("t2_container", "t2_grandparent", "t2_child", "t2_child2")); + gQueue.push(new test3("t3_container", "t3_parent", "t3_parent2", "t3_child", "t3_child2")); + gQueue.push(new test4("t4_container", "t4_child")); + gQueue.push(new test5("t5_container", "t5_subcontainer", "t5_child")); + gQueue.push(new test6("t6_container", "t6_subcontainer", "t6_child", "t6_child2")); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Develop a way to handle visibility style" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=606125"> + Mozilla Bug 606125 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- hide parent while child stays visible --> + <div id="t1_container"> + <div id="t1_parent"> + <div id="t1_child" style="visibility: visible">text</div> + </div> + </div> + + <!-- hide grandparent while its children stay visible --> + <div id="t2_container"> + <div id="t2_grandparent"> + <div id="t2_parent"> + <div id="t2_child" style="visibility: visible">text</div> + <div id="t2_child2" style="visibility: visible">text</div> + </div> + </div> + </div> + + <!-- change container style, hide parents while their children stay visible --> + <div id="t3_container"> + <div id="t3_parent"> + <div id="t3_child" style="visibility: visible">text</div> + </div> + <div id="t3_parent2"> + <div id="t3_child2" style="visibility: visible">text</div> + </div> + </div> + + <!-- change container style, show child inside the table --> + <div id="t4_container"> + <table> + <tr> + <td> + <div id="t4_child" style="visibility: hidden;">text</div> + </td> + </tr> + </table> + </div> + + <!-- hide subcontainer while child inside the table stays visible --> + <div id="t5_container"> + <div id="t5_subcontainer"> + <table> + <tr> + <td> + <div id="t5_child" style="visibility: visible;">text</div> + </td> + </tr> + </table> + </div> + </div> + + <!-- hide subcontainer while its child and child inside the nested table stays visible --> + <div id="t6_container"> + <div id="t6_subcontainer"> + <table> + <tr> + <td> + <table> + <tr> + <td> + <div id="t6_child" style="visibility: visible;">text</div> + </td> + </tr> + </table> + </td> + </tr> + </table> + <div id="t6_child2" style="visibility: visible">text</div> + </div> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeupdate/test_whitespace.html b/accessible/tests/mochitest/treeupdate/test_whitespace.html new file mode 100644 index 0000000000..ebb199cfbe --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_whitespace.html @@ -0,0 +1,200 @@ +<!DOCTYPE html> +<html> + +<head> + <title>Whitespace text accessible creation/destruction</title> + + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + + // ////////////////////////////////////////////////////////////////////////// + // Invokers + + /** + * Middle image accessible removal results in text accessible removal. + * + * Before: + * DOM: whitespace img1 whitespace img2 whitespace img3 whitespace, + * a11y: img1 whitespace img2 whitespace img3 + * After: + * DOM: whitespace img1 whitespace whitespace img3 whitespace, + * a11y: img1 whitespace img3 + */ + function removeImg() { + this.containerNode = getNode("container1"); + this.imgNode = getNode("img1"); + this.img = getAccessible(this.imgNode); + this.text = this.img.nextSibling; + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.img), + new invokerChecker(EVENT_HIDE, this.text), + new invokerChecker(EVENT_REORDER, this.containerNode), + ]; + + this.finalCheck = function textLeafUpdate_finalCheck() { + var tree = + { SECTION: [ + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + ] }; + + testAccessibleTree(this.containerNode, tree); + }; + + this.invoke = function setOnClickAttr_invoke() { + var tree = + { SECTION: [ + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + { TEXT_LEAF: [] }, + { GRAPHIC: [] }, + ] }; + + testAccessibleTree(this.containerNode, tree); + + this.containerNode.removeChild(this.imgNode); + }; + + this.getID = function setOnClickAttr_getID() { + return "remove middle img"; + }; + } + + /** + * Append image making the whitespace visible and thus accessible. + * Note: images and whitespaces are on different leves of accessible trees, + * so that image container accessible update doesn't update the tree + * of whitespace container. + * + * Before: + * DOM: whitespace emptylink whitespace linkwithimg whitespace + * a11y: emptylink linkwithimg + * After: + * DOM: whitespace linkwithimg whitespace linkwithimg whitespace + * a11y: linkwithimg whitespace linkwithimg + */ + function insertImg() { + this.containerNode = getNode("container2"); + this.topNode = this.containerNode.parentNode; + this.textNode = this.containerNode.nextSibling; + this.imgNode = document.createElement("img"); + this.imgNode.setAttribute("src", "../moz.png"); + + this.eventSeq = [ + new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.textNode), + new asyncInvokerChecker(EVENT_SHOW, getAccessible, this.imgNode), + new orderChecker(), + new invokerChecker(EVENT_REORDER, this.topNode), + ]; + + this.invoke = function insertImg_invoke() { + var tree = + { SECTION: [ + { LINK: [] }, + { LINK: [ + { GRAPHIC: [] }, + ] }, + ] }; + + testAccessibleTree(this.topNode, tree); + + this.containerNode.appendChild(this.imgNode); + }; + + this.finalCheck = function insertImg_finalCheck() { + var tree = + { SECTION: [ + { LINK: [ + { GRAPHIC: [ ] }, + ] }, + { TEXT_LEAF: [ ] }, + { LINK: [ + { GRAPHIC: [ ] }, + ] }, + ] }; + + testAccessibleTree(this.topNode, tree); + }; + + this.getID = function appendImg_getID() { + return "insert img into internal container"; + }; + } + + function dontCreateMapWhiteSpace() { + const tree = { SECTION: [ { role: ROLE_TEXT_LEAF, name: "x" } ] }; + testAccessibleTree("container3", tree); + + getNode("c3_inner").style.textAlign = "center"; + document.body.offsetTop; // Flush layout. + window.windowUtils.advanceTimeAndRefresh(100); + + testAccessibleTree("container3", tree); + window.windowUtils.restoreNormalRefresh(); + } + + // ////////////////////////////////////////////////////////////////////////// + // Test + + // gA11yEventDumpID = "eventdump"; // debug stuff + // gA11yEventDumpToConsole = true; + + var gQueue = null; + + function doTest() { + dontCreateMapWhiteSpace(); + + gQueue = new eventQueue(); + + gQueue.push(new removeImg()); + gQueue.push(new insertImg()); + + gQueue.invoke(); // SimpleTest.finish() will be called in the end + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Make sure accessible tree is correct when rendered text is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=625652"> + Mozilla Bug 625652 + </a> + + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <!-- Whitespace between the div and img tags will be inconsistent depending + on the image cache state and what optimizations layout was able to + apply. --> + <div id="container1"><img src="../moz.png"> <img id="img1" src="../moz.png"> <img src="../moz.png"></div> + <div><a id="container2"></a> <a><img src="../moz.png"></a></div> + + <div id="container3"> + <div id="c3_inner" role="presentation"> + x<map> </map> + </div> + </div> + + <div id="eventdump"></div> +</body> +</html> diff --git a/accessible/tests/mochitest/treeview.css b/accessible/tests/mochitest/treeview.css new file mode 100644 index 0000000000..462f560d6f --- /dev/null +++ b/accessible/tests/mochitest/treeview.css @@ -0,0 +1,15 @@ +treechildren::-moz-tree-checkbox(checked) { + list-style-image: url("chrome://global/skin/icons/check.svg"); +} + +treechildren::-moz-tree-image(cyclerState1) { + list-style-type: disc; +} + +treechildren::-moz-tree-image(cyclerState2) { + list-style-type: circle; +} + +treechildren::-moz-tree-image(cyclerState3) { + list-style-type: square; +} diff --git a/accessible/tests/mochitest/treeview.js b/accessible/tests/mochitest/treeview.js new file mode 100644 index 0000000000..5dc3b595d8 --- /dev/null +++ b/accessible/tests/mochitest/treeview.js @@ -0,0 +1,273 @@ +/* import-globals-from common.js */ +/* import-globals-from events.js */ + +/** + * Helper method to start a single XUL tree test. + */ +function loadXULTreeAndDoTest(aDoTestFunc, aTreeID, aTreeView) { + var doTestFunc = aDoTestFunc ? aDoTestFunc : gXULTreeLoadContext.doTestFunc; + var treeID = aTreeID ? aTreeID : gXULTreeLoadContext.treeID; + var treeView = aTreeView ? aTreeView : gXULTreeLoadContext.treeView; + + let treeNode = getNode(treeID); + + gXULTreeLoadContext.queue = new eventQueue(); + gXULTreeLoadContext.queue.push({ + treeNode, + + eventSeq: [new invokerChecker(EVENT_REORDER, treeNode)], + + invoke() { + this.treeNode.view = treeView; + }, + + getID() { + return "Load XUL tree " + prettyName(treeID); + }, + }); + gXULTreeLoadContext.queue.onFinish = function () { + SimpleTest.executeSoon(doTestFunc); + return DO_NOT_FINISH_TEST; + }; + gXULTreeLoadContext.queue.invoke(); +} + +/** + * Analogy of addA11yLoadEvent, nice helper to load XUL tree and start the test. + */ +function addA11yXULTreeLoadEvent(aDoTestFunc, aTreeID, aTreeView) { + gXULTreeLoadContext.doTestFunc = aDoTestFunc; + gXULTreeLoadContext.treeID = aTreeID; + gXULTreeLoadContext.treeView = aTreeView; + + addA11yLoadEvent(loadXULTreeAndDoTest); +} + +function nsTableTreeView(aRowCount) { + this.__proto__ = new nsTreeView(); + + for (var idx = 0; idx < aRowCount; idx++) { + this.mData.push(new treeItem("row" + String(idx) + "_")); + } +} + +function nsTreeTreeView() { + this.__proto__ = new nsTreeView(); + + this.mData = [ + new treeItem("row1"), + new treeItem("row2_", true, [ + new treeItem("row2.1_"), + new treeItem("row2.2_"), + ]), + new treeItem("row3_", false, [ + new treeItem("row3.1_"), + new treeItem("row3.2_"), + ]), + new treeItem("row4"), + ]; +} + +function nsTreeView() { + this.mTree = null; + this.mData = []; +} + +nsTreeView.prototype = { + // //////////////////////////////////////////////////////////////////////////// + // nsITreeView implementation + + get rowCount() { + return this.getRowCountIntl(this.mData); + }, + setTree: function setTree(aTree) { + this.mTree = aTree; + }, + getCellText: function getCellText(aRow, aCol) { + var data = this.getDataForIndex(aRow); + if (aCol.id in data.colsText) { + return data.colsText[aCol.id]; + } + + return data.text + aCol.id; + }, + getCellValue: function getCellValue(aRow, aCol) { + var data = this.getDataForIndex(aRow); + return data.value; + }, + getRowProperties: function getRowProperties(aIndex) { + return ""; + }, + getCellProperties: function getCellProperties(aIndex, aCol) { + if (!aCol.cycler) { + return ""; + } + + var data = this.getDataForIndex(aIndex); + return this.mCyclerStates[data.cyclerState]; + }, + getColumnProperties: function getColumnProperties(aCol) { + return ""; + }, + getParentIndex: function getParentIndex(aRowIndex) { + var info = this.getInfoByIndex(aRowIndex); + return info.parentIndex; + }, + hasNextSibling: function hasNextSibling(aRowIndex, aAfterIndex) {}, + getLevel: function getLevel(aIndex) { + var info = this.getInfoByIndex(aIndex); + return info.level; + }, + getImageSrc: function getImageSrc(aRow, aCol) {}, + isContainer: function isContainer(aIndex) { + var data = this.getDataForIndex(aIndex); + return data.open != undefined; + }, + isContainerOpen: function isContainerOpen(aIndex) { + var data = this.getDataForIndex(aIndex); + return data.open; + }, + isContainerEmpty: function isContainerEmpty(aIndex) { + var data = this.getDataForIndex(aIndex); + return data.open == undefined; + }, + isSeparator: function isSeparator(aIndex) {}, + isSorted: function isSorted() {}, + toggleOpenState: function toggleOpenState(aIndex) { + var data = this.getDataForIndex(aIndex); + + data.open = !data.open; + var rowCount = this.getRowCountIntl(data.children); + + if (data.open) { + this.mTree.rowCountChanged(aIndex + 1, rowCount); + } else { + this.mTree.rowCountChanged(aIndex + 1, -rowCount); + } + }, + selectionChanged: function selectionChanged() {}, + cycleHeader: function cycleHeader(aCol) {}, + cycleCell: function cycleCell(aRow, aCol) { + var data = this.getDataForIndex(aRow); + data.cyclerState = (data.cyclerState + 1) % 3; + + this.mTree.invalidateCell(aRow, aCol); + }, + isEditable: function isEditable(aRow, aCol) { + return true; + }, + setCellText: function setCellText(aRow, aCol, aValue) { + var data = this.getDataForIndex(aRow); + data.colsText[aCol.id] = aValue; + }, + setCellValue: function setCellValue(aRow, aCol, aValue) { + var data = this.getDataForIndex(aRow); + data.value = aValue; + + this.mTree.invalidateCell(aRow, aCol); + }, + + // //////////////////////////////////////////////////////////////////////////// + // public implementation + + appendItem: function appendItem(aText) { + this.mData.push(new treeItem(aText)); + }, + + // //////////////////////////////////////////////////////////////////////////// + // private implementation + + getDataForIndex: function getDataForIndex(aRowIndex) { + return this.getInfoByIndex(aRowIndex).data; + }, + + getInfoByIndex: function getInfoByIndex(aRowIndex) { + var info = { + data: null, + parentIndex: -1, + level: 0, + index: -1, + }; + + this.getInfoByIndexIntl(aRowIndex, info, this.mData, 0); + return info; + }, + + getRowCountIntl: function getRowCountIntl(aChildren) { + var rowCount = 0; + for (var childIdx = 0; childIdx < aChildren.length; childIdx++) { + rowCount++; + + var data = aChildren[childIdx]; + if (data.open) { + rowCount += this.getRowCountIntl(data.children); + } + } + + return rowCount; + }, + + getInfoByIndexIntl: function getInfoByIndexIntl( + aRowIdx, + aInfo, + aChildren, + aLevel + ) { + var rowIdx = aRowIdx; + for (var childIdx = 0; childIdx < aChildren.length; childIdx++) { + var data = aChildren[childIdx]; + + aInfo.index++; + + if (rowIdx == 0) { + aInfo.data = data; + aInfo.level = aLevel; + return -1; + } + + if (data.open) { + var parentIdx = aInfo.index; + rowIdx = this.getInfoByIndexIntl( + rowIdx - 1, + aInfo, + data.children, + aLevel + 1 + ); + + if (rowIdx == -1) { + if (aInfo.parentIndex == -1) { + aInfo.parentIndex = parentIdx; + } + return 0; + } + } else { + rowIdx--; + } + } + + return rowIdx; + }, + + mCyclerStates: ["cyclerState1", "cyclerState2", "cyclerState3"], +}; + +function treeItem(aText, aOpen, aChildren) { + this.text = aText; + this.colsText = {}; + this.open = aOpen; + this.value = "true"; + this.cyclerState = 0; + if (aChildren) { + this.children = aChildren; + } +} + +/** + * Used in conjunction with loadXULTreeAndDoTest and addA11yXULTreeLoadEvent. + */ +var gXULTreeLoadContext = { + doTestFunc: null, + treeID: null, + treeView: null, + queue: null, +}; |