summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/tree
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /accessible/tests/mochitest/tree
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--accessible/tests/mochitest/tree/a11y.ini58
-rw-r--r--accessible/tests/mochitest/tree/dockids.html32
-rw-r--r--accessible/tests/mochitest/tree/test_applicationacc.xhtml73
-rw-r--r--accessible/tests/mochitest/tree/test_aria_display_contents.html173
-rw-r--r--accessible/tests/mochitest/tree/test_aria_globals.html127
-rw-r--r--accessible/tests/mochitest/tree/test_aria_grid.html318
-rw-r--r--accessible/tests/mochitest/tree/test_aria_imgmap.html104
-rw-r--r--accessible/tests/mochitest/tree/test_aria_list.html90
-rw-r--r--accessible/tests/mochitest/tree/test_aria_menu.html91
-rw-r--r--accessible/tests/mochitest/tree/test_aria_owns.html197
-rw-r--r--accessible/tests/mochitest/tree/test_aria_presentation.html176
-rw-r--r--accessible/tests/mochitest/tree/test_aria_table.html101
-rw-r--r--accessible/tests/mochitest/tree/test_brokencontext.html214
-rw-r--r--accessible/tests/mochitest/tree/test_button.xhtml83
-rw-r--r--accessible/tests/mochitest/tree/test_canvas.html53
-rw-r--r--accessible/tests/mochitest/tree/test_combobox.xhtml116
-rw-r--r--accessible/tests/mochitest/tree/test_cssflexbox.html78
-rw-r--r--accessible/tests/mochitest/tree/test_cssoverflow.html135
-rw-r--r--accessible/tests/mochitest/tree/test_display_contents.html92
-rw-r--r--accessible/tests/mochitest/tree/test_divs.html351
-rw-r--r--accessible/tests/mochitest/tree/test_dochierarchy.html84
-rw-r--r--accessible/tests/mochitest/tree/test_dockids.html62
-rw-r--r--accessible/tests/mochitest/tree/test_filectrl.html56
-rw-r--r--accessible/tests/mochitest/tree/test_formctrl.html125
-rw-r--r--accessible/tests/mochitest/tree/test_formctrl.xhtml129
-rw-r--r--accessible/tests/mochitest/tree/test_gencontent.html69
-rw-r--r--accessible/tests/mochitest/tree/test_groupbox.xhtml63
-rw-r--r--accessible/tests/mochitest/tree/test_html_in_mathml.html61
-rw-r--r--accessible/tests/mochitest/tree/test_iframe.html50
-rw-r--r--accessible/tests/mochitest/tree/test_image.xhtml58
-rw-r--r--accessible/tests/mochitest/tree/test_img.html84
-rw-r--r--accessible/tests/mochitest/tree/test_invalid_img.xhtml48
-rw-r--r--accessible/tests/mochitest/tree/test_invalidationlist.html56
-rw-r--r--accessible/tests/mochitest/tree/test_list.html346
-rw-r--r--accessible/tests/mochitest/tree/test_map.html81
-rw-r--r--accessible/tests/mochitest/tree/test_media.html127
-rw-r--r--accessible/tests/mochitest/tree/test_select.html121
-rw-r--r--accessible/tests/mochitest/tree/test_svg.html127
-rw-r--r--accessible/tests/mochitest/tree/test_tabbox.xhtml108
-rw-r--r--accessible/tests/mochitest/tree/test_tabbrowser.xhtml261
-rw-r--r--accessible/tests/mochitest/tree/test_table.html507
-rw-r--r--accessible/tests/mochitest/tree/test_table_2.html242
-rw-r--r--accessible/tests/mochitest/tree/test_table_3.html244
-rw-r--r--accessible/tests/mochitest/tree/test_tree.xhtml182
-rw-r--r--accessible/tests/mochitest/tree/test_txtcntr.html234
-rw-r--r--accessible/tests/mochitest/tree/test_txtctrl.html171
-rw-r--r--accessible/tests/mochitest/tree/test_txtctrl.xhtml86
-rw-r--r--accessible/tests/mochitest/tree/wnd.xhtml8
-rw-r--r--accessible/tests/mochitest/treeupdate/a11y.ini46
-rw-r--r--accessible/tests/mochitest/treeupdate/test_ariadialog.html113
-rw-r--r--accessible/tests/mochitest/treeupdate/test_ariahidden.html118
-rw-r--r--accessible/tests/mochitest/treeupdate/test_ariaowns.html851
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1040735.html40
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1175913.html95
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1189277.html82
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1276857.html131
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html33
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug852150.xhtml57
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug883708.xhtml31
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug884251.xhtml19
-rw-r--r--accessible/tests/mochitest/treeupdate/test_bug895082.html49
-rw-r--r--accessible/tests/mochitest/treeupdate/test_canvas.html87
-rw-r--r--accessible/tests/mochitest/treeupdate/test_contextmenu.xhtml315
-rw-r--r--accessible/tests/mochitest/treeupdate/test_cssoverflow.html149
-rw-r--r--accessible/tests/mochitest/treeupdate/test_deck.xhtml154
-rw-r--r--accessible/tests/mochitest/treeupdate/test_delayed_removal.html500
-rw-r--r--accessible/tests/mochitest/treeupdate/test_doc.html415
-rw-r--r--accessible/tests/mochitest/treeupdate/test_gencontent.html187
-rw-r--r--accessible/tests/mochitest/treeupdate/test_general.html174
-rw-r--r--accessible/tests/mochitest/treeupdate/test_hidden.html125
-rw-r--r--accessible/tests/mochitest/treeupdate/test_imagemap.html402
-rw-r--r--accessible/tests/mochitest/treeupdate/test_inert.html138
-rw-r--r--accessible/tests/mochitest/treeupdate/test_inner_reorder.html148
-rw-r--r--accessible/tests/mochitest/treeupdate/test_list.html139
-rw-r--r--accessible/tests/mochitest/treeupdate/test_list_editabledoc.html100
-rw-r--r--accessible/tests/mochitest/treeupdate/test_list_style.html181
-rw-r--r--accessible/tests/mochitest/treeupdate/test_listbox.xhtml181
-rw-r--r--accessible/tests/mochitest/treeupdate/test_menu.xhtml127
-rw-r--r--accessible/tests/mochitest/treeupdate/test_menubutton.xhtml141
-rw-r--r--accessible/tests/mochitest/treeupdate/test_optgroup.html122
-rw-r--r--accessible/tests/mochitest/treeupdate/test_recreation.html93
-rw-r--r--accessible/tests/mochitest/treeupdate/test_select.html191
-rw-r--r--accessible/tests/mochitest/treeupdate/test_shadow_slots.html554
-rw-r--r--accessible/tests/mochitest/treeupdate/test_shutdown.xhtml131
-rw-r--r--accessible/tests/mochitest/treeupdate/test_table.html74
-rw-r--r--accessible/tests/mochitest/treeupdate/test_textleaf.html167
-rw-r--r--accessible/tests/mochitest/treeupdate/test_tooltip.xhtml75
-rw-r--r--accessible/tests/mochitest/treeupdate/test_visibility.html411
-rw-r--r--accessible/tests/mochitest/treeupdate/test_whitespace.html200
-rw-r--r--accessible/tests/mochitest/treeview.css15
-rw-r--r--accessible/tests/mochitest/treeview.js273
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> &amp; <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> &amp; <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,
+};