summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/events
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/mochitest/events')
-rw-r--r--accessible/tests/mochitest/events/a11y.ini67
-rw-r--r--accessible/tests/mochitest/events/docload_wnd.html39
-rw-r--r--accessible/tests/mochitest/events/focus.html10
-rw-r--r--accessible/tests/mochitest/events/scroll.html181
-rw-r--r--accessible/tests/mochitest/events/test_aria_alert.html92
-rw-r--r--accessible/tests/mochitest/events/test_aria_menu.html285
-rw-r--r--accessible/tests/mochitest/events/test_aria_objattr.html118
-rw-r--r--accessible/tests/mochitest/events/test_aria_owns.html129
-rw-r--r--accessible/tests/mochitest/events/test_aria_statechange.html208
-rw-r--r--accessible/tests/mochitest/events/test_attrs.html90
-rw-r--r--accessible/tests/mochitest/events/test_bug1322593-2.html83
-rw-r--r--accessible/tests/mochitest/events/test_bug1322593.html80
-rw-r--r--accessible/tests/mochitest/events/test_caretmove.html140
-rw-r--r--accessible/tests/mochitest/events/test_caretmove.xul72
-rw-r--r--accessible/tests/mochitest/events/test_coalescence.html864
-rw-r--r--accessible/tests/mochitest/events/test_contextmenu.html139
-rw-r--r--accessible/tests/mochitest/events/test_descrchange.html85
-rw-r--r--accessible/tests/mochitest/events/test_docload.html360
-rw-r--r--accessible/tests/mochitest/events/test_docload.xul243
-rw-r--r--accessible/tests/mochitest/events/test_docload_aria.html83
-rw-r--r--accessible/tests/mochitest/events/test_dragndrop.html110
-rw-r--r--accessible/tests/mochitest/events/test_flush.html77
-rw-r--r--accessible/tests/mochitest/events/test_focus_aria_activedescendant.html120
-rw-r--r--accessible/tests/mochitest/events/test_focus_autocomplete.xul518
-rw-r--r--accessible/tests/mochitest/events/test_focus_browserui.xul149
-rw-r--r--accessible/tests/mochitest/events/test_focus_canvas.html61
-rw-r--r--accessible/tests/mochitest/events/test_focus_contextmenu.xul99
-rw-r--r--accessible/tests/mochitest/events/test_focus_controls.html75
-rw-r--r--accessible/tests/mochitest/events/test_focus_dialog.html164
-rw-r--r--accessible/tests/mochitest/events/test_focus_doc.html95
-rw-r--r--accessible/tests/mochitest/events/test_focus_general.html179
-rw-r--r--accessible/tests/mochitest/events/test_focus_general.xul179
-rw-r--r--accessible/tests/mochitest/events/test_focus_listcontrols.xul189
-rw-r--r--accessible/tests/mochitest/events/test_focus_menu.xul119
-rw-r--r--accessible/tests/mochitest/events/test_focus_name.html122
-rw-r--r--accessible/tests/mochitest/events/test_focus_selects.html118
-rw-r--r--accessible/tests/mochitest/events/test_focus_tabbox.xul103
-rw-r--r--accessible/tests/mochitest/events/test_focus_tree.xul122
-rw-r--r--accessible/tests/mochitest/events/test_fromUserInput.html127
-rw-r--r--accessible/tests/mochitest/events/test_label.xul177
-rw-r--r--accessible/tests/mochitest/events/test_menu.xul202
-rw-r--r--accessible/tests/mochitest/events/test_mutation.html632
-rw-r--r--accessible/tests/mochitest/events/test_mutation.xhtml97
-rw-r--r--accessible/tests/mochitest/events/test_namechange.html123
-rw-r--r--accessible/tests/mochitest/events/test_namechange.xul92
-rw-r--r--accessible/tests/mochitest/events/test_scroll.xul131
-rw-r--r--accessible/tests/mochitest/events/test_scroll_caret.xul91
-rw-r--r--accessible/tests/mochitest/events/test_selection.html118
-rw-r--r--accessible/tests/mochitest/events/test_selection.xul255
-rw-r--r--accessible/tests/mochitest/events/test_selection_aria.html127
-rw-r--r--accessible/tests/mochitest/events/test_statechange.html287
-rw-r--r--accessible/tests/mochitest/events/test_text.html339
-rw-r--r--accessible/tests/mochitest/events/test_text_alg.html249
-rw-r--r--accessible/tests/mochitest/events/test_textattrchange.html115
-rw-r--r--accessible/tests/mochitest/events/test_textselchange.html86
-rw-r--r--accessible/tests/mochitest/events/test_tree.xul348
-rw-r--r--accessible/tests/mochitest/events/test_valuechange.html255
57 files changed, 9818 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/events/a11y.ini b/accessible/tests/mochitest/events/a11y.ini
new file mode 100644
index 000000000..4ea7c9d10
--- /dev/null
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -0,0 +1,67 @@
+[DEFAULT]
+support-files =
+ docload_wnd.html
+ focus.html
+ scroll.html
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/letters.gif
+
+[test_aria_alert.html]
+[test_aria_menu.html]
+[test_aria_objattr.html]
+[test_aria_owns.html]
+[test_aria_statechange.html]
+[test_attrs.html]
+[test_bug1322593.html]
+[test_bug1322593-2.html]
+[test_caretmove.html]
+[test_caretmove.xul]
+[test_coalescence.html]
+[test_contextmenu.html]
+[test_descrchange.html]
+[test_docload.html]
+[test_docload.xul]
+skip-if = buildapp == 'mulet'
+[test_docload_aria.html]
+[test_dragndrop.html]
+[test_flush.html]
+[test_focus_aria_activedescendant.html]
+[test_focus_autocomplete.xul]
+# Disabled on Linux and Windows due to frequent failures - bug 695019, bug 890795
+skip-if = os == 'win' || os == 'linux'
+[test_focus_browserui.xul]
+[test_focus_canvas.html]
+[test_focus_contextmenu.xul]
+[test_focus_controls.html]
+[test_focus_dialog.html]
+[test_focus_doc.html]
+[test_focus_general.html]
+[test_focus_general.xul]
+[test_focus_listcontrols.xul]
+[test_focus_menu.xul]
+[test_focus_name.html]
+[test_focus_selects.html]
+[test_focus_tabbox.xul]
+[test_focus_tree.xul]
+[test_fromUserInput.html]
+[test_label.xul]
+[test_menu.xul]
+[test_mutation.html]
+[test_mutation.xhtml]
+[test_namechange.xul]
+[test_namechange.html]
+[test_scroll.xul]
+[test_scroll_caret.xul]
+[test_selection.html]
+skip-if = buildapp == 'mulet' || os == 'mac'
+[test_selection.xul]
+skip-if = os == 'mac'
+[test_selection_aria.html]
+[test_statechange.html]
+[test_text.html]
+[test_text_alg.html]
+[test_textattrchange.html]
+[test_textselchange.html]
+[test_tree.xul]
+[test_valuechange.html]
+skip-if = os == 'mac'
diff --git a/accessible/tests/mochitest/events/docload_wnd.html b/accessible/tests/mochitest/events/docload_wnd.html
new file mode 100644
index 000000000..86ddfac5e
--- /dev/null
+++ b/accessible/tests/mochitest/events/docload_wnd.html
@@ -0,0 +1,39 @@
+<html>
+<head>
+ <title>Accessible events testing for document</title>
+ <script>
+ const STATE_BUSY = Components.interfaces.nsIAccessibleStates.STATE_BUSY;
+
+ var gService = null;
+ function waitForDocLoad()
+ {
+ if (!gService) {
+ gService = Components.classes["@mozilla.org/accessibilityService;1"].
+ getService(Components.interfaces.nsIAccessibilityService);
+ }
+
+ var accDoc = gService.getAccessibleFor(document);
+
+ var state = {};
+ accDoc.getState(state, {});
+ if (state.value & STATE_BUSY) {
+ window.setTimeout(waitForDocLoad, 0);
+ return;
+ }
+
+ hideIFrame();
+ }
+
+ function hideIFrame()
+ {
+ var iframe = document.getElementById("iframe");
+ gService.getAccessibleFor(iframe.contentDocument);
+ iframe.style.display = 'none';
+ }
+ </script>
+</head>
+
+<body onload="waitForDocLoad();">
+ <iframe id="iframe"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/focus.html b/accessible/tests/mochitest/events/focus.html
new file mode 100644
index 000000000..ab055df82
--- /dev/null
+++ b/accessible/tests/mochitest/events/focus.html
@@ -0,0 +1,10 @@
+<html>
+
+<head>
+ <title>editable document</title>
+</head>
+
+<body contentEditable="true">
+ editable document
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/scroll.html b/accessible/tests/mochitest/events/scroll.html
new file mode 100644
index 000000000..562e0a382
--- /dev/null
+++ b/accessible/tests/mochitest/events/scroll.html
@@ -0,0 +1,181 @@
+<html>
+
+<head>
+ <title>nsIAccessible actions testing for anchors</title>
+</head>
+
+<body>
+ <p>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ </p>
+ <a name="link1">link1</a>
+
+ <p style="color: blue">
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ </p>
+
+ <h1 id="heading_1">heading 1</h1>
+ <p style="color: blue">
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ text text text text text text text text text text text text text text <br>
+ </p>
+</body>
+<html>
diff --git a/accessible/tests/mochitest/events/test_aria_alert.html b/accessible/tests/mochitest/events/test_aria_alert.html
new file mode 100644
index 000000000..2dab35723
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_alert.html
@@ -0,0 +1,92 @@
+<html>
+
+<head>
+ <title>ARIA alert event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ 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">
+ function showAlert(aID)
+ {
+ this.DOMNode = document.createElement("div");
+
+ this.invoke = function showAlert_invoke()
+ {
+ this.DOMNode.setAttribute("role", "alert");
+ this.DOMNode.setAttribute("id", aID);
+ var text = document.createTextNode("alert");
+ this.DOMNode.appendChild(text);
+ document.body.appendChild(this.DOMNode);
+ };
+
+ this.getID = function showAlert_getID()
+ {
+ return "Show ARIA alert " + aID;
+ };
+ }
+
+ function changeAlert(aID)
+ {
+ this.__defineGetter__("DOMNode", function() { return getNode(aID) });
+
+ this.invoke = function changeAlert_invoke()
+ {
+ this.DOMNode.textContent = "new alert";
+ }
+
+ this.getID = function showAlert_getID()
+ {
+ return "Change ARIA alert " + aID;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuging
+ //enableLogging("tree,events,verbose");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_ALERT);
+
+ gQueue.push(new showAlert("alert"));
+ gQueue.push(new changeAlert("alert"));
+
+ 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=591199"
+ title="mochitest for bug 334386: fire alert event when ARIA alert is shown or new its children are inserted">
+ Mozilla Bug 591199
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_aria_menu.html b/accessible/tests/mochitest/events/test_aria_menu.html
new file mode 100644
index 000000000..5ac595ebf
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_menu.html
@@ -0,0 +1,285 @@
+<html>
+
+<head>
+ <title>ARIA menu events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ const kViaDisplayStyle = 0;
+ const kViaVisibilityStyle = 1;
+
+ function focusMenu(aMenuBarID, aMenuID, aActiveMenuBarID)
+ {
+ this.eventSeq = [];
+
+ if (aActiveMenuBarID) {
+ this.eventSeq.push(new invokerChecker(EVENT_MENU_END,
+ getNode(aActiveMenuBarID)));
+ }
+
+ this.eventSeq.push(new invokerChecker(EVENT_MENU_START, getNode(aMenuBarID)));
+ this.eventSeq.push(new invokerChecker(EVENT_FOCUS, getNode(aMenuID)));
+
+ this.invoke = function focusMenu_invoke()
+ {
+ getNode(aMenuID).focus();
+ }
+
+ this.getID = function focusMenu_getID()
+ {
+ return "focus menu '" + aMenuID + "'";
+ }
+ }
+
+ function showMenu(aMenuID, aParentMenuID, aHow)
+ {
+ this.menuNode = getNode(aMenuID);
+
+ // Because of aria-owns processing we may have menupopup start fired before
+ // related show event.
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.menuNode),
+ new invokerChecker(EVENT_REORDER, getNode(aParentMenuID)),
+ new invokerChecker(EVENT_MENUPOPUP_START, this.menuNode)
+ ];
+
+ this.invoke = function showMenu_invoke()
+ {
+ if (aHow == kViaDisplayStyle)
+ this.menuNode.style.display = "block";
+ else
+ this.menuNode.style.visibility = "visible";
+ };
+
+ this.getID = function showMenu_getID()
+ {
+ return "Show ARIA menu '" + aMenuID + "' by " +
+ (aHow == kViaDisplayStyle ? "display" : "visibility") +
+ " style tricks";
+ };
+ }
+
+ function closeMenu(aMenuID, aParentMenuID, aHow)
+ {
+ this.menuNode = getNode(aMenuID);
+ this.menu = null;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getMenu, this),
+ new invokerChecker(EVENT_MENUPOPUP_END, getMenu, this),
+ new invokerChecker(EVENT_REORDER, getNode(aParentMenuID))
+ ];
+
+ this.invoke = function closeMenu_invoke()
+ {
+ // Store menu accessible reference while menu is still open.
+ this.menu = getAccessible(this.menuNode);
+
+ // Hide menu.
+ if (aHow == kViaDisplayStyle)
+ this.menuNode.style.display = "none";
+ else
+ this.menuNode.style.visibility = "hidden";
+ }
+
+ this.getID = function closeMenu_getID()
+ {
+ return "Close ARIA menu " + aMenuID + " by " +
+ (aHow == kViaDisplayStyle ? "display" : "visibility") +
+ " style tricks";
+ }
+
+ function getMenu(aThisObj)
+ {
+ return aThisObj.menu;
+ }
+ }
+
+ function focusInsideMenu(aMenuID, aMenuBarID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode(aMenuID))
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_MENU_END, getNode(aMenuBarID))
+ ];
+
+ this.invoke = function focusInsideMenu_invoke()
+ {
+ getNode(aMenuID).focus();
+ }
+
+ this.getID = function focusInsideMenu_getID()
+ {
+ return "focus menu '" + aMenuID + "'";
+ }
+ }
+
+ function blurMenu(aMenuBarID)
+ {
+ var eventSeq = [
+ new invokerChecker(EVENT_MENU_END, getNode(aMenuBarID)),
+ new invokerChecker(EVENT_FOCUS, getNode("outsidemenu"))
+ ];
+
+ this.__proto__ = new synthClick("outsidemenu", eventSeq);
+
+ this.getID = function blurMenu_getID()
+ {
+ return "blur menu";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuging
+ //enableLogging("tree,events,verbose");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new focusMenu("menubar2", "menu-help"));
+ gQueue.push(new focusMenu("menubar", "menu-file", "menubar2"));
+ gQueue.push(new showMenu("menupopup-file", "menu-file", kViaDisplayStyle));
+ gQueue.push(new closeMenu("menupopup-file", "menu-file", kViaDisplayStyle));
+ gQueue.push(new showMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
+ gQueue.push(new closeMenu("menupopup-edit", "menu-edit", kViaVisibilityStyle));
+ gQueue.push(new focusInsideMenu("menu-edit", "menubar"));
+ gQueue.push(new blurMenu("menubar"));
+
+ gQueue.push(new focusMenu("menubar3", "mb3-mi-outside"));
+ gQueue.push(new showMenu("mb4-menu", document, kViaDisplayStyle));
+ gQueue.push(new focusMenu("menubar4", "mb4-item1"));
+ gQueue.push(new focusMenu("menubar5", "mb5-mi"));
+
+ gQueue.push(new synthFocus("mi6"));
+
+ 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=606207"
+ title="Dojo dropdown buttons are broken">
+ Bug 606207
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=614829"
+ title="Menupopup end event isn't fired for ARIA menus">
+ Bug 614829
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=615189"
+ title="Clean up FireAccessibleFocusEvent">
+ Bug 615189
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Bug 673958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=933322"
+ title="menustart/end events are missing when aria-owns makes a menu hierarchy">
+ Bug 933322
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=934460"
+ title="menustart/end events may be missed when top level menuitem is focused">
+ Bug 934460
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=970005"
+ title="infinite long loop in a11y:FocusManager::ProcessFocusEvent">
+ Bug 970005
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="menubar" role="menubar">
+ <div id="menu-file" role="menuitem" tabindex="0">
+ File
+ <div id="menupopup-file" role="menu" style="display: none;">
+ <div id="menuitem-newtab" role="menuitem" tabindex="0">New Tab</div>
+ <div id="menuitem-newwindow" role="menuitem" tabindex="0">New Window</div>
+ </div>
+ </div>
+ <div id="menu-edit" role="menuitem" tabindex="0">
+ Edit
+ <div id="menupopup-edit" role="menu" style="visibility: hidden;">
+ <div id="menuitem-undo" role="menuitem" tabindex="0">Undo</div>
+ <div id="menuitem-redo" role="menuitem" tabindex="0">Redo</div>
+ </div>
+ </div>
+ </div>
+ <div id="menubar2" role="menubar">
+ <div id="menu-help" role="menuitem" tabindex="0">
+ Help
+ <div id="menupopup-help" role="menu" style="display: none;">
+ <div id="menuitem-about" role="menuitem" tabindex="0">About</div>
+ </div>
+ </div>
+ </div>
+ <div tabindex="0" id="outsidemenu">outsidemenu</div>
+
+ <!-- aria-owns relations -->
+ <div id="menubar3" role="menubar" aria-owns="mb3-mi-outside"></div>
+ <div id="mb3-mi-outside" role="menuitem" tabindex="0">Outside</div>
+
+ <div id="menubar4" role="menubar">
+ <div id="mb4_topitem" role="menuitem" aria-haspopup="true"
+ aria-owns="mb4-menu">Item</div>
+ </div>
+ <div id="mb4-menu" role="menu" style="display:none;">
+ <div role="menuitem" id="mb4-item1" tabindex="0">Item 1.1</div>
+ <div role="menuitem" tabindex="0">Item 1.2</div>
+ </div>
+
+ <!-- focus top-level menu item having haspopup -->
+ <div id="menubar5" role="menubar">
+ <div role="menuitem" aria-haspopup="true" id="mb5-mi" tabindex="0">
+ Item
+ <div role="menu" style="display:none;">
+ <div role="menuitem" tabindex="0">Item 1.1</div>
+ <div role="menuitem" tabindex="0">Item 1.2</div>
+ </div>
+ </div>
+ </div>
+
+ <!-- other aria-owns relations -->
+ <div id="mi6" tabindex="0" role="menuitem">aria-owned item</div>
+ <div aria-owns="mi6">Obla</div>
+
+ <div id="eventdump"></div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_aria_objattr.html b/accessible/tests/mochitest/events/test_aria_objattr.html
new file mode 100644
index 000000000..5f16ba794
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_objattr.html
@@ -0,0 +1,118 @@
+<html>
+
+<head>
+ <title>Accessible ARIA object attribute changes</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+ function updateAttribute(aID, aAttr, aValue)
+ {
+ this.node = getNode(aID);
+ this.accessible = getAccessible(this.node);
+
+ this.eventSeq = [
+ new objAttrChangedChecker(aID, aAttr),
+ ];
+
+ this.invoke = function updateAttribute_invoke()
+ {
+ this.node.setAttribute(aAttr, aValue);
+ };
+
+ this.getID = function updateAttribute_getID()
+ {
+ return aAttr + " for " + aID + " " + aValue;
+ };
+ }
+
+ function updateARIAHidden(aID, aIsDefined, aChildId)
+ {
+ this.__proto__ = new updateAttribute(aID, "aria-hidden",
+ aIsDefined ? "true" : "false");
+
+ this.finalCheck = function updateARIAHidden()
+ {
+ if (aIsDefined) {
+ testAttrs(aID, {"hidden" : "true"}, true);
+ testAttrs(aChildId, {"hidden" : "true"}, true);
+ } else {
+ testAbsentAttrs(aID, { "hidden": "true"});
+ testAbsentAttrs(aChildId, { "hidden": "true"});
+ }
+ }
+ }
+
+ // Debug stuff.
+ // gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new updateARIAHidden("hideable", true, "hideable_child"));
+ gQueue.push(new updateARIAHidden("hideable", false, "hideable_child"));
+
+ gQueue.push(new updateAttribute("sortable", "aria-sort", "ascending"));
+
+ // For experimental ARIA extensions
+ gQueue.push(new updateAttribute("custom", "aria-blah", "true"));
+
+ 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=581096"
+ title="Add support for aria-hidden">
+ Mozilla Bug 581096
+ </a>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=640707"
+ title="Add event support for aria-sort">
+ Mozilla Bug 640707
+ </a>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=640707"
+ title="Expand support for aria attribute change events">
+ Mozilla Bug 563862
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="hideable"><div id="hideable_child">Hi</div><div>there</div></div>
+
+ <div id="sortable" role="columnheader" aria-sort="none">aria-sort</div>
+
+ <div id="custom" role="custom" aria-blah="false">Fat free cheese</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_aria_owns.html b/accessible/tests/mochitest/events/test_aria_owns.html
new file mode 100644
index 000000000..a415a6d8d
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_owns.html
@@ -0,0 +1,129 @@
+<html>
+
+<head>
+ <title>Aria-owns targets shouldn't be on invalidation list so shouldn't have
+ show/hide events</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests.
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+ //enableLogging("tree,eventTree,verbose");
+
+ /**
+ * Aria-owns target shouldn't have a show event.
+ * Markup:
+ * <div id="t1_fc" aria-owns="t1_owns"></div>
+ * <span id="t1_owns"></div>
+ */
+ function testAriaOwns()
+ {
+ this.parent = getNode("t1");
+ this.fc = document.createElement("div");
+ this.fc.setAttribute("id", "t1_fc");
+ this.owns = document.createElement("span");
+ this.owns.setAttribute("id", "t1_owns");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.fc),
+ new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+ ];
+
+ this.invoke = function testAriaOwns_invoke()
+ {
+ getNode("t1").appendChild(this.fc);
+ getNode("t1").appendChild(this.owns);
+ getNode("t1_fc").setAttribute("aria-owns", "t1_owns");
+ };
+
+ this.getID = function testAriaOwns_getID() {
+ return "Aria-owns target shouldn't have show event";
+ };
+ }
+
+ /**
+ * Target of both aria-owns and other aria attribute like aria-labelledby
+ * shouldn't have a show event.
+ * Markup:
+ * <div id="t2_fc" aria-owns="t1_owns"></div>
+ * <div id="t2_sc" aria-labelledby="t2_owns"></div>
+ * <span id="t2_owns"></div>
+ */
+ function testAriaOwnsAndLabelledBy()
+ {
+ this.parent = getNode("t2");
+ this.fc = document.createElement("div");
+ this.fc.setAttribute("id", "t2_fc");
+ this.sc = document.createElement("div");
+ this.sc.setAttribute("id", "t2_sc");
+ this.owns = document.createElement("span");
+ this.owns.setAttribute("id", "t2_owns");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.fc),
+ new invokerChecker(EVENT_SHOW, this.sc),
+ new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+ ];
+
+ this.invoke = function testAriaOwns_invoke()
+ {
+ getNode("t2").appendChild(this.fc);
+ getNode("t2").appendChild(this.sc);
+ getNode("t2").appendChild(this.owns);
+ getNode("t2_fc").setAttribute("aria-owns", "t2_owns");
+ getNode("t2_sc").setAttribute("aria-labelledby", "t2_owns");
+ };
+
+ this.getID = function testAriaOwns_getID() {
+ return "Aria-owns and aria-labelledby target shouldn't have show event";
+ };
+ }
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+ gQueue.push(new testAriaOwns());
+ gQueue.push(new testAriaOwnsAndLabelledBy());
+
+ 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=1296420"
+ title="Aria-owns targets shouldn't be on invalidation list so shouldn't
+ have show/hide events">
+ Mozilla Bug 1296420
+ </a><br>
+
+ <div id="testContainer">
+ <div id="t1"></div>
+
+ <div id="t2"></div>
+ </div>
+
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_aria_statechange.html b/accessible/tests/mochitest/events/test_aria_statechange.html
new file mode 100644
index 000000000..d8c833157
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_aria_statechange.html
@@ -0,0 +1,208 @@
+<html>
+
+<head>
+ <title>ARIA state change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ 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">
+
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ //gA11yEventDumpID = "eventdump"; // debugging
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function expandNode(aID, aIsExpanded)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new expandedStateChecker(aIsExpanded, this.DOMNode)
+ ];
+
+ this.invoke = function expandNode_invoke()
+ {
+ this.DOMNode.setAttribute("aria-expanded",
+ (aIsExpanded ? "true" : "false"));
+ };
+
+ this.getID = function expandNode_getID()
+ {
+ return prettyName(aID) + " aria-expanded changed to '" + aIsExpanded + "'";
+ };
+ }
+
+ function busyify(aID, aIsBusy)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new stateChangeChecker(STATE_BUSY, kOrdinalState, aIsBusy, this.DOMNode)
+ ];
+
+ this.invoke = function busyify_invoke()
+ {
+ this.DOMNode.setAttribute("aria-busy", (aIsBusy ? "true" : "false"));
+ };
+
+ this.getID = function busyify_getID()
+ {
+ return prettyName(aID) + " aria-busy changed to '" + aIsBusy + "'";
+ };
+ }
+
+ function setAttrOfMixedType(aID, aAttr, aState, aValue)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new stateChangeChecker(aState, kOrdinalState,
+ aValue == "true", this.DOMNode)
+ ];
+
+ if (hasState(aID, STATE_MIXED) || aValue == "mixed") {
+ this.eventSeq.push(
+ new stateChangeChecker(STATE_MIXED, kOrdinalState,
+ aValue == "mixed", this.DOMNode)
+ );
+ }
+
+ this.invoke = function setAttrOfMixedType_invoke()
+ {
+ this.DOMNode.setAttribute(aAttr, aValue);
+ };
+
+ this.getID = function setAttrOfMixedType_getID()
+ {
+ return prettyName(aID) + " " + aAttr + " changed to '" + aValue + "'";
+ };
+ }
+
+ function setPressed(aID, aValue)
+ {
+ this.__proto__ =
+ new setAttrOfMixedType(aID, "aria-pressed", STATE_PRESSED, aValue);
+ }
+
+ function setChecked(aID, aValue)
+ {
+ this.__proto__ =
+ new setAttrOfMixedType(aID, "aria-checked", STATE_CHECKED, aValue);
+ }
+
+ function buildQueueForAttr(aList, aQueue, aID, aInvokerFunc)
+ {
+ for (var i = 0; i < aList.length; i++) {
+ for (var j = i + 1; j < aList.length; j++) {
+ // XXX: changes from/to "undefined"/"" shouldn't fire state change
+ // events, bug 472142.
+ aQueue.push(new aInvokerFunc(aID, aList[i]));
+ aQueue.push(new aInvokerFunc(aID, aList[j]));
+ }
+ }
+ }
+
+ function buildQueueForAttrOfMixedType(aQueue, aID, aInvokerFunc)
+ {
+ var list = [ "", "undefined", "false", "true", "mixed" ];
+ buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
+ }
+
+ function buildQueueForAttrOfBoolType(aQueue, aID, aInvokerFunc)
+ {
+ var list = [ "", "undefined", "false", "true" ];
+ buildQueueForAttr(list, aQueue, aID, aInvokerFunc);
+ }
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new expandNode("section", true));
+ gQueue.push(new expandNode("section", false));
+ gQueue.push(new expandNode("div", true));
+ gQueue.push(new expandNode("div", false));
+
+ gQueue.push(new busyify("aria_doc", true));
+ gQueue.push(new busyify("aria_doc", false));
+
+ buildQueueForAttrOfMixedType(gQueue, "pressable", setPressed);
+ buildQueueForAttrOfMixedType(gQueue, "pressable_native", setPressed);
+ buildQueueForAttrOfMixedType(gQueue, "checkable", setChecked);
+ buildQueueForAttrOfBoolType(gQueue, "checkableBool", setChecked);
+
+ 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=551684"
+ title="No statechange event for aria-expanded on native HTML elements, is fired on ARIA widgets">
+ Mozilla Bug 551684
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=648133"
+ title="fire state change event for aria-busy">
+ Mozilla Bug 648133
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=467143"
+ title="mixed state change event is fired for focused accessible only">
+ Mozilla Bug 467143
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=989958"
+ title="Pressed state is not exposed on a button element with aria-pressed attribute">
+ Mozilla Bug 989958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1136563"
+ title="Support ARIA 1.1 switch role">
+ Mozilla Bug 1136563
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <!-- aria-expanded -->
+ <div id="section" role="section" aria-expanded="false">expandable section</div>
+ <div id="div" aria-expanded="false">expandable native div</div>
+
+ <!-- aria-busy -->
+ <div id="aria_doc" role="document" tabindex="0">A document</div>
+
+ <!-- aria-pressed -->
+ <div id="pressable" role="button"></div>
+ <button id="pressable_native"></button>
+
+ <!-- aria-checked -->
+ <div id="checkable" role="checkbox"></div>
+ <div id="checkableBool" role="switch"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_attrs.html b/accessible/tests/mochitest/events/test_attrs.html
new file mode 100644
index 000000000..1d5577572
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_attrs.html
@@ -0,0 +1,90 @@
+<html>
+
+<head>
+ <title>Event object attributes tests</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Test "event-from-input" object attribute.
+ */
+ function eventFromInputChecker(aEventType, aID, aValue, aNoTargetID)
+ {
+ this.type = aEventType;
+ this.target = getAccessible(aID);
+
+ this.noTarget = getNode(aNoTargetID);
+
+ this.check = function checker_check(aEvent)
+ {
+ testAttrs(aEvent.accessible, { "event-from-input": aValue }, true);
+
+ var accessible = getAccessible(this.noTarget);
+ testAbsentAttrs(accessible, { "event-from-input": "" });
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ var id = "textbox", noTargetId = "textarea";
+ var checker =
+ new eventFromInputChecker(EVENT_FOCUS, id, "false", noTargetId);
+ gQueue.push(new synthFocus(id, checker));
+
+ if (!MAC) { // Mac failure is bug 541093
+ var checker =
+ new eventFromInputChecker(EVENT_TEXT_CARET_MOVED, id, "true", noTargetId);
+ gQueue.push(new synthHomeKey(id, checker));
+ }
+
+ 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=540285"
+ title="Event object attributes testing">
+ Mozilla Bug 540285
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox" value="hello">
+ <textarea id="textarea"></textarea>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_bug1322593-2.html b/accessible/tests/mochitest/events/test_bug1322593-2.html
new file mode 100644
index 000000000..20136d393
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_bug1322593-2.html
@@ -0,0 +1,83 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+ function changeMultipleElements()
+ {
+ this.node1 = getNode("span1");
+ this.node2 = getNode("span2");
+
+ this.eventSeq = [
+ new textChangeChecker("container", 0, 5, "hello", false, undefined, true),
+ new textChangeChecker("container", 6, 11, "world", false, undefined, true),
+ new orderChecker(),
+ new textChangeChecker("container", 0, 1, "a", true, undefined, true),
+ new textChangeChecker("container", 7, 8, "b", true, undefined, true)
+ ];
+
+ this.invoke = function changeMultipleElements_invoke()
+ {
+ this.node1.textContent = "a";
+ this.node2.textContent = "b";
+ }
+
+ this.getID = function changeMultipleElements_invoke_getID()
+ {
+ return "Change the text content of multiple sibling divs";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+// gA11yEventDumpToConsole = true; // debugging
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeMultipleElements());
+
+ 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=1322593"
+ title="missing text change events when multiple elements updated at once">
+ Mozilla Bug 1322593
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="container">
+ <span id="span1">hello</span>
+ <span>your</span>
+ <span id="span2">world</span>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_bug1322593.html b/accessible/tests/mochitest/events/test_bug1322593.html
new file mode 100644
index 000000000..38786d0b9
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_bug1322593.html
@@ -0,0 +1,80 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+ function changeMultipleElements()
+ {
+ this.node1 = getNode("div1");
+ this.node2 = getNode("div2");
+
+ this.eventSeq = [
+ new textChangeChecker("div1", 0, 5, "hello", false, undefined, true),
+ new textChangeChecker("div2", 0, 5, "world", false, undefined, true),
+ new orderChecker(),
+ new textChangeChecker("div1", 0, 1, "a", true, undefined, true),
+ new textChangeChecker("div2", 0, 1, "b", true, undefined, true)
+ ];
+
+ this.invoke = function changeMultipleElements_invoke()
+ {
+ this.node1.textContent = "a";
+ this.node2.textContent = "b";
+ }
+
+ this.getID = function changeMultipleElements_invoke_getID()
+ {
+ return "Change the text content of multiple sibling divs";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+// gA11yEventDumpToConsole = true; // debugging
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeMultipleElements());
+
+ 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=1322593"
+ title="missing text change events when multiple elements updated at once">
+ Mozilla Bug 1322593
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="div1">hello</div>
+ <div id="div2">world</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_caretmove.html b/accessible/tests/mochitest/events/test_caretmove.html
new file mode 100644
index 000000000..6d39c4ef6
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_caretmove.html
@@ -0,0 +1,140 @@
+<html>
+
+<head>
+ <title>Accessible caret move events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+ /**
+ * Click checker.
+ */
+ function clickChecker(aCaretOffset, aID, aExtraNodeOrID, aExtraCaretOffset)
+ {
+ this.__proto__ = new caretMoveChecker(aCaretOffset, aID);
+
+ this.extraNode = getNode(aExtraNodeOrID);
+
+ this.check = function clickChecker_check(aEvent)
+ {
+ this.__proto__.check(aEvent);
+
+ if (this.extraNode) {
+ var acc = getAccessible(this.extraNode, [nsIAccessibleText]);
+ is(acc.caretOffset, aExtraCaretOffset,
+ "Wrong caret offset for " + aExtraNodeOrID);
+ }
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ // test caret move events and caret offsets
+ gQueue = new eventQueue();
+
+ var id = "textbox";
+ gQueue.push(new synthFocus(id, new caretMoveChecker(5, id)));
+ gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, id)));
+ gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+
+ id = "textarea";
+ gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+ gQueue.push(new synthDownKey(id, new caretMoveChecker(12, id)));
+
+ id = "textarea_wrapped";
+ gQueue.push(new setCaretOffset(id, 4, id));
+ gQueue.push(new synthLeftKey(id, new caretMoveChecker(4, id)));
+
+ id = "p";
+ gQueue.push(new synthClick(id, new caretMoveChecker(0, id)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, id)));
+ gQueue.push(new synthDownKey(id, new caretMoveChecker(6, id)));
+
+ id = "p1_in_div";
+ gQueue.push(new synthClick(id, new clickChecker(0, id, "p2_in_div", -1)));
+
+ id = "p";
+ gQueue.push(new synthShiftTab(id, new caretMoveChecker(0, id)));
+ id = "textarea";
+ gQueue.push(new synthShiftTab(id, new caretMoveChecker(12, id)));
+ id = "p";
+ gQueue.push(new synthTab(id, new caretMoveChecker(0, id)));
+
+ // Set caret after a child of span element, i.e. after 'text' text.
+ gQueue.push(new moveCaretToDOMPoint("test1", getNode("test1_span"), 1,
+ 4, "test1"));
+ gQueue.push(new moveCaretToDOMPoint("test2", getNode("test2_span"), 1,
+ 4, "test2"));
+
+ // empty text element
+ gQueue.push(new moveCaretToDOMPoint("test3", getNode("test3"), 0,
+ 0, "test3"));
+ gQueue.push(new moveCaretToDOMPoint("test4", getNode("test4_span"), 0,
+ 0, "test4"));
+
+ 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=454377"
+ title="Accessible caret move events testing">
+ Bug 454377
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=567571"
+ title="caret-moved events missing at the end of a wrapped line of text">
+ Bug 567571
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=824901"
+ title="HyperTextAccessible::DOMPointToHypertextOffset fails for node and offset equal to node child count">
+ Bug 824901
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox" value="hello"/>
+ <textarea id="textarea">text<br>text</textarea>
+ <p id="p" contentEditable="true"><span>text</span><br/>text</p>
+ <div id="div" contentEditable="true"><p id="p1_in_div">text</p><p id="p2_in_div">text</p></div>
+
+ <p contentEditable="true" id="test1"><span id="test1_span">text</span>ohoho</p>
+ <p contentEditable="true" id="test2"><span><span id="test2_span">text</span></span>ohoho</p>
+ <p contentEditable="true" id="test3"></p>
+ <p contentEditable="true" id="test4"><span id="test4_span"></span></p>
+
+ <textarea id="textarea_wrapped" cols="5">hey friend</textarea>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_caretmove.xul b/accessible/tests/mochitest/events/test_caretmove.xul
new file mode 100644
index 000000000..cf4dcd483
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_caretmove.xul
@@ -0,0 +1,72 @@
+<?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="Caret move event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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">
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ if (MAC) {
+ todo(false, "Make these tests pass on OSX (bug 650294)");
+ SimpleTest.finish();
+ return;
+ }
+
+ gQueue = new eventQueue(EVENT_TEXT_CARET_MOVED);
+
+ var id = "textbox";
+ var input = getNode(id).inputField;
+ gQueue.push(new synthFocus(id, new caretMoveChecker(5, input)));
+ gQueue.push(new synthSelectAll(id, new caretMoveChecker(5, input)));
+ gQueue.push(new synthHomeKey(id, new caretMoveChecker(0, input)));
+ gQueue.push(new synthRightKey(id, new caretMoveChecker(1, input)));
+
+ 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=634240"
+ title="No caret move events are fired for XUL textbox accessible">
+ Mozilla Bug 634240
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="textbox" value="hello"/>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_coalescence.html b/accessible/tests/mochitest/events/test_coalescence.html
new file mode 100644
index 000000000..d95ef99b0
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -0,0 +1,864 @@
+<html>
+
+<head>
+ <title>Accessible mutation events coalescence testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker base classes
+
+ const kRemoveElm = 1;
+ const kHideElm = 2;
+ const kAddElm = 3;
+ const kShowElm = 4;
+
+ /**
+ * Base class to test of mutation events coalescence.
+ */
+ function coalescenceBase(aChildAction, aParentAction,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ // Invoker interface
+
+ this.invoke = function coalescenceBase_invoke()
+ {
+ if (aPerformActionOnChildInTheFirstPlace) {
+ this.invokeAction(this.childNode, aChildAction);
+ this.invokeAction(this.parentNode, aParentAction);
+ } else {
+ this.invokeAction(this.parentNode, aParentAction);
+ this.invokeAction(this.childNode, aChildAction);
+ }
+ }
+
+ this.getID = function coalescenceBase_getID()
+ {
+ var childAction = this.getActionName(aChildAction) + " child";
+ var parentAction = this.getActionName(aParentAction) + " parent";
+
+ if (aPerformActionOnChildInTheFirstPlace)
+ return childAction + " and then " + parentAction;
+
+ return parentAction + " and then " + childAction;
+ }
+
+ this.finalCheck = function coalescenceBase_check()
+ {
+ if (this.getEventType(aChildAction) == EVENT_HIDE) {
+ testIsDefunct(this.child);
+ }
+ if (this.getEventType(aParentAction) == EVENT_HIDE) {
+ testIsDefunct(this.parent);
+ }
+ }
+
+ // Implementation details
+
+ this.invokeAction = function coalescenceBase_invokeAction(aNode, aAction)
+ {
+ switch (aAction) {
+ case kRemoveElm:
+ aNode.parentNode.removeChild(aNode);
+ break;
+
+ case kHideElm:
+ aNode.style.display = "none";
+ break;
+
+ case kAddElm:
+ if (aNode == this.parentNode)
+ this.hostNode.appendChild(this.parentNode);
+ else
+ this.parentNode.appendChild(this.childNode);
+ break;
+
+ case kShowElm:
+ aNode.style.display = "block";
+ break;
+
+ default:
+ return INVOKER_ACTION_FAILED;
+ }
+ }
+
+ this.getEventType = function coalescenceBase_getEventType(aAction)
+ {
+ switch (aAction) {
+ case kRemoveElm: case kHideElm:
+ return EVENT_HIDE;
+ case kAddElm: case kShowElm:
+ return EVENT_SHOW;
+ }
+ }
+
+ this.getActionName = function coalescenceBase_getActionName(aAction)
+ {
+ switch (aAction) {
+ case kRemoveElm:
+ return "remove";
+ case kHideElm:
+ return "hide";
+ case kAddElm:
+ return "add";
+ case kShowElm:
+ return "show";
+ default:
+ return "??";
+ }
+ }
+
+ this.initSequence = function coalescenceBase_initSequence()
+ {
+ // expected events
+ var eventType = this.getEventType(aParentAction);
+ this.eventSeq = [
+ new invokerChecker(eventType, this.parentNode),
+ new invokerChecker(EVENT_REORDER, this.hostNode)
+ ];
+
+ // unexpected events
+ this.unexpectedEventSeq = [
+ new invokerChecker(this.getEventType(aChildAction), this.childNode),
+ new invokerChecker(EVENT_REORDER, this.parentNode)
+ ];
+ }
+ }
+
+ /**
+ * Remove or hide mutation events coalescence testing.
+ */
+ function removeOrHideCoalescenceBase(aChildID, aParentID,
+ aChildAction, aParentAction,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ this.__proto__ = new coalescenceBase(aChildAction, aParentAction,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init = function removeOrHideCoalescenceBase_init()
+ {
+ this.childNode = getNode(aChildID);
+ this.parentNode = getNode(aParentID);
+ this.child = getAccessible(this.childNode);
+ this.parent = getAccessible(this.parentNode);
+ this.hostNode = this.parentNode.parentNode;
+ }
+
+ // Initalization
+
+ this.init();
+ this.initSequence();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Remove child node and then its parent node from DOM tree.
+ */
+ function removeChildNParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kRemoveElm,
+ true);
+ }
+
+ /**
+ * Remove parent node and then its child node from DOM tree.
+ */
+ function removeParentNChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kRemoveElm,
+ false);
+ }
+
+ /**
+ * Hide child node and then its parent node.
+ */
+ function hideChildNParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kHideElm,
+ true);
+ }
+
+ /**
+ * Hide parent node and then its child node.
+ */
+ function hideParentNChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kHideElm,
+ false);
+ }
+
+ /**
+ * Hide child node and then remove its parent node.
+ */
+ function hideChildNRemoveParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kRemoveElm,
+ true);
+ }
+
+ /**
+ * Hide parent node and then remove its child node.
+ */
+ function hideParentNRemoveChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kHideElm,
+ false);
+ }
+
+ /**
+ * Remove child node and then hide its parent node.
+ */
+ function removeChildNHideParent(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kRemoveElm, kHideElm,
+ true);
+ }
+
+ /**
+ * Remove parent node and then hide its child node.
+ */
+ function removeParentNHideChild(aChildID, aParentID)
+ {
+ this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
+ kHideElm, kRemoveElm,
+ false);
+ }
+
+ /**
+ * Create and append parent node and create and append child node to it.
+ */
+ function addParentNChild(aHostID, aPerformActionOnChildInTheFirstPlace)
+ {
+ this.init = function addParentNChild_init()
+ {
+ this.hostNode = getNode(aHostID);
+ this.parentNode = document.createElement("select");
+ this.childNode = document.createElement("option");
+ this.childNode.textContent = "testing";
+ }
+
+ this.__proto__ = new coalescenceBase(kAddElm, kAddElm,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init();
+ this.initSequence();
+ }
+
+ /**
+ * Show parent node and show child node to it.
+ */
+ function showParentNChild(aParentID, aChildID,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ this.init = function showParentNChild_init()
+ {
+ this.parentNode = getNode(aParentID);
+ this.hostNode = this.parentNode.parentNode;
+ this.childNode = getNode(aChildID);
+ }
+
+ this.__proto__ = new coalescenceBase(kShowElm, kShowElm,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init();
+ this.initSequence();
+ }
+
+ /**
+ * Create and append child node to the DOM and then show parent node.
+ */
+ function showParentNAddChild(aParentID,
+ aPerformActionOnChildInTheFirstPlace)
+ {
+ this.init = function showParentNAddChild_init()
+ {
+ this.parentNode = getNode(aParentID);
+ this.hostNode = this.parentNode.parentNode;
+ this.childNode = document.createElement("option");
+ this.childNode.textContent = "testing";
+ }
+
+ this.__proto__ = new coalescenceBase(kAddElm, kShowElm,
+ aPerformActionOnChildInTheFirstPlace);
+
+ this.init();
+ this.initSequence();
+ }
+
+ /**
+ * Remove children and parent
+ */
+ function removeGrandChildrenNHideParent(aChild1Id, aChild2Id, aParentId)
+ {
+ this.child1 = getNode(aChild1Id);
+ this.child2 = getNode(aChild2Id);
+ this.parent = getNode(aParentId);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aParentId)),
+ new invokerChecker(EVENT_REORDER, getNode(aParentId).parentNode),
+ new unexpectedInvokerChecker(EVENT_HIDE, getAccessible(aChild1Id)),
+ new unexpectedInvokerChecker(EVENT_HIDE, getAccessible(aChild2Id)),
+ new unexpectedInvokerChecker(EVENT_REORDER, getAccessible(aParentId))
+ ];
+
+ this.invoke = function removeGrandChildrenNHideParent_invoke()
+ {
+ this.child1.parentNode.removeChild(this.child1);
+ this.child2.parentNode.removeChild(this.child2);
+ this.parent.hidden = true;
+ }
+
+ this.getID = function removeGrandChildrenNHideParent_getID() {
+ return "remove grand children of different parents and then hide their grand parent";
+ }
+ }
+
+ /**
+ * Remove a child, and then its parent.
+ */
+ function test3()
+ {
+ this.o = getAccessible("t3_o");
+ this.ofc = getAccessible("t3_o").firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.o),
+ new invokerChecker(EVENT_REORDER, "t3_lb"),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o)
+ ];
+
+ this.invoke = function test3_invoke()
+ {
+ getNode("t3_o").textContent = "";
+ getNode("t3_lb").removeChild(getNode("t3_o"));
+ }
+
+ this.finalCheck = function test3_finalCheck()
+ {
+ testIsDefunct(this.o);
+ testIsDefunct(this.ofc);
+ }
+
+ this.getID = function test3_getID() {
+ return "remove a child, and then its parent";
+ }
+ }
+
+ /**
+ * Remove children, and then a parent of 2nd child.
+ */
+ function test4()
+ {
+ this.o1 = getAccessible("t4_o1");
+ this.o1fc = this.o1.firstChild;
+ this.o2 = getAccessible("t4_o2");
+ this.o2fc = this.o2.firstChild;
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.o1fc),
+ new invokerChecker(EVENT_HIDE, this.o2),
+ new invokerChecker(EVENT_REORDER, "t4_lb"),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.o2fc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o1),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o2)
+ ];
+
+ this.invoke = function test4_invoke()
+ {
+ getNode("t4_o1").textContent = "";
+ getNode("t4_o2").textContent = "";
+ getNode("t4_lb").removeChild(getNode("t4_o2"));
+ }
+
+ this.finalCheck = function test4_finalCheck()
+ {
+ testIsDefunct(this.o1fc);
+ testIsDefunct(this.o2);
+ testIsDefunct(this.o2fc);
+ }
+
+ this.getID = function test4_getID() {
+ return "remove children, and then a parent of 2nd child";
+ }
+ }
+
+ /**
+ * Remove a child, remove a parent sibling, remove the parent
+ */
+ function test5()
+ {
+ this.o = getAccessible("t5_o");
+ this.ofc = this.o.firstChild;
+ this.b = getAccessible("t5_b");
+ this.lb = getAccessible("t5_lb");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, this.b),
+ new invokerChecker(EVENT_HIDE, this.o),
+ new invokerChecker(EVENT_REORDER, "t5"),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.ofc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.o),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.lb)
+ ];
+
+ this.invoke = function test5_invoke()
+ {
+ getNode("t5_o").textContent = "";
+ getNode("t5").removeChild(getNode("t5_b"));
+ getNode("t5_lb").removeChild(getNode("t5_o"));
+ }
+
+ this.finalCheck = function test5_finalCheck()
+ {
+ testIsDefunct(this.ofc);
+ testIsDefunct(this.o);
+ testIsDefunct(this.b);
+ }
+
+ this.getID = function test5_getID() {
+ return "remove a child, remove a parent sibling, remove the parent";
+ }
+ }
+
+ /**
+ * Insert accessibles with a child node moved by aria-owns
+ * Markup:
+ * <div id="t6_fc">
+ * <div id="t6_owns"></div>
+ * </div>
+ * <div id="t6_sc" aria-owns="t6_owns"></div>
+ */
+ function test6()
+ {
+ this.parent = getNode("t6");
+ this.fc = document.createElement("div");
+ this.fc.setAttribute("id", "t6_fc");
+ this.owns = document.createElement("div");
+ this.owns.setAttribute("id", "t6_owns");
+ this.sc = document.createElement("div");
+ this.sc.setAttribute("id", "t6_sc");
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.fc),
+ new invokerChecker(EVENT_SHOW, this.sc),
+ new invokerChecker(EVENT_REORDER, this.parent),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.fc),
+ new unexpectedInvokerChecker(EVENT_REORDER, this.sc),
+ new unexpectedInvokerChecker(EVENT_HIDE, this.owns),
+ new unexpectedInvokerChecker(EVENT_SHOW, this.owns)
+ ];
+
+ this.invoke = function test6_invoke()
+ {
+ getNode("t6").appendChild(this.fc);
+ getNode("t6_fc").appendChild(this.owns);
+ getNode("t6").appendChild(this.sc);
+ getNode("t6_sc").setAttribute("aria-owns", "t6_owns");
+ };
+
+ this.getID = function test6_getID() {
+ return "Insert accessibles with a child node moved by aria-owns";
+ };
+ }
+
+ /**
+ * Insert text nodes under direct and grand children, and then hide
+ * their container by means of aria-owns.
+ *
+ * Markup:
+ * <div id="t7_moveplace" aria-owns="t7_c"></div>
+ * <div id="t7_c">
+ * <div id="t7_c_directchild">ha</div>
+ * <div><div id="t7_c_grandchild">ha</div></div>
+ * </div>
+ */
+ function test7()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t7_c')),
+ new invokerChecker(EVENT_SHOW, getNode('t7_c')),
+ new invokerChecker(EVENT_REORDER, getNode('t7')),
+ new unexpectedInvokerChecker(EVENT_REORDER, getNode('t7_c_directchild')),
+ new unexpectedInvokerChecker(EVENT_REORDER, getNode('t7_c_grandchild')),
+ new unexpectedInvokerChecker(EVENT_SHOW, () => getNode('t7_c_directchild').firstChild),
+ new unexpectedInvokerChecker(EVENT_SHOW, () => getNode('t7_c_grandchild').firstChild)
+ ];
+
+ this.invoke = function test7_invoke()
+ {
+ getNode('t7_c_directchild').textContent = 'ha';
+ getNode('t7_c_grandchild').textContent = 'ha';
+ getNode('t7_moveplace').setAttribute('aria-owns', 't7_c');
+ };
+
+ this.getID = function test7_getID() {
+ return "Show child accessibles and then hide their container";
+ };
+ }
+
+ /**
+ * Move a node by aria-owns from right to left in the tree, so that
+ * the eventing looks this way:
+ * reorder for 't8_c1'
+ * hide for 't8_c1_child'
+ * show for 't8_c2_moved'
+ * reorder for 't8_c2'
+ * hide for 't8_c2_moved'
+ *
+ * The hide event should be delivered before the paired show event.
+ */
+ function test8()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t8_c1_child')),
+ new invokerChecker(EVENT_HIDE, 't8_c2_moved'),
+ new invokerChecker(EVENT_SHOW, 't8_c2_moved'),
+ new invokerChecker(EVENT_REORDER, 't8_c2'),
+ new invokerChecker(EVENT_REORDER, 't8_c1'),
+ ];
+
+ this.invoke = function test8_invoke()
+ {
+ // Remove a node from 't8_c1' container to give the event tree a
+ // desired structure (the 't8_c1' container node goes first in the event
+ // tree)
+ getNode('t8_c1_child').remove();
+ // then move 't8_c2_moved' from 't8_c2' to 't8_c1'.
+ getNode('t8_c1').setAttribute('aria-owns', 't8_c2_moved');
+ };
+
+ this.getID = function test8_getID() {
+ return "Move a node by aria-owns to left within the tree";
+ };
+ }
+
+ /**
+ * Move 't9_c3_moved' node under 't9_c2_moved', and then move 't9_c2_moved'
+ * node by aria-owns (same as test10 but has different aria-owns
+ * ordering), the eventing looks same way as in test10:
+ * reorder for 't9_c1'
+ * hide for 't9_c1_child'
+ * show for 't9_c2_moved'
+ * reorder for 't9_c2'
+ * hide for 't9_c2_child'
+ * hide for 't9_c2_moved'
+ * reorder for 't9_c3'
+ * hide for 't9_c3_moved'
+ *
+ * The hide events for 't9_c2_moved' and 't9_c3_moved' should be delivered
+ * before the show event for 't9_c2_moved'.
+ */
+ function test9()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t9_c1_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t9_c2_child')),
+ new invokerChecker(EVENT_HIDE, 't9_c3_moved'),
+ new invokerChecker(EVENT_HIDE, 't9_c2_moved'),
+ new invokerChecker(EVENT_SHOW, 't9_c2_moved'),
+ new invokerChecker(EVENT_REORDER, 't9_c3'),
+ new invokerChecker(EVENT_REORDER, 't9_c2'),
+ new invokerChecker(EVENT_REORDER, 't9_c1'),
+ new unexpectedInvokerChecker(EVENT_SHOW, 't9_c3_moved')
+ ];
+
+ this.invoke = function test9_invoke()
+ {
+ // Remove child nodes from 't9_c1' and 't9_c2' containers to give
+ // the event tree a needed structure ('t9_c1' and 't9_c2' nodes go
+ // first in the event tree),
+ getNode('t9_c1_child').remove();
+ getNode('t9_c2_child').remove();
+ // then do aria-owns magic.
+ getNode('t9_c2_moved').setAttribute('aria-owns', 't9_c3_moved');
+ getNode('t9_c1').setAttribute('aria-owns', 't9_c2_moved');
+ };
+
+ this.getID = function test9_getID() {
+ return "Move node #1 by aria-owns and then move node #2 into node #1";
+ };
+ }
+
+ /**
+ * Move a node 't10_c3_moved' by aria-owns under a node 't10_c2_moved',
+ * moved by under 't10_1', so that the eventing looks this way:
+ * reorder for 't10_c1'
+ * hide for 't10_c1_child'
+ * show for 't10_c2_moved'
+ * reorder for 't10_c2'
+ * hide for 't10_c2_child'
+ * hide for 't10_c2_moved'
+ * reorder for 't10_c3'
+ * hide for 't10_c3_moved'
+ *
+ * The hide events for 't10_c2_moved' and 't10_c3_moved' should be delivered
+ * before the show event for 't10_c2_moved'.
+ */
+ function test10()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t10_c1_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t10_c2_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t10_c2_moved')),
+ new invokerChecker(EVENT_HIDE, getNode('t10_c3_moved')),
+ new invokerChecker(EVENT_SHOW, getNode('t10_c2_moved')),
+ new invokerChecker(EVENT_REORDER, 't10_c2'),
+ new invokerChecker(EVENT_REORDER, 't10_c1'),
+ new invokerChecker(EVENT_REORDER, 't10_c3')
+ ];
+
+ this.invoke = function test10_invoke()
+ {
+ // Remove child nodes from 't10_c1' and 't10_c2' containers to give
+ // the event tree a needed structure ('t10_c1' and 't10_c2' nodes go first
+ // in the event tree),
+ getNode('t10_c1_child').remove();
+ getNode('t10_c2_child').remove();
+ // then do aria-owns stuff.
+ getNode('t10_c1').setAttribute('aria-owns', 't10_c2_moved');
+ getNode('t10_c2_moved').setAttribute('aria-owns', 't10_c3_moved');
+ };
+
+ this.getID = function test10_getID() {
+ return "Move a node by aria-owns into a node moved by aria-owns to left within the tree";
+ };
+ }
+
+ /**
+ * Move a node by aria-owns from right to left in the tree, and then
+ * move its parent too by aria-owns. No hide event should be fired for
+ * original node.
+ */
+ function test11()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode('t11_c1_child')),
+ new invokerChecker(EVENT_HIDE, getNode('t11_c2')),
+ new orderChecker(),
+ new asyncInvokerChecker(EVENT_SHOW, 't11_c2_child'),
+ new asyncInvokerChecker(EVENT_SHOW, 't11_c2'),
+ new orderChecker(),
+ new invokerChecker(EVENT_REORDER, 't11'),
+ new unexpectedInvokerChecker(EVENT_HIDE, 't11_c2_child'),
+ new unexpectedInvokerChecker(EVENT_REORDER, 't11_c1'),
+ new unexpectedInvokerChecker(EVENT_REORDER, 't11_c2'),
+ new unexpectedInvokerChecker(EVENT_REORDER, 't11_c3')
+ ];
+
+ this.invoke = function test11_invoke()
+ {
+ // Remove a node from 't11_c1' container to give the event tree a
+ // desired structure (the 't11_c1' container node goes first in
+ // the event tree),
+ getNode('t11_c1_child').remove();
+ // then move 't11_c2_moved' from 't11_c2' to 't11_c1', and then move
+ // 't11_c2' to 't11_c3'.
+ getNode('t11_c1').setAttribute('aria-owns', 't11_c2_child');
+ getNode('t11_c3').setAttribute('aria-owns', 't11_c2');
+ };
+
+ this.getID = function test11_getID() {
+ return "Move a node by aria-owns to left within the tree";
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests.
+
+ gA11yEventDumpToConsole = true; // debug stuff
+ //enableLogging("eventTree");
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new removeChildNParent("option1", "select1"));
+ gQueue.push(new removeParentNChild("option2", "select2"));
+ gQueue.push(new hideChildNParent("option3", "select3"));
+ gQueue.push(new hideParentNChild("option4", "select4"));
+ gQueue.push(new hideChildNRemoveParent("option5", "select5"));
+ gQueue.push(new hideParentNRemoveChild("option6", "select6"));
+ gQueue.push(new removeChildNHideParent("option7", "select7"));
+ gQueue.push(new removeParentNHideChild("option8", "select8"));
+
+ gQueue.push(new addParentNChild("testContainer", false));
+ gQueue.push(new addParentNChild("testContainer", true));
+ gQueue.push(new showParentNChild("select9", "option9", false));
+ gQueue.push(new showParentNChild("select10", "option10", true));
+ gQueue.push(new showParentNAddChild("select11", false));
+ gQueue.push(new showParentNAddChild("select12", true));
+
+ gQueue.push(new removeGrandChildrenNHideParent("t1_child1", "t1_child2", "t1_parent"));
+ gQueue.push(new test3());
+ gQueue.push(new test4());
+ gQueue.push(new test5());
+ gQueue.push(new test6());
+ gQueue.push(new test7());
+ gQueue.push(new test8());
+ gQueue.push(new test9());
+ gQueue.push(new test10());
+ gQueue.push(new test11());
+
+ 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=513213"
+ title="coalesce events when new event is appended to the queue">
+ Mozilla Bug 513213
+ </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>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="testContainer">
+ <select id="select1">
+ <option id="option1">option</option>
+ </select>
+ <select id="select2">
+ <option id="option2">option</option>
+ </select>
+ <select id="select3">
+ <option id="option3">option</option>
+ </select>
+ <select id="select4">
+ <option id="option4">option</option>
+ </select>
+ <select id="select5">
+ <option id="option5">option</option>
+ </select>
+ <select id="select6">
+ <option id="option6">option</option>
+ </select>
+ <select id="select7">
+ <option id="option7">option</option>
+ </select>
+ <select id="select8">
+ <option id="option8">option</option>
+ </select>
+
+ <select id="select9" style="display: none">
+ <option id="option9" style="display: none">testing</option>
+ </select>
+ <select id="select10" style="display: none">
+ <option id="option10" style="display: none">testing</option>
+ </select>
+ <select id="select11" style="display: none"></select>
+ <select id="select12" style="display: none"></select>
+ </div>
+
+ <div id="testContainer2">
+ <div id="t1_parent">
+ <div id="t1_mid1"><div id="t1_child1"></div></div>
+ <div id="t1_mid2"><div id="t1_child2"></div></div>
+ </div>
+ </div>
+
+ <div id="t3">
+ <div role="listbox" id="t3_lb">
+ <div role="option" id="t3_o">opt</div>
+ </div>
+ </div>
+
+ <div id="t4">
+ <div role="listbox" id="t4_lb">
+ <div role="option" id="t4_o1">opt1</div>
+ <div role="option" id="t4_o2">opt2</div>
+ </div>
+ </div>
+
+ <div id="t5">
+ <div role="button" id="t5_b">btn</div>
+ <div role="listbox" id="t5_lb">
+ <div role="option" id="t5_o">opt</div>
+ </div>
+ </div>
+
+ <div id="t6">
+ </div>
+
+ <div id="t7">
+ <div id="t7_moveplace"></div>
+ <div id="t7_c">
+ <div><div id="t7_c_grandchild"></div></div>
+ <div id="t7_c_directchild"></div>
+ </div>
+ </div>
+
+ <div id="t8">
+ <div id="t8_c1"><div id="t8_c1_child"></div></div>
+ <div id="t8_c2">
+ <div id="t8_c2_moved"></div>
+ </div>
+ </div>
+
+ <div id="t9">
+ <div id="t9_c1"><div id="t9_c1_child"></div></div>
+ <div id="t9_c2">
+ <div id="t9_c2_child"></div>
+ <div id="t9_c2_moved"></div>
+ </div>
+ <div id="t9_c3">
+ <div id="t9_c3_moved"></div>
+ </div>
+ </div>
+
+ <div id="t10">
+ <div id="t10_c1"><div id="t10_c1_child"></div></div>
+ <div id="t10_c2">
+ <div id="t10_c2_child"></div>
+ <div id="t10_c2_moved"></div>
+ </div>
+ <div id="t10_c3">
+ <div id="t10_c3_moved"></div>
+ </div>
+ </div>
+
+ <div id="t11">
+ <div id="t11_c1"><div id="t11_c1_child"></div></div>
+ <div id="t11_c2"><div id="t11_c2_child"></div></div>
+ <div id="t11_c3"></div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_contextmenu.html b/accessible/tests/mochitest/events/test_contextmenu.html
new file mode 100644
index 000000000..065e1b50e
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_contextmenu.html
@@ -0,0 +1,139 @@
+<html>
+
+<head>
+ <title>Context menu tests</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function showContextMenu(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_START, getContextMenuNode()),
+ ];
+
+ this.invoke = function showContextMenu_invoke()
+ {
+ synthesizeMouse(this.DOMNode, 4, 4, { type: "contextmenu", button: 2 });
+ }
+
+ this.getID = function showContextMenu_getID()
+ {
+ return "show context menu";
+ }
+ }
+
+ function selectMenuItem()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getFocusedMenuItem)
+ ];
+
+ this.invoke = function selectMenuItem_invoke()
+ {
+ synthesizeKey("VK_DOWN", { });
+ }
+
+ this.getID = function selectMenuItem_getID()
+ {
+ return "select first menuitem";
+ }
+ }
+
+ function closeContextMenu(aID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_END,
+ getAccessible(getContextMenuNode()))
+ ];
+
+ this.invoke = function closeContextMenu_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function closeContextMenu_getID()
+ {
+ return "close context menu";
+ }
+ }
+
+ function getContextMenuNode()
+ {
+ return getRootAccessible().DOMDocument.
+ getElementById("contentAreaContextMenu");
+ }
+
+ function getFocusedMenuItem()
+ {
+ var menu = getAccessible(getAccessible(getContextMenuNode()));
+ for (var idx = 0; idx < menu.childCount; idx++) {
+ var item = menu.getChildAt(idx);
+
+ if (hasState(item, STATE_FOCUSED))
+ return getAccessible(item);
+ }
+ return null;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new showContextMenu("input"));
+ gQueue.push(new selectMenuItem());
+ gQueue.push(new closeContextMenu());
+
+ 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=580535"
+ title="Broken accessibility in context menus">
+ Mozilla Bug 580535
+ </a><br>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_descrchange.html b/accessible/tests/mochitest/events/test_descrchange.html
new file mode 100644
index 000000000..d51318532
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_descrchange.html
@@ -0,0 +1,85 @@
+<html>
+
+<head>
+ <title>Accessible description change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function setAttr(aID, aAttr, aValue, aChecker)
+ {
+ this.eventSeq = [ aChecker ];
+ this.invoke = function setAttr_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function setAttr_getID()
+ {
+ return "set attr '" + aAttr + "', value '" + aValue + "'";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setAttr("tst1", "aria-describedby", "display",
+ new invokerChecker(EVENT_DESCRIPTION_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "title", "title",
+ new unexpectedInvokerChecker(EVENT_DESCRIPTION_CHANGE, "tst1")));
+
+ gQueue.push(new setAttr("tst2", "title", "title",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst2")));
+
+ 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=991969"
+ title="Event not fired when description changes">
+ Bug 991969
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <button id="tst1">btn1</button>
+ <button id="tst2">btn2</button>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_docload.html b/accessible/tests/mochitest/events/test_docload.html
new file mode 100644
index 000000000..a530592ee
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_docload.html
@@ -0,0 +1,360 @@
+<html>
+
+<head>
+ <title>Accessible events testing for document</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ 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">
+ // Front end stuff sometimes likes to stuff things in the hidden window(s)
+ // in which case there's accessibles for that content.
+ Components.utils.import("resource://gre/modules/Services.jsm");
+
+ // Force the creation of an accessible for the hidden window's document.
+ var doc = Services.appShell.hiddenDOMWindow.document;
+ gAccService.getAccessibleFor(doc);
+
+ // The private hidden window will be lazily created that's why we need to do
+ // it here *before* loading '../events.js' or else we'll have a duplicate
+ // reorder event.
+ var privateDoc = Services.appShell.hiddenPrivateDOMWindow.document;
+
+ // Force the creation of an accessible for the private hidden window's doc.
+ gAccService.getAccessibleFor(privateDoc);
+ </script>
+
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function changeIframeSrc(aIdentifier, aURL)
+ {
+ this.DOMNode = getNode(aIdentifier);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, getAccessible(this.DOMNode)),
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getIframeDoc)
+ ];
+
+ this.invoke = function changeIframeSrc_invoke()
+ {
+ this.DOMNode.src = aURL;
+ }
+
+ this.finalCheck = function changeIframeSrc_finalCheck()
+ {
+ var accTree = {
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ {
+ role: ROLE_DOCUMENT,
+ name: aURL == "about:" ? "About:" : aURL
+ }
+ ]
+ };
+
+ testAccessibleTree(this.DOMNode, accTree);
+ }
+
+ this.getID = function changeIframeSrc_getID()
+ {
+ return "change iframe src on " + aURL;
+ }
+
+ function getIframeDoc()
+ {
+ return getAccessible(getNode(aIdentifier).contentDocument);
+ }
+ }
+
+ const kHide = 1;
+ const kShow = 2;
+ const kRemove = 3;
+ function morphIFrame(aIdentifier, aAction)
+ {
+ this.DOMNode = getNode(aIdentifier);
+ this.IFrameContainerDOMNode = this.DOMNode.parentNode;
+
+ this.eventSeq = [];
+
+ var checker = null;
+ if (aAction == kShow)
+ checker = new invokerChecker(EVENT_SHOW, this.DOMNode);
+ else
+ checker = new invokerChecker(EVENT_HIDE, this.DOMNode);
+ this.eventSeq.push(checker);
+
+ var reorderChecker =
+ new invokerChecker(EVENT_REORDER, this.IFrameContainerDOMNode);
+ this.eventSeq.push(reorderChecker);
+
+ this.invoke = function morphIFrame_invoke()
+ {
+ if (aAction == kHide) {
+ this.DOMNode.style.display = "none";
+ } else if (aAction == kShow) {
+ this.DOMNode.style.display = "block";
+ } else {
+ this.IFrameContainerDOMNode.removeChild(this.DOMNode);
+ }
+ }
+
+ this.finalCheck = function morphIFrame_finalCheck()
+ {
+ var accTree = {
+ role: ROLE_SECTION,
+ children: (aAction == kHide || aAction == kRemove) ? [ ] :
+ [
+ {
+ role: ROLE_INTERNAL_FRAME,
+ children: [
+ { role: ROLE_DOCUMENT }
+ ]
+ }
+ ]
+ };
+
+ testAccessibleTree(this.IFrameContainerDOMNode, accTree);
+ }
+
+ this.getID = function morphIFrame_getID()
+ {
+ if (aAction == kRemove)
+ return "remove iframe";
+
+ return "change display style of iframe to " +
+ ((aAction == kHide) ? "none" : "block");
+ }
+ }
+
+ function makeIFrameVisible(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.DOMNode.parentNode)
+ ];
+
+ this.invoke = function makeIFrameVisible_invoke()
+ {
+ this.DOMNode.style.visibility = "visible";
+ }
+
+ this.getID = function makeIFrameVisible_getID()
+ {
+ return "The accessible for DOM document loaded before it's shown shouldn't have busy state.";
+ }
+ }
+
+ function openDialogWnd(aURL)
+ {
+ // Get application root accessible.
+ var docAcc = getAccessible(document);
+ while (docAcc) {
+ this.mRootAcc = docAcc;
+ try {
+ docAcc = docAcc.parent;
+ } catch (e) {
+ ok(false, "Can't get parent for " + prettyName(docAcc));
+ throw e;
+ }
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.mRootAcc)
+ ];
+
+ this.invoke = function openDialogWnd_invoke()
+ {
+ this.mDialog = window.openDialog(aURL);
+ }
+
+ this.finalCheck = function openDialogWnd_finalCheck()
+ {
+ this.finalCheckImpl();
+ }
+
+ this.finalCheckImpl = function openDialogWnd_finalCheckImpl()
+ {
+ var accTree = {
+ role: ROLE_APP_ROOT,
+ children: [
+ {
+ role: ROLE_CHROME_WINDOW
+ },
+ {
+ role: ROLE_CHROME_WINDOW
+ },
+ {
+ role: ROLE_CHROME_WINDOW
+ },
+ {
+ role: ROLE_CHROME_WINDOW
+ }
+ ]
+ };
+
+ testAccessibleTree(this.mRootAcc, accTree);
+
+ var dlgDoc = this.mDialog.document;
+ ok(isAccessibleInCache(dlgDoc),
+ "The document accessible for '" + aURL + "' is not in cache!");
+
+ this.mDialog.close();
+
+ // close() is asynchronous.
+ SimpleTest.executeSoon(function() {
+ ok(!isAccessibleInCache(dlgDoc),
+ "The document accessible for '" + aURL + "' is in cache still!");
+ });
+ }
+
+ this.getID = function openDialogWnd_getID()
+ {
+ return "open dialog '" + aURL + "'";
+ }
+ }
+
+ function openWndShutdownDoc()
+ {
+ this.__proto__ =
+ new openDialogWnd("../events/docload_wnd.html");
+
+ var thisObj = this;
+ var docChecker = {
+ type: EVENT_HIDE,
+ get target()
+ {
+ var iframe = this.invoker.mDialog.document.getElementById("iframe");
+ this.invoker.iframeDoc = iframe.contentDocument;
+ return iframe;
+ },
+ get targetDescr()
+ {
+ return "inner iframe of docload_wnd.html document";
+ },
+ invoker: thisObj
+ };
+
+ this.eventSeq.push(docChecker);
+
+ this.finalCheck = function openWndShutdownDoc_finalCheck()
+ {
+ // After timeout after event hide for iframe was handled the document
+ // accessible for iframe's document is in cache still.
+ ok(!isAccessibleInCache(this.iframeDoc),
+ "The document accessible for iframe is in cache still after iframe hide!");
+
+ this.finalCheckImpl();
+
+ // After the window is closed all alive subdocument accessibles should
+ // be shut down.
+ ok(!isAccessibleInCache(this.iframeDoc),
+ "The document accessible for iframe is in cache still!");
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ // Debug stuff.
+ // gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeIframeSrc("iframe", "about:"));
+ gQueue.push(new changeIframeSrc("iframe", "about:buildconfig"));
+ gQueue.push(new morphIFrame("iframe", kHide));
+ gQueue.push(new morphIFrame("iframe", kShow));
+ gQueue.push(new morphIFrame("iframe", kRemove));
+ gQueue.push(new makeIFrameVisible("iframe2"));
+ gQueue.push(new openDialogWnd("about:"));
+ gQueue.push(new openWndShutdownDoc());
+
+ gQueue.onFinish = doLastCallTests;
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ function doLastCallTests()
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // makeIFrameVisible() test, part2
+
+ // The document shouldn't have busy state (the DOM document was loaded
+ // before its accessible was created). Do this test lately to make sure
+ // the content of document accessible was created initially, prior to this
+ // the document accessible keeps busy state. The initial creation happens
+ // asynchronously after document creation, there are no events we could
+ // use to catch it.
+ var iframeDoc = getAccessible("iframe2").firstChild;
+ testStates(iframeDoc, 0, 0, STATE_BUSY);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=420845"
+ title="Fire event_reorder on any embedded frames/iframes whos document has just loaded">
+ Mozilla Bug 420845
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=506206"
+ title="Fire event_reorder application root accessible">
+ Mozilla Bug 506206
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=566103"
+ title="Reorganize accessible document handling">
+ Mozilla Bug 566103
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=571459"
+ title="Shutdown document accessible when presshell goes away">
+ Mozilla Bug 571459
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=658185"
+ title="The DOM document loaded before it's shown shouldn't have busy state">
+ Mozilla Bug 658185
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
+ title="Fire document load events on iframes too">
+ Mozilla Bug 754165
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="testContainer"><iframe id="iframe"></iframe></div>
+ <div id="testContainer2"><iframe id="iframe2" src="about:" style="visibility: hidden;"></iframe></div>
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_docload.xul b/accessible/tests/mochitest/events/test_docload.xul
new file mode 100644
index 000000000..4b07b0e72
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_docload.xul
@@ -0,0 +1,243 @@
+<?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="Accessibility Loading Document Events Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker checkers.
+ function stateBusyChecker(aIsEnabled)
+ {
+ this.type = EVENT_STATE_CHANGE;
+ this.__defineGetter__("target", currentTabDocument);
+
+ this.check = function stateBusyChecker_check(aEvent)
+ {
+ var event = null;
+ try {
+ var event = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
+ } catch (e) {
+ ok(false, "State change event was expected");
+ }
+
+ if (!event)
+ return;
+
+ is(event.state, STATE_BUSY, "Wrong state of statechange event.");
+ is(event.isEnabled, aIsEnabled,
+ "Wrong value of state of statechange event");
+
+ testStates(event.accessible, (aIsEnabled ? STATE_BUSY : 0), 0,
+ (aIsEnabled ? 0 : STATE_BUSY), 0);
+ }
+ }
+
+ function documentReloadChecker(aIsFromUserInput)
+ {
+ this.type = EVENT_DOCUMENT_RELOAD;
+ this.__defineGetter__("target", currentTabDocument);
+
+ this.check = function documentReloadChecker_check(aEvent)
+ {
+ is(aEvent.isFromUserInput, aIsFromUserInput,
+ "Wrong value of isFromUserInput");
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers.
+
+ /**
+ * Load URI.
+ */
+ function loadURIInvoker(aURI)
+ {
+ this.invoke = function loadURIInvoker_invoke()
+ {
+ tabBrowser().loadURI(aURI);
+ }
+
+ this.eventSeq = [
+ // We don't expect state change event for busy true since things happen
+ // quickly and it's coalesced.
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function loadURIInvoker_getID()
+ {
+ return "load uri " + aURI;
+ }
+ }
+
+ /**
+ * Load the document having sub document. No document loading events for
+ * nested document.
+ */
+ function loadNestedDocURIInvoker(aNestedDocURI)
+ {
+ this.__proto__ = new loadURIInvoker(aNestedDocURI);
+
+ // Remove reorder event checker since the event is likely coalesced by
+ // reorder event on Firefox UI (refer to bug 759670 for details).
+ this.eventSeq.shift();
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getNestedDoc),
+ new invokerChecker(EVENT_STATE_CHANGE, getNestedDoc)
+ ];
+
+ function getNestedDoc()
+ {
+ var iframeNodes = currentTabDocument().getElementsByTagName("iframe");
+ return iframeNodes && iframeNodes.length > 0 ?
+ iframeNodes[0].firstChild : null;
+ }
+ }
+
+ /**
+ * Reload the page by F5 (isFromUserInput flag is true).
+ */
+ function userReloadInvoker()
+ {
+ this.invoke = function userReloadInvoker_invoke()
+ {
+ synthesizeKey("VK_F5", {}, browserWindow());
+ }
+
+ this.eventSeq = [
+ new documentReloadChecker(true),
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function userReloadInvoker_getID()
+ {
+ return "user reload page";
+ }
+ }
+
+ /**
+ * Reload the page (isFromUserInput flag is false).
+ */
+ function reloadInvoker()
+ {
+ this.invoke = function reloadInvoker_invoke()
+ {
+ tabBrowser().reload();
+ }
+
+ this.eventSeq = [
+ new documentReloadChecker(false),
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function reloadInvoker_getID()
+ {
+ return "reload page";
+ }
+ }
+
+ /**
+ * Load wrong URI what results in error page loading.
+ */
+ function loadErrorPageInvoker(aURL, aURLDescr)
+ {
+ this.invoke = function loadErrorPageInvoker_invoke()
+ {
+ tabBrowser().loadURI(aURL);
+ }
+
+ this.eventSeq = [
+ // We don't expect state change for busy true, load stopped events since
+ // things happen quickly and it's coalesced.
+ new asyncInvokerChecker(EVENT_REORDER, currentBrowser),
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new stateBusyChecker(false)
+ ];
+
+ this.getID = function loadErrorPageInvoker_getID()
+ {
+ return "load error page: '" + aURLDescr + "'";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpToConsole = true; // debug
+ //gA11yEventDumpFeature = "parentchain:reorder";
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ var dataURL =
+ "data:text/html,<html><body><iframe src='http://example.com'></iframe></body></html>";
+ gQueue.push(new loadNestedDocURIInvoker(dataURL));
+
+ gQueue.push(new loadURIInvoker("about:"));
+ gQueue.push(new userReloadInvoker());
+ gQueue.push(new loadURIInvoker("about:mozilla"));
+ gQueue.push(new reloadInvoker());
+ gQueue.push(new loadErrorPageInvoker("www.wronguri.wronguri",
+ "Server not found"));
+ gQueue.push(new loadErrorPageInvoker("https://nocert.example.com:443",
+ "Untrusted Connection"));
+
+ gQueue.onFinish = function() { closeBrowserWindow(); }
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests);
+ ]]>
+ </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=566103"
+ title=" reorganize accessible document handling">
+ Mozilla Bug 566103
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=754165"
+ title="Fire document load events on iframes too">
+ Mozilla Bug 754165
+ </a>
+ <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/events/test_docload_aria.html b/accessible/tests/mochitest/events/test_docload_aria.html
new file mode 100644
index 000000000..c5f470aee
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_docload_aria.html
@@ -0,0 +1,83 @@
+<html>
+
+<head>
+ <title>Accessible events testing for ARIA document</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ 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
+
+ function showARIADialog(aID)
+ {
+ this.dialogNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, this.dialogNode)
+ ];
+
+ this.invoke = function showARIADialog_invoke()
+ {
+ this.dialogNode.style.display = "block";
+ }
+
+ this.getID = function showARIADialog_getID()
+ {
+ return "show ARIA dialog";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ // Debug stuff.
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new showARIADialog("dialog"));
+ gQueue.push(new showARIADialog("document"));
+
+ 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=759833"
+ title="ARIA documents should fire document loading events">
+ Mozilla Bug 759833
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="dialog" id="dialog" style="display: none;">It's a dialog</div>
+ <div role="document" id="document" style="display: none;">It's a document</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_dragndrop.html b/accessible/tests/mochitest/events/test_dragndrop.html
new file mode 100644
index 000000000..cfba80a46
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_dragndrop.html
@@ -0,0 +1,110 @@
+<html>
+
+<head>
+ <title>Accessible drag and drop event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // aria grabbed invoker
+ function changeGrabbed(aNodeOrID, aGrabValue)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+
+ this.invoke = function changeGrabbed_invoke() {
+ if (aGrabValue != undefined) {
+ this.DOMNode.setAttribute("aria-grabbed", aGrabValue);
+ }
+ }
+
+ this.check = function changeGrabbed_check() {
+ testAttrs(aNodeOrID, {"grabbed" : aGrabValue}, true);
+ }
+
+ this.getID = function changeGrabbed_getID() {
+ return prettyName(aNodeOrID) + " aria-grabbed changed";
+ }
+ }
+
+ // aria dropeffect invoker
+ function changeDropeffect(aNodeOrID, aDropeffectValue)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+
+ this.invoke = function changeDropeffect_invoke() {
+ if (aDropeffectValue != undefined) {
+ this.DOMNode.setAttribute("aria-dropeffect", aDropeffectValue);
+ }
+ }
+
+ this.check = function changeDropeffect_check() {
+ testAttrs(aNodeOrID, {"dropeffect" : aDropeffectValue}, true);
+ }
+
+ this.getID = function changeDropeffect_getID() {
+ return prettyName(aNodeOrID) + " aria-dropeffect changed";
+ }
+ }
+
+ function doTests()
+ {
+ // Test aria attribute mutation events
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_OBJECT_ATTRIBUTE_CHANGED);
+
+ var id="grabbable";
+ gQueue.push(new changeGrabbed(id, "true"));
+ gQueue.push(new changeGrabbed(id, "false"));
+ todo(false, "uncomment this test when 472142 is fixed.");
+ //gQueue.push(new changeGrabbed(id, "undefined"));
+
+ var id="dropregion";
+ gQueue.push(new changeDropeffect(id, "copy"));
+ gQueue.push(new changeDropeffect(id, "execute"));
+
+ 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=510441"
+ title="Add support for nsIAccessibleEvent::OBJECT_ATTRIBUTE_CHANGED">
+ Mozilla Bug 510441
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <!-- ARIA grabbed -->
+ <div id="grabbable" role="button" aria-grabbed="foo">button</div>
+
+ <!-- ARIA dropeffect -->
+ <div id="dropregion" role="region" aria-dropeffect="none">button</div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_flush.html b/accessible/tests/mochitest/events/test_flush.html
new file mode 100644
index 000000000..44a9afd94
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_flush.html
@@ -0,0 +1,77 @@
+<html>
+
+<head>
+ <title>Flush delayed events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+ SimpleTest.expectAssertions(0, 1);
+
+ var gFocusHandler = {
+ handleEvent: function(aEvent) {
+ switch (this.count) {
+ case 0:
+ is(aEvent.DOMNode, getNode("input1"),
+ "Focus event for input1 was expected!");
+ getAccessible("input2").takeFocus();
+ break;
+
+ case 1:
+ is(aEvent.DOMNode, getNode("input2"),
+ "Focus event for input2 was expected!");
+
+ unregisterA11yEventListener(EVENT_FOCUS, gFocusHandler);
+ SimpleTest.finish();
+ break;
+
+ default:
+ ok(false, "Wrong focus event!");
+ }
+
+ this.count++;
+ },
+
+ count: 0
+ };
+
+ function doTests()
+ {
+ registerA11yEventListener(EVENT_FOCUS, gFocusHandler);
+
+ getAccessible("input1").takeFocus();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=477551"
+ title="DocAccessible::FlushPendingEvents isn't robust">
+ Mozilla Bug 477551
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input1">
+ <input id="input2">
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
new file mode 100644
index 000000000..4cd57fe3b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=429547
+-->
+<head>
+ <title>aria-activedescendant focus tests</title>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ 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">
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function changeARIAActiveDescendant(aID, aItemID)
+ {
+ this.eventSeq = [
+ new focusChecker(aItemID)
+ ];
+
+ this.invoke = function changeARIAActiveDescendant_invoke()
+ {
+ getNode(aID).setAttribute("aria-activedescendant", aItemID);
+ }
+
+ this.getID = function changeARIAActiveDescendant_getID()
+ {
+ return "change aria-activedescendant on " + aItemID;
+ }
+ }
+
+ function insertItemNFocus(aID, aNewItemID)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, aNewItemID),
+ new focusChecker(aNewItemID)
+ ];
+
+ this.invoke = function insertItemNFocus_invoke()
+ {
+ var container = getNode(aID);
+ var itemNode = document.createElement("div");
+ itemNode.setAttribute("id", aNewItemID);
+ itemNode.textContent = aNewItemID;
+ container.appendChild(itemNode);
+
+ container.setAttribute("aria-activedescendant", aNewItemID);
+ }
+
+ this.getID = function insertItemNFocus_getID()
+ {
+ return "insert new node and focus it with ID: " + aNewItemID;
+ }
+ }
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("listbox", new focusChecker("item1")));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item2"));
+ gQueue.push(new changeARIAActiveDescendant("listbox", "item3"));
+
+ gQueue.push(new synthFocus("combobox_entry", new focusChecker("combobox_entry")));
+ gQueue.push(new changeARIAActiveDescendant("combobox", "combobox_option2"));
+
+ todo(false, "No focus for inserted element, bug 687011");
+ //gQueue.push(new insertItemNFocus("listbox", "item4"));
+
+ 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=429547"
+ title="Support aria-activedescendant usage in nsIAccesible::TakeFocus()">
+ Mozilla Bug 429547
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761102"
+ title="Focus may be missed when ARIA active-descendant is changed on active composite widget">
+ Mozilla Bug 761102
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="listbox" aria-activedescendant="item1" id="listbox" tabindex="1"
+ aria-owns="item3">
+ <div role="listitem" id="item1">item1</div>
+ <div role="listitem" id="item2">item2</div>
+ </div>
+ <div role="listitem" id="item3">item3</div>
+
+ <div role="combobox" id="combobox">
+ <input id="combobox_entry">
+ <ul>
+ <li role="option" id="combobox_option1">option1</li>
+ <li role="option" id="combobox_option2">option2</li>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_autocomplete.xul b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
new file mode 100644
index 000000000..2a32a6587
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -0,0 +1,518 @@
+<?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 searchbar -->
+<?xml-stylesheet href="chrome://browser/content/browser.css"
+ type="text/css"?>
+<!-- SeaMonkey searchbar -->
+<?xml-stylesheet href="chrome://navigator/content/navigator.css"
+ type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Accessible focus event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript"
+ src="../autocomplete.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Hacky stuffs
+
+ // This is the hacks needed to use a searchbar without browser.js.
+ function getBrowser()
+ {
+ return {
+ mCurrentBrowser: { engines: new Array() }
+ };
+ }
+
+ var BrowserSearch = {
+ updateOpenSearchBadge: function() {}
+ };
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function loadFormAutoComplete(aIFrameID)
+ {
+ this.iframeNode = getNode(aIFrameID);
+ this.iframe = getAccessible(aIFrameID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.iframe)
+ ];
+
+ this.invoke = function loadFormAutoComplete_invoke()
+ {
+ var url = "data:text/html,<html><body><form id='form'>" +
+ "<input id='input' name='a11ytest-formautocomplete'>" +
+ "</form></body></html>";
+ this.iframeNode.setAttribute("src", url);
+ }
+
+ this.getID = function loadFormAutoComplete_getID()
+ {
+ return "load form autocomplete page";
+ }
+ }
+
+ function initFormAutoCompleteBy(aIFrameID, aAutoCompleteValue)
+ {
+ this.iframe = getAccessible(aIFrameID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.iframe)
+ ];
+
+ this.invoke = function initFormAutoCompleteBy_invoke()
+ {
+ var iframeDOMDoc = getIFrameDOMDoc(aIFrameID);
+
+ var inputNode = iframeDOMDoc.getElementById("input");
+ inputNode.value = aAutoCompleteValue;
+ var formNode = iframeDOMDoc.getElementById("form");
+ formNode.submit();
+ }
+
+ this.getID = function initFormAutoCompleteBy_getID()
+ {
+ return "init form autocomplete by '" + aAutoCompleteValue + "'";
+ }
+ }
+
+ function loadHTML5ListAutoComplete(aIFrameID)
+ {
+ this.iframeNode = getNode(aIFrameID);
+ this.iframe = getAccessible(aIFrameID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, this.iframe)
+ ];
+
+ this.invoke = function loadHTML5ListAutoComplete_invoke()
+ {
+ var url = "data:text/html,<html><body>" +
+ "<datalist id='cities'><option>hello</option><option>hi</option></datalist>" +
+ "<input id='input' list='cities'>" +
+ "</body></html>";
+ this.iframeNode.setAttribute("src", url);
+ }
+
+ this.getID = function loadHTML5ListAutoComplete_getID()
+ {
+ return "load HTML5 list autocomplete page";
+ }
+ }
+
+ function removeChar(aID, aCheckerOrEventSeq)
+ {
+ this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
+
+ this.invoke = function removeChar_invoke()
+ {
+ synthesizeKey("VK_LEFT", { shiftKey: true });
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeChar_getID()
+ {
+ return "remove char on " + prettyName(aID);
+ }
+ }
+
+ function replaceOnChar(aID, aChar, aCheckerOrEventSeq)
+ {
+ this.__proto__ = new synthAction(aID, aCheckerOrEventSeq);
+
+ this.invoke = function replaceOnChar_invoke()
+ {
+ this.DOMNode.select();
+ synthesizeKey(aChar, {});
+ }
+
+ this.getID = function replaceOnChar_getID()
+ {
+ return "replace on char '" + aChar + "' for" + prettyName(aID);
+ }
+ }
+
+ function focusOnMouseOver(aIDFunc, aIDFuncArg)
+ {
+ this.eventSeq = [ new focusChecker(aIDFunc, aIDFuncArg) ];
+
+ this.invoke = function focusOnMouseOver_invoke()
+ {
+ this.id = aIDFunc.call(null, aIDFuncArg);
+ this.node = getNode(this.id);
+ this.window = this.node.ownerDocument.defaultView;
+
+ if (this.node.localName == "tree") {
+ var tree = getAccessible(this.node);
+ var accessible = getAccessible(this.id);
+ if (tree != accessible) {
+ var itemX = {}, itemY = {}, treeX = {}, treeY = {};
+ accessible.getBounds(itemX, itemY, {}, {});
+ tree.getBounds(treeX, treeY, {}, {});
+ this.x = itemX.value - treeX.value;
+ this.y = itemY.value - treeY.value;
+ }
+ }
+
+ // Generate mouse move events in timeouts until autocomplete popup list
+ // doesn't have it, the reason is do that because autocomplete popup
+ // ignores mousemove events firing in too short range.
+ synthesizeMouse(this.node, this.x, this.y, { type: "mousemove" });
+ this.doMouseMoveFlood(this);
+ }
+
+ this.finalCheck = function focusOnMouseOver_getID()
+ {
+ this.isFlooding = false;
+ }
+
+ this.getID = function focusOnMouseOver_getID()
+ {
+ return "mouse over on " + prettyName(aIDFunc.call(null, aIDFuncArg));
+ }
+
+ this.doMouseMoveFlood = function focusOnMouseOver_doMouseMoveFlood(aThis)
+ {
+ synthesizeMouse(aThis.node, aThis.x + 1, aThis.y + 1,
+ { type: "mousemove" }, aThis.window);
+
+ if (aThis.isFlooding)
+ aThis.window.setTimeout(aThis.doMouseMoveFlood, 0, aThis);
+ }
+
+ this.id = null;
+ this.node = null;
+ this.window = null;
+
+ this.isFlooding = true;
+ this.x = 1;
+ this.y = 1;
+ }
+
+ function selectByClick(aIDFunc, aIDFuncArg,
+ aFocusTargetFunc, aFocusTargetFuncArg)
+ {
+ this.eventSeq = [ new focusChecker(aFocusTargetFunc, aFocusTargetFuncArg) ];
+
+ this.invoke = function selectByClick_invoke()
+ {
+ var id = aIDFunc.call(null, aIDFuncArg);
+ var node = getNode(id);
+ var targetWindow = node.ownerDocument.defaultView;
+
+ var x = 0, y = 0;
+ if (node.localName == "tree") {
+ var tree = getAccessible(node);
+ var accessible = getAccessible(id);
+ if (tree != accessible) {
+ var itemX = {}, itemY = {}, treeX = {}, treeY = {};
+ accessible.getBounds(itemX, itemY, {}, {});
+ tree.getBounds(treeX, treeY, {}, {});
+ x = itemX.value - treeX.value;
+ y = itemY.value - treeY.value;
+ }
+ }
+
+ synthesizeMouseAtCenter(node, {}, targetWindow);
+ }
+
+ this.getID = function selectByClick_getID()
+ {
+ return "select by click " + prettyName(aIDFunc.call(null, aIDFuncArg));
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Target getters
+
+ function getItem(aItemObj)
+ {
+ var autocomplete = aItemObj.autocomplete;
+ var autocompleteNode = aItemObj.autocompleteNode;
+
+ // XUL searchbar
+ if (autocompleteNode.localName == "searchbar") {
+ var popupNode = autocompleteNode._popup;
+ if (popupNode) {
+ var list = getAccessible(popupNode);
+ return list.getChildAt(aItemObj.index);
+ }
+ }
+
+ // XUL autocomplete
+ var popupNode = autocompleteNode.popup;
+ if (!popupNode) {
+ // HTML form autocomplete
+ var controller = Components.classes["@mozilla.org/autocomplete/controller;1"].
+ getService(Components.interfaces.nsIAutoCompleteController);
+ popupNode = controller.input.popup.QueryInterface(nsIDOMNode);
+ }
+
+ if (popupNode) {
+ if ("richlistbox" in popupNode) {
+ var list = getAccessible(popupNode.richlistbox);
+ return list.getChildAt(aItemObj.index);
+ }
+
+ var list = getAccessible(popupNode.tree);
+ return list.getChildAt(aItemObj.index + 1);
+ }
+ }
+
+ function getTextEntry(aID)
+ {
+ // For form autocompletes the autocomplete widget and text entry widget
+ // is the same widget, for XUL autocompletes the text entry is a first
+ // child.
+ var localName = getNode(aID).localName;
+
+ // XUL autocomplete
+ if (localName == "textbox")
+ return getAccessible(aID).firstChild;
+
+ // HTML form autocomplete
+ if (localName == "input")
+ return getAccessible(aID);
+
+ // XUL searchbar
+ if (localName == "searchbar")
+ return getAccessible(getNode(aID).textbox.inputField);
+
+ return null;
+ }
+
+ function itemObj(aID, aIdx)
+ {
+ this.autocompleteNode = getNode(aID);
+
+ this.autocomplete = this.autocompleteNode.localName == "searchbar" ?
+ getAccessible(this.autocompleteNode.textbox) :
+ getAccessible(this.autocompleteNode);
+
+ this.index = aIdx;
+ }
+
+ function getIFrameDOMDoc(aIFrameID)
+ {
+ return getNode(aIFrameID).contentDocument;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test helpers
+
+ function queueAutoCompleteTests(aID)
+ {
+ // focus autocomplete text entry
+ gQueue.push(new synthFocus(aID, new focusChecker(getTextEntry, aID)));
+
+ // open autocomplete popup
+ gQueue.push(new synthDownKey(aID, new nofocusChecker()));
+
+ // select second option ('hi' option), focus on it
+ gQueue.push(new synthUpKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 1))));
+
+ // choose selected option, focus on text entry
+ gQueue.push(new synthEnterKey(aID, new focusChecker(getTextEntry, aID)));
+
+ // remove char, autocomplete popup appears
+ gQueue.push(new removeChar(aID, new nofocusChecker()));
+
+ // select first option ('hello' option), focus on it
+ gQueue.push(new synthDownKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 0))));
+
+ // mouse move on second option ('hi' option), focus on it
+ gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 1)));
+
+ // autocomplete popup updated (no selected item), focus on textentry
+ gQueue.push(new synthKey(aID, "e", null, new focusChecker(getTextEntry, aID)));
+
+ // select first option ('hello' option), focus on it
+ gQueue.push(new synthDownKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 0))));
+
+ // popup gets hidden, focus on textentry
+ gQueue.push(new synthRightKey(aID, new focusChecker(getTextEntry, aID)));
+
+ // popup gets open, no focus
+ gQueue.push(new synthOpenComboboxKey(aID, new nofocusChecker()));
+
+ // select first option again ('hello' option), focus on it
+ gQueue.push(new synthDownKey(aID,
+ new focusChecker(getItem, new itemObj(aID, 0))));
+
+ // no option is selected, focus on text entry
+ gQueue.push(new synthUpKey(aID, new focusChecker(getTextEntry, aID)));
+
+ // close popup, no focus
+ gQueue.push(new synthEscapeKey(aID, new nofocusChecker()));
+
+ // autocomplete popup appears (no selected item), focus stays on textentry
+ gQueue.push(new replaceOnChar(aID, "h", new nofocusChecker()));
+
+ // mouse move on first option ('hello' option), focus on it
+ gQueue.push(new focusOnMouseOver(getItem, new itemObj(aID, 0)));
+
+ // click first option ('hello' option), popup closes, focus on text entry
+ gQueue.push(new selectByClick(getItem, new itemObj(aID, 0), getTextEntry, aID));
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gInitQueue = null;
+ function initTests()
+ {
+ if (SEAMONKEY || MAC) {
+ todo(false, "Skipping this test on SeaMonkey ftb. (Bug 718237), and on Mac (bug 746177)");
+ shutdownAutoComplete();
+ SimpleTest.finish();
+ return;
+ }
+
+ gInitQueue = new eventQueue();
+ gInitQueue.push(new loadFormAutoComplete("iframe"));
+ gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
+ gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
+ gInitQueue.push(new loadHTML5ListAutoComplete("iframe2"));
+ gInitQueue.onFinish = function initQueue_onFinish()
+ {
+ SimpleTest.executeSoon(doTests);
+ return DO_NOT_FINISH_TEST;
+ }
+ gInitQueue.invoke();
+ }
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ ////////////////////////////////////////////////////////////////////////////
+ // tree popup autocomplete tests
+ queueAutoCompleteTests("autocomplete");
+
+ ////////////////////////////////////////////////////////////////////////////
+ // richlistbox popup autocomplete tests
+ queueAutoCompleteTests("richautocomplete");
+
+ ////////////////////////////////////////////////////////////////////////////
+ // HTML form autocomplete tests
+ queueAutoCompleteTests(getIFrameDOMDoc("iframe").getElementById("input"));
+
+ ////////////////////////////////////////////////////////////////////////////
+ // HTML5 list autocomplete tests
+ queueAutoCompleteTests(getIFrameDOMDoc("iframe2").getElementById("input"));
+
+ ////////////////////////////////////////////////////////////////////////////
+ // searchbar tests
+
+ // focus searchbar, focus on text entry
+ gQueue.push(new synthFocus("searchbar",
+ new focusChecker(getTextEntry, "searchbar")));
+ // open search engine popup, no focus
+ gQueue.push(new synthOpenComboboxKey("searchbar", new nofocusChecker()));
+ // select first item, focus on it
+ gQueue.push(new synthDownKey("searchbar",
+ new focusChecker(getItem, new itemObj("searchbar", 0))));
+ // mouse over on second item, focus on it
+ gQueue.push(new focusOnMouseOver(getItem, new itemObj("searchbar", 1)));
+ // press enter key, focus on text entry
+ gQueue.push(new synthEnterKey("searchbar",
+ new focusChecker(getTextEntry, "searchbar")));
+ // click on search button, open popup, focus goes to document
+ var searchBtn = getAccessible(getNode("searchbar").searchButton);
+ gQueue.push(new synthClick(searchBtn, new focusChecker(document)));
+ // select first item, focus on it
+ gQueue.push(new synthDownKey("searchbar",
+ new focusChecker(getItem, new itemObj("searchbar", 0))));
+ // close popup, focus goes on document
+ gQueue.push(new synthEscapeKey("searchbar", new focusChecker(document)));
+
+ gQueue.onFinish = function()
+ {
+ // unregister 'test-a11y-search' autocomplete search
+ shutdownAutoComplete();
+ }
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Register 'test-a11y-search' autocomplete search.
+ // XPFE AutoComplete needs to register early.
+ initAutoComplete([ "hello", "hi" ],
+ [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
+ addA11yLoadEvent(initTests);
+ ]]>
+ </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=383759"
+ title="Focus event inconsistent for search box autocomplete">
+ Mozilla Bug 383759
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559766"
+ title="Add accessibility support for @list on HTML input and for HTML datalist">
+ Mozilla Bug 559766
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="autocomplete" type="autocomplete"
+ autocompletesearch="test-a11y-search"/>
+
+ <textbox id="richautocomplete" type="autocomplete"
+ autocompletesearch="test-a11y-search"
+ autocompletepopup="richpopup"/>
+ <panel id="richpopup" type="autocomplete-richlistbox" noautofocus="true"/>
+
+ <iframe id="iframe"/>
+
+ <iframe id="iframe2"/>
+
+ <searchbar id="searchbar"/>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_browserui.xul b/accessible/tests/mochitest/events/test_focus_browserui.xul
new file mode 100644
index 000000000..bd621ebf2
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_browserui.xul
@@ -0,0 +1,149 @@
+<?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="Accessibility Loading Document Events Test.">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+ ////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ function inputInDocument()
+ {
+ var tabdoc = currentTabDocument();
+ return tabdoc.getElementById("input");
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function loadURI(aURI)
+ {
+ this.invoke = function loadURI_invoke()
+ {
+ tabBrowser().loadURI(aURI);
+ }
+
+ this.eventSeq = [
+ new focusChecker(currentTabDocument)
+ ];
+
+ this.getID = function loadURI_getID()
+ {
+ return "load uri " + aURI;
+ }
+ }
+
+ function goBack()
+ {
+ this.invoke = function goBack_invoke()
+ {
+ tabBrowser().goBack();
+ }
+
+ this.eventSeq = [
+ new focusChecker(inputInDocument)
+ ];
+
+ this.getID = function goBack_getID()
+ {
+ return "go back one page in history ";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Testing
+
+ var gInputDocURI = "data:text/html,<html><input id='input'></html>";
+ var gButtonDocURI = "data:text/html,<html><input id='input' type='button' value='button'></html>";
+
+ //gA11yEventDumpToConsole = true; // debug
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ var tabDocument = currentTabDocument();
+ var input = inputInDocument();
+
+ // move focus to input inside tab document
+ gQueue.push(new synthTab(tabDocument, new focusChecker(input),
+ browserWindow()));
+
+ // open new url, focus moves to new document
+ gQueue.push(new loadURI(gButtonDocURI));
+
+ // back one page in history, moves moves on input of tab document
+ gQueue.push(new goBack());
+
+ // open new tab, focus moves to urlbar
+ gQueue.push(new synthKey(tabDocument, "t", { accelKey: true, window: browserWindow() },
+ new focusChecker(urlbarInput)));
+
+ // close open tab, focus goes on input of tab document
+ gQueue.push(new synthKey(tabDocument, "w", { accelKey: true, window: browserWindow() },
+ new focusChecker(inputInDocument)));
+
+ gQueue.onFinish = function()
+ {
+ closeBrowserWindow();
+ }
+ gQueue.invoke();
+ }
+
+ if (navigator.oscpu.startsWith("Windows NT 6.1") || navigator.oscpu.startsWith("Windows NT 6.2")) {
+ todo(false, "fix the leak!");
+ } else {
+ SimpleTest.waitForExplicitFinish();
+ openBrowserWindow(doTests, gInputDocURI);
+ }
+ ]]>
+ </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=644452"
+ title="Focus not set when switching to cached document with back or forward if anything other than the document was last focused">
+ Mozilla Bug 644452
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=665412"
+ title="Broken focus when returning to editable text field after closing a tab while focused in the Navigation toolbar">
+ Mozilla Bug 665412
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <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/events/test_focus_canvas.html b/accessible/tests/mochitest/events/test_focus_canvas.html
new file mode 100644
index 000000000..dcccb08e0
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_canvas.html
@@ -0,0 +1,61 @@
+<html>
+
+<head>
+ <title>Accessible focus testing in canvas subdom</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new synthTab("button", new focusChecker("textbox")));
+
+ gQueue.invoke(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTests);
+ </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>
+ <input id="button" type="button">
+ <input id="textbox">
+ </canvas>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_contextmenu.xul b/accessible/tests/mochitest/events/test_focus_contextmenu.xul
new file mode 100644
index 000000000..0cf62b912
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_contextmenu.xul
@@ -0,0 +1,99 @@
+<?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="Context nenu focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var winLowerThanVista = navigator.platform.indexOf("Win") == 0;
+ if (winLowerThanVista) {
+ var version = Components.classes["@mozilla.org/system-info;1"]
+ .getService(Components.interfaces.nsIPropertyBag2)
+ .getProperty("version");
+ version = parseFloat(version);
+ winLowerThanVista = !(version >= 6.0);
+ }
+
+ var gQueue = null;
+ function doTests()
+ {
+ // bug 746183 - Whole file times out on OS X
+ if (MAC || winLowerThanVista) {
+ todo(false, "Reenable on mac after fixing bug 746183!");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new synthContextMenu("button",
+ new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
+ gQueue.push(new synthEscapeKey("contextmenu", new focusChecker("button")));
+
+ gQueue.push(new synthContextMenu("button",
+ new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
+ gQueue.push(new synthDownKey("contextmenu", new focusChecker("item1")));
+ gQueue.push(new synthDownKey("item1", new focusChecker("item2")));
+ gQueue.push(new synthRightKey("item2", new focusChecker("item2.1")));
+ if (WIN) {
+ todo(false, "synthEscapeKey for item2.1 and item2 disabled due to bug 691580");
+ } else {
+ gQueue.push(new synthEscapeKey("item2.1", new focusChecker("item2")));
+ gQueue.push(new synthEscapeKey("item2", new focusChecker("button")));
+ }
+ 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=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <button id="button" context="contextmenu" label="button"/>
+ <menupopup id="contextmenu">
+ <menuitem id="item1" label="item1"/>
+ <menu id="item2" label="item2">
+ <menupopup>
+ <menuitem id="item2.1" label="item2.1"/>
+ </menupopup>
+ </menu>
+ </menupopup>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_controls.html b/accessible/tests/mochitest/events/test_focus_controls.html
new file mode 100644
index 000000000..a18832a8f
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_controls.html
@@ -0,0 +1,75 @@
+<html>
+
+<head>
+ <title>Accessible focus testing on HTML controls</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue(EVENT_FOCUS);
+
+ gQueue.push(new synthFocus("textbox"));
+ gQueue.push(new synthFocus("textarea"));
+ gQueue.push(new synthFocus("button1"));
+ gQueue.push(new synthFocus("button2"));
+ gQueue.push(new synthFocus("checkbox"));
+ gQueue.push(new synthFocus("radio1"));
+ gQueue.push(new synthDownKey("radio1", new focusChecker("radio2")));
+
+ // no focus events for checkbox or radio inputs when they are checked
+ // programmatically
+ gQueue.push(new changeCurrentItem("checkbox"));
+ gQueue.push(new changeCurrentItem("radio1"));
+
+ 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=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="textbox">
+ <textarea id="textarea"></textarea>
+
+ <input id="button1" type="button" value="button">
+ <button id="button2">button</button>
+ <input id="checkbox" type="checkbox">
+ <input id="radio1" type="radio" name="radiogroup">
+ <input id="radio2" type="radio" name="radiogroup">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_dialog.html b/accessible/tests/mochitest/events/test_focus_dialog.html
new file mode 100644
index 000000000..9d88b0cb4
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_dialog.html
@@ -0,0 +1,164 @@
+<html>
+
+<head>
+ <title>Accessible focus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ function openCloseDialog(aID)
+ {
+ this.eventSeq = [
+ new focusChecker(getNode(aID))
+ ];
+
+ this.invoke = function openCloseDialog_invoke()
+ {
+ var wnd = window.open("focus.html");
+ wnd.close();
+ }
+
+ this.getID = function openCloseDialog_getID()
+ {
+ return "Open close dialog while focus on " + prettyName(aID);
+ }
+ }
+
+ var gDialogWnd = null;
+ function getDialogDocument()
+ {
+ return gDialogWnd.document;
+ }
+
+ function openDialog(aID)
+ {
+ this.eventSeq = [
+ new focusChecker(getDialogDocument)
+ ];
+
+ this.invoke = function openDialog_invoke()
+ {
+ gDialogWnd = window.open("focus.html");
+ }
+
+ this.getID = function openDialog_getID()
+ {
+ return "Open dialog while focus on " + prettyName(aID);
+ }
+ }
+
+ function closeDialog(aID)
+ {
+ this.eventSeq = [
+ new focusChecker(aID)
+ ];
+
+ this.invoke = function closeDialog_invoke()
+ {
+ gDialogWnd.close();
+ }
+
+ this.getID = function closeDialog_getID()
+ {
+ return "Close dialog while focus on " + prettyName(aID);
+ }
+ }
+
+ function showNFocusAlertDialog()
+ {
+ this.ID = "alertdialog";
+ this.DOMNode = getNode(this.ID);
+
+ this.invoke = function showNFocusAlertDialog_invoke()
+ {
+ document.getElementById(this.ID).style.display = 'block';
+ document.getElementById(this.ID).focus();
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.DOMNode),
+ new focusChecker(this.DOMNode)
+ ];
+
+ this.getID = function showNFocusAlertDialog_getID()
+ {
+ return "Show and focus alert dialog " + prettyName(this.ID);
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ gQueue = new eventQueue(EVENT_FOCUS);
+
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new openDialog("button"));
+ gQueue.push(new closeDialog("button"));
+
+ var frameNode = getNode("editabledoc");
+ gQueue.push(new synthFocusOnFrame(frameNode));
+ gQueue.push(new openCloseDialog(frameNode.contentDocument));
+
+ gQueue.push(new showNFocusAlertDialog());
+
+ 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=551679"
+ title="focus is not fired for focused document when switching between windows">
+ Mozilla Bug 551679
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=580464"
+ title="Accessible focus incorrect after JS focus() but correct after switching apps or using menu bar">
+ Mozilla Bug 580464
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <button id="button">button</button>
+ <iframe id="editabledoc" src="focus.html"></iframe>
+
+ <div id="alertdialog" style="display: none" tabindex="-1" role="alertdialog" aria-labelledby="title2" aria-describedby="desc2">
+ <div id="title2">Blah blah</div>
+ <div id="desc2">Woof woof woof.</div>
+ <button>Close</button>
+ </div>
+
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_doc.html b/accessible/tests/mochitest/events/test_focus_doc.html
new file mode 100644
index 000000000..bd4934d84
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_doc.html
@@ -0,0 +1,95 @@
+<html>
+
+<head>
+ <title>Accessible document focus event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ var gQueue = null;
+
+ //var gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ // setup
+ var frameDoc = document.getElementById("iframe").contentDocument;
+ frameDoc.designMode = "on";
+ var frameDocAcc = getAccessible(frameDoc, [nsIAccessibleDocument]);
+ var buttonAcc = getAccessible("b1");
+
+ var frame2Doc = document.getElementById("iframe2").contentDocument;
+ var frame2Input = frame2Doc.getElementById("input");
+ var frame2DocAcc = getAccessible(frame2Doc);
+ var frame2InputAcc = getAccessible(frame2Input);
+
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ // try to give focus to contentEditable frame twice to cover bug 512059
+ gQueue.push(new synthFocus(buttonAcc));
+ gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
+ gQueue.push(new synthFocus(buttonAcc));
+ gQueue.push(new synthTab(frameDocAcc, new focusChecker(frameDocAcc)));
+
+ // focus on not editable document
+ gQueue.push(new synthFocus(frame2InputAcc));
+ gQueue.push(new synthShiftTab(frame2DocAcc, new focusChecker(frame2DocAcc)));
+
+ 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=512058"
+ title="Can't set focus to designMode document via accessibility APIs">
+ Mozilla Bug 512058
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=512059"
+ title="Accessibility focus event never fired for designMode document after the first focus">
+ Mozilla Bug 512059
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=618046"
+ title="No focus change event when Shift+Tab at top of screen">
+ Mozilla Bug 618046
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="eventdump"></div>
+
+ <div id="testContainer">
+ <button id="b1">a button</button>
+ <iframe id="iframe" src="about:blank"></iframe>
+ <button id="b2">a button</button>
+ <iframe id="iframe2" src="data:text/html,<html><input id='input'></html>"></iframe>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_general.html b/accessible/tests/mochitest/events/test_focus_general.html
new file mode 100644
index 000000000..e881a5a4f
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_general.html
@@ -0,0 +1,179 @@
+<html>
+
+<head>
+ <title>Accessible focus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+ function focusElmWhileSubdocIsFocused(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.invoke = function focusElmWhileSubdocIsFocused_invoke()
+ {
+ this.DOMNode.focus();
+ }
+
+ this.eventSeq = [
+ new focusChecker(this.DOMNode)
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_FOCUS, this.DOMNode.ownerDocument)
+ ];
+
+ this.getID = function focusElmWhileSubdocIsFocused_getID()
+ {
+ return "Focus element while subdocument is focused " + prettyName(aID);
+ }
+ }
+
+ function imageMapChecker(aID)
+ {
+ var node = getNode(aID);
+ this.type = EVENT_FOCUS;
+ this.match = function imageMapChecker_match(aEvent)
+ {
+ return aEvent.DOMNode == node;
+ }
+ }
+
+ function topMenuChecker()
+ {
+ this.type = EVENT_FOCUS;
+ this.match = function topMenuChecker_match(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_PARENT_MENUITEM;
+ }
+ }
+
+ function contextMenuChecker()
+ {
+ this.type = EVENT_MENUPOPUP_START;
+ this.match = function contextMenuChecker_match(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_MENUPOPUP;
+ }
+ }
+
+ function focusContextMenuItemChecker()
+ {
+ this.__proto__ = new focusChecker();
+
+ this.match = function focusContextMenuItemChecker_match(aEvent)
+ {
+ return aEvent.accessible.role == ROLE_MENUITEM;
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ var frameDoc = document.getElementById("iframe").contentDocument;
+
+ var editableDoc = document.getElementById('editabledoc').contentDocument;
+ editableDoc.designMode = 'on';
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("editablearea"));
+ gQueue.push(new synthFocus("navarea"));
+ gQueue.push(new synthTab("navarea", new focusChecker(frameDoc)));
+ gQueue.push(new focusElmWhileSubdocIsFocused("link"));
+
+ gQueue.push(new synthTab(editableDoc, new focusChecker(editableDoc)));
+ if (WIN || LINUX) {
+ // Alt key is used to active menubar and focus menu item on Windows,
+ // other platforms requires setting a ui.key.menuAccessKeyFocuses
+ // preference.
+ gQueue.push(new toggleTopMenu(editableDoc, new topMenuChecker()));
+ gQueue.push(new toggleTopMenu(editableDoc, new focusChecker(editableDoc)));
+ }
+ gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
+ gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
+ gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
+ if (SEAMONKEY) {
+ todo(false, "shift tab from editable document fails on (Windows) SeaMonkey! (Bug 718235)");
+ } else {
+ if (LINUX || MAC)
+ todo(false, "shift tab from editable document fails on linux and Mac, bug 746519!");
+ else
+ gQueue.push(new synthShiftTab("link", new focusChecker("link")));
+ } // ! SEAMONKEY
+
+ gQueue.push(new synthFocus("a", new imageMapChecker("a")));
+ gQueue.push(new synthFocus("b", new imageMapChecker("b")));
+
+ 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=352220"
+ title="Inconsistent focus events when returning to a document frame">
+ Mozilla Bug 352220
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=550338"
+ title="Broken focus when returning to editable documents from menus">
+ Mozilla Bug 550338
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=961696"
+ title="Accessible object:state-changed:focused events for imagemap links are broken">
+ Mozilla Bug 961696
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="editablearea" contentEditable="true">editable area</div>
+ <div id="navarea" tabindex="0">navigable area</div>
+ <iframe id="iframe" src="data:text/html,<html></html>"></iframe>
+ <a id="link" href="">link</a>
+ <iframe id="editabledoc" src="about:blank"></iframe>
+
+ <map name="atoz_map">
+ <area id="a" coords="0,0,13,14" shape="rect">
+ <area id="b" coords="17,0,30,14" shape="rect">
+ </map>
+ <img width="447" height="15" usemap="#atoz_map" src="../letters.gif">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_general.xul b/accessible/tests/mochitest/events/test_focus_general.xul
new file mode 100644
index 000000000..f72834f39
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_general.xul
@@ -0,0 +1,179 @@
+<?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 focus event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ function getColorBtn(aBtnObj)
+ {
+ var colorpicker = aBtnObj.colorpicker;
+ var container = colorpicker.firstChild;
+ var btn = container.getChildAt(aBtnObj.btnIndex);
+ return btn;
+ }
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("textbox",
+ new focusChecker(getNode("textbox").inputField)));
+ gQueue.push(new synthFocus("textbox_multiline",
+ new focusChecker(getNode("textbox_multiline").inputField)));
+ gQueue.push(new synthFocus("scale"));
+ gQueue.push(new synthFocusOnFrame("editabledoc"));
+ gQueue.push(new synthFocus("radioclothes",
+ new focusChecker("radiosweater")));
+ gQueue.push(new synthDownKey("radiosweater",
+ new focusChecker("radiojacket")));
+ gQueue.push(new synthFocus("checkbox"));
+ gQueue.push(new synthFocus("button"));
+ gQueue.push(new synthFocus("checkbutton"));
+ gQueue.push(new synthFocus("radiobutton"));
+
+ // focus menubutton
+ gQueue.push(new synthFocus("menubutton"));
+ // click menubutton, open popup, focus stays on menu button
+ gQueue.push(new synthClick("menubutton", new nofocusChecker()));
+ // select first menu item ("item 1"), focus on menu item
+ gQueue.push(new synthDownKey("menubutton", new focusChecker("mb_item1")));
+ // choose select menu item, focus gets back to menubutton
+ gQueue.push(new synthEnterKey("mb_item1", new focusChecker("menubutton")));
+ // press enter to open popup, focus stays on menubutton
+ gQueue.push(new synthEnterKey("menubutton", new nofocusChecker()));
+ // select second menu item ("item 2"), focus on menu item
+ gQueue.push(new synthUpKey("menubutton", new focusChecker("mb_item2")));
+
+ // clicking on button having associated popup doesn't change a focus
+ gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
+ // select first menu item ("item 1"), focus on menu item
+ gQueue.push(new synthDownKey("popupbutton", new focusChecker("bp_item1")));
+ // choose select menu item, focus gets back to menubutton
+ gQueue.push(new synthEnterKey("bp_item1", new focusChecker("menubutton")));
+ // show popup again for the next test
+ gQueue.push(new synthClick("popupbutton", new nofocusChecker()));
+
+if (!MAC) {
+ // click menubutton of the 'menubutton' button while popup of button open.
+ gQueue.push(new synthClick("mbb", new focusChecker("mbb"), { where: "right" }));
+ // close popup, focus stays on menubutton, fire focus event
+ gQueue.push(new synthEscapeKey("mbb", new focusChecker("mbb")));
+ // click menubutton, open popup, focus stays on menubutton
+ gQueue.push(new synthClick("mbb", new nofocusChecker(), { where: "right" }));
+ // select first menu item ("item 1"), focus on menu item
+ gQueue.push(new synthDownKey("mbb", new focusChecker("mbb_item1")));
+ // choose select menu item, focus gets back to menubutton
+ gQueue.push(new synthEnterKey("mbb_item1", new focusChecker("mbb")));
+ // open popup, focus stays on menubutton
+ gQueue.push(new synthOpenComboboxKey("mbb", new nofocusChecker()));
+ // select second menu item ("item 2"), focus on menu item
+ gQueue.push(new synthUpKey("menubutton", new focusChecker("mbb_item2")));
+ // click on menu item of menubutton menu, focus menubutton
+ gQueue.push(new synthClick("mbb_item2", new focusChecker("mbb")));
+} else {
+ todo(false, "mbb tests time out on OS X, fix bug 746970 and reenable!");
+}
+
+ // focus colorpicker button
+ gQueue.push(new synthFocus("colorpicker"));
+ // click on button, open popup, focus goes to current color button
+ var btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 0 };
+ var checker = new focusChecker(getColorBtn, btnObj);
+ gQueue.push(new synthClick("colorpicker", checker));
+ // select sibling color button, focus on it
+ btnObj = { colorpicker: getAccessible("colorpicker"), btnIndex: 1 };
+ var checker = new focusChecker(getColorBtn, btnObj);
+ gQueue.push(new synthRightKey("colorpicker", checker));
+ // choose selected color button, close popup, focus on colorpicker button
+ gQueue.push(new synthEnterKey("colorpicker", new focusChecker("colorpicker")));
+
+ 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=492518"
+ title="xul:slider accessible of xul:scale is accessible illegally">
+ Mozilla Bug 492518
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
+ title=" fire focus event on document accessible whenever the root or body element is focused">
+ Mozilla Bug 552368
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <textbox id="textbox" value="hello"/>
+ <textbox id="textbox_multiline" multiline="true" value="hello"/>
+ <scale id="scale" min="0" max="9" value="5"/>
+ <iframe id="editabledoc" src="focus.html"/>
+ <radiogroup id="radioclothes">
+ <radio id="radiosweater" label="radiosweater"/>
+ <radio id="radiocap" label="radiocap" disabled="true"/>
+ <radio id="radiojacket" label="radiojacket"/>
+ </radiogroup>
+ <checkbox id="checkbox" label="checkbox"/>
+ <button id="button" label="button"/>
+ <button id="checkbutton" type="checkbox" label="checkbutton"/>
+ <button id="radiobutton" type="radio" group="rbgroup" label="radio1"/>
+
+ <button id="menubutton" type="menu" label="menubutton">
+ <menupopup>
+ <menuitem id="mb_item1" label="item1"/>
+ <menuitem id="mb_item2" label="item2"/>
+ </menupopup>
+ </button>
+ <button id="mbb" type="menu-button" label="menubutton button">
+ <menupopup>
+ <menuitem id="mbb_item1" label="item1"/>
+ <menuitem id="mbb_item2" label="item2"/>
+ </menupopup>
+ </button>
+
+ <colorpicker id="colorpicker" type="button" label="color picker"
+ color="#FFFFFF"/>
+
+ <popupset>
+ <menupopup id="backpopup" position="after_start">
+ <menuitem id="bp_item1" label="Page 1"/>
+ <menuitem id="bp_item2" label="Page 2"/>
+ </menupopup>
+ </popupset>
+ <button id="popupbutton" label="Pop Me Up" popup="backpopup"/>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_listcontrols.xul b/accessible/tests/mochitest/events/test_focus_listcontrols.xul
new file mode 100644
index 000000000..db45048e2
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_listcontrols.xul
@@ -0,0 +1,189 @@
+<?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 focus event testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthFocus("listbox", new focusChecker("lb_item1")));
+ gQueue.push(new synthDownKey("lb_item1", new focusChecker("lb_item2")));
+ gQueue.push(new synthTab("lb_item2", new focusChecker("mslb_item1")));
+ gQueue.push(new synthDownKey("mslb_item1", new focusChecker("mslb_item2"), { shiftKey: true }));
+ gQueue.push(new synthTab("mslb_item2", new focusChecker("emptylistbox")));
+ gQueue.push(new synthFocus("mcolumnlistbox", new focusChecker("mclb_item1")));
+ gQueue.push(new synthDownKey("mclb_item1", new focusChecker("mclb_item2")));
+ gQueue.push(new synthFocus("headerlistbox", new focusChecker("hlb_item1")));
+ gQueue.push(new synthDownKey("hlb_item1", new focusChecker("hlb_item2")));
+
+ gQueue.push(new synthFocus("richlistbox", new focusChecker("rlb_item1")));
+ gQueue.push(new synthDownKey("rlb_item1", new focusChecker("rlb_item2")));
+ gQueue.push(new synthFocus("multiselrichlistbox", new focusChecker("msrlb_item1")));
+ gQueue.push(new synthDownKey("msrlb_item1", new focusChecker("msrlb_item2"), { shiftKey: true }));
+ gQueue.push(new synthFocus("emptyrichlistbox", new focusChecker("emptyrichlistbox")));
+
+ gQueue.push(new synthFocus("menulist"));
+ gQueue.push(new synthClick("menulist", new focusChecker("ml_tangerine")));
+ gQueue.push(new synthDownKey("ml_tangerine", new focusChecker("ml_marmalade")));
+ gQueue.push(new synthEscapeKey("ml_marmalade", new focusChecker("menulist")));
+if (!MAC) {
+ // On Windows, items get selected during navigation.
+ let expectedItem = WIN ? "ml_tangerine" : "ml_marmalade";
+ gQueue.push(new synthDownKey("menulist", new nofocusChecker(expectedItem)));
+ gQueue.push(new synthOpenComboboxKey("menulist", new focusChecker(expectedItem)));
+ gQueue.push(new synthEnterKey(expectedItem, new focusChecker("menulist")));
+} else {
+ todo(false, "Bug 746531 - timeouts of last three menulist tests on OS X");
+}
+
+ var textentry = getAccessible("emenulist").firstChild;
+ gQueue.push(new synthFocus("emenulist", new focusChecker(textentry)));
+ gQueue.push(new synthDownKey(textentry, new nofocusChecker("eml_tangerine")));
+ gQueue.push(new synthUpKey(textentry, new focusChecker("eml_marmalade")));
+ gQueue.push(new synthEnterKey("eml_marmalade", new focusChecker(textentry)));
+ gQueue.push(new synthOpenComboboxKey("emenulist", new focusChecker("eml_marmalade")));
+ gQueue.push(new synthEscapeKey("eml_marmalade", new focusChecker(textentry)));
+
+ // no focus events for unfocused list controls when current item is
+ // changed.
+ gQueue.push(new synthFocus("emptylistbox"));
+
+ gQueue.push(new changeCurrentItem("listbox", "lb_item1"));
+ gQueue.push(new changeCurrentItem("richlistbox", "rlb_item1"));
+if (!MAC) {
+ gQueue.push(new changeCurrentItem("menulist", WIN ? "ml_marmalade" : "ml_tangerine"));
+}
+ gQueue.push(new changeCurrentItem("emenulist", "eml_tangerine"));
+
+ 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=433418"
+ title="Accessibles for focused HTML Select elements are not getting focused state">
+ Mozilla Bug 433418
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
+ title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
+ Mozilla Bug 474893
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=552368"
+ title=" fire focus event on document accessible whenever the root or body element is focused">
+ Mozilla Bug 552368
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <listbox id="listbox" rows="3">
+ <listitem id="lb_item1" label="item1"/>
+ <listitem id="lb_item2" label="item1"/>
+ </listbox>
+ <listbox id="multisellistbox" rows="3" seltype="multiple">
+ <listitem id="mslb_item1" label="item1"/>
+ <listitem id="mslb_item2" label="item1"/>
+ </listbox>
+ <listbox id="emptylistbox" rows="3"/>
+ <listbox id="mcolumnlistbox" rows="3">
+ <listcols>
+ <listcol/>
+ <listcol/>
+ </listcols>
+ <listitem id="mclb_item1">
+ <listcell label="George"/>
+ <listcell label="House Painter"/>
+ </listitem>
+ <listitem id="mclb_item2">
+ <listcell label="Mary Ellen"/>
+ <listcell label="Candle Maker"/>
+ </listitem>
+ </listbox>
+ <listbox id="headerlistbox" rows="3">
+ <listhead>
+ <listheader label="Name"/>
+ <listheader label="Occupation"/>
+ </listhead>
+ <listcols>
+ <listcol/>
+ <listcol flex="1"/>
+ </listcols>
+ <listitem id="hlb_item1">
+ <listcell label="George"/>
+ <listcell label="House Painter"/>
+ </listitem>
+ <listitem id="hlb_item2">
+ <listcell label="Mary Ellen"/>
+ <listcell label="Candle Maker"/>
+ </listitem>
+ </listbox>
+
+ <richlistbox id="richlistbox">
+ <richlistitem id="rlb_item1">
+ <description>A XUL Description!</description>
+ </richlistitem>
+ <richlistitem id="rlb_item2">
+ <button label="A XUL Button"/>
+ </richlistitem>
+ </richlistbox>
+ <richlistbox id="multiselrichlistbox" seltype="multiple">
+ <richlistitem id="msrlb_item1">
+ <description>A XUL Description!</description>
+ </richlistitem>
+ <richlistitem id="msrlb_item2">
+ <button label="A XUL Button"/>
+ </richlistitem>
+ </richlistbox>
+ <richlistbox id="emptyrichlistbox" seltype="multiple"/>
+
+ <menulist id="menulist">
+ <menupopup>
+ <menuitem id="ml_tangerine" label="tangerine trees"/>
+ <menuitem id="ml_marmalade" label="marmalade skies"/>
+ </menupopup>
+ </menulist>
+ <menulist id="emenulist" editable="true">
+ <menupopup>
+ <menuitem id="eml_tangerine" label="tangerine trees"/>
+ <menuitem id="eml_marmalade" label="marmalade skies"/>
+ </menupopup>
+ </menulist>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_menu.xul b/accessible/tests/mochitest/events/test_focus_menu.xul
new file mode 100644
index 000000000..f205e8d25
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_menu.xul
@@ -0,0 +1,119 @@
+<?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 focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ if (WIN) {
+ gQueue.push(new toggleTopMenu("fruit", new focusChecker("fruit")));
+ gQueue.push(new synthRightKey("fruit", new focusChecker("vehicle")));
+ gQueue.push(new synthEscapeKey("vehicle", new focusChecker(document)));
+ }
+
+ // mouse move activate items but no focus event until menubar is active
+ gQueue.push(new synthMouseMove("fruit", new nofocusChecker("apple")));
+
+ // mouseover and click on menuitem makes it active before menubar is
+ // active
+ gQueue.push(new synthClick("fruit", new focusChecker("fruit")));
+
+ // mouseover on menuitem when menubar is active
+ gQueue.push(new synthMouseMove("apple", new focusChecker("apple")));
+
+ // keydown on disabled menuitem (disabled items are skipped on linux)
+ if (WIN)
+ gQueue.push(new synthDownKey("apple", new focusChecker("orange")));
+
+ // menu and menuitem are both active
+ // XXX: intermitent failure because two focus events may be coalesced,
+ // think to workaround or fix this issue, when done enable queue invoker
+ // below and remove next two.
+ //gQueue.push(new synthRightKey("apple",
+ // [ new focusChecker("vehicle"),
+ // new focusChecker("cycle")]));
+ gQueue.push(new synthClick("vehicle", new focusChecker("vehicle")));
+ gQueue.push(new synthDownKey("cycle", new focusChecker("cycle")));
+
+ // open submenu
+ gQueue.push(new synthRightKey("cycle", new focusChecker("tricycle")));
+
+ // move to first menu in cycle, DOMMenuItemActive is fired for fruit,
+ // cycle and apple menuitems (bug 685191)
+ todo(false, "focus is fired for 'cycle' menuitem");
+ //gQueue.push(new synthRightKey("vehicle", new focusChecker("apple")));
+
+ // click menuitem to close menu, focus gets back to document
+ gQueue.push(new synthClick("tricycle", new focusChecker(document)));
+
+ //enableLogging("focus,DOMEvents,tree"); // logging for bug708927
+ //gQueue.onFinish = function() { disableLogging(); }
+
+ 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=673958"
+ title="Rework accessible focus handling">
+ Mozilla Bug 673958
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menubar>
+ <menu id="fruit" label="Fruit">
+ <menupopup>
+ <menuitem id="apple" label="Apple"/>
+ <menuitem id="orange" label="Orange" disabled="true"/>
+ </menupopup>
+ </menu>
+ <menu id="vehicle" label="Vehicle">
+ <menupopup>
+ <menu id="cycle" label="cycle">
+ <menupopup>
+ <menuitem id="tricycle" label="tricycle"/>
+ </menupopup>
+ </menu>
+ <menuitem id="car" label="Car" disabled="true"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_name.html b/accessible/tests/mochitest/events/test_focus_name.html
new file mode 100644
index 000000000..f0db26427
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_name.html
@@ -0,0 +1,122 @@
+<html>
+
+<head>
+ <title>Accessible name testing on focus</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+ /**
+ * Checker for invokers.
+ */
+ function actionChecker(aID, aDescription)
+ {
+ this.__proto__ = new invokerChecker(EVENT_FOCUS, aID);
+
+ this.check = function actionChecker_check(aEvent)
+ {
+ var target = aEvent.accessible;
+ is(target.description, aDescription,
+ "Wrong description for " + prettyName(target));
+ }
+ }
+
+ var gFocusHandler = {
+ handleEvent: function gFocusHandler_handleEvent(aEvent) {
+ var elm = aEvent.target;
+ if (elm.nodeType != nsIDOMNode.ELEMENT_NODE)
+ return;
+
+ gTooltipElm.style.display = "block";
+
+ elm.setAttribute("aria-describedby", "tooltip");
+ }
+ };
+
+ var gBlurHandler = {
+ handleEvent: function gBlurHandler_handleEvent(aEvent) {
+ gTooltipElm.style.display = "none";
+
+ var elm = aEvent.target;
+ if (elm.nodeType == nsIDOMNode.ELEMENT_NODE)
+ elm.removeAttribute("aria-describedby");
+ }
+ };
+
+ /**
+ * Do tests.
+ */
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ var gButtonElm = null;
+ var gTextboxElm = null;
+ var gTooltipElm = null;
+
+ function doTests()
+ {
+ gButtonElm = getNode("button");
+ gTextboxElm = getNode("textbox");
+ gTooltipElm = getNode("tooltip");
+
+ gButtonElm.addEventListener("focus", gFocusHandler, false);
+ gButtonElm.addEventListener("blur", gBlurHandler, false);
+ gTextboxElm.addEventListener("focus", gFocusHandler, false);
+ gTextboxElm.addEventListener("blur", gBlurHandler, false);
+
+ // The aria-describedby is changed on DOM focus. Accessible description
+ // should be updated when a11y focus is fired.
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_FOCUS);
+ gQueue.onFinish = function()
+ {
+ gButtonElm.removeEventListener("focus", gFocusHandler, false);
+ gButtonElm.removeEventListener("blur", gBlurHandler, false);
+ gTextboxElm.removeEventListener("focus", gFocusHandler, false);
+ gTextboxElm.removeEventListener("blur", gBlurHandler, false);
+ }
+
+ var descr = "It's a tooltip";
+ gQueue.push(new synthFocus("button", new actionChecker("button", descr)));
+ gQueue.push(new synthTab("textbox", new actionChecker("textbox", descr)));
+
+ 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=520709"
+ title="mochitest to ensure name/description are updated on a11y focus if they were changed on DOM focus">
+ Mozilla Bug 520709
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="tooltip" style="display: none" aria-hidden="true">It's a tooltip</div>
+ <button id="button">button</button>
+ <input id="textbox">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_selects.html b/accessible/tests/mochitest/events/test_focus_selects.html
new file mode 100644
index 000000000..ef742c4b2
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_selects.html
@@ -0,0 +1,118 @@
+<html>
+
+<head>
+ <title>Accessible focus testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ // Bug 746534 - File causes crash or hang on OS X
+ if (MAC) {
+ todo(false, "Bug 746534 - test file causes crash or hang on OS X");
+ SimpleTest.finish();
+ return;
+ }
+
+ gQueue = new eventQueue();
+
+ // first item is focused until there's selection
+ gQueue.push(new synthFocus("list", new focusChecker("orange")));
+
+ // item is selected and stays focused
+ gQueue.push(new synthDownKey("list", new nofocusChecker()));
+
+ // last selected item is focused
+ gQueue.push(new synthDownKey("list", new focusChecker("apple"), { shiftKey: true }));
+
+ // no focus event if nothing is changed
+ gQueue.push(new synthDownKey("list", new nofocusChecker("apple")));
+
+ // current item is focused
+ gQueue.push(new synthUpKey("list", new focusChecker("orange"), { ctrlKey: true }));
+
+ // focus on empty list (no items to be focused)
+ gQueue.push(new synthTab("list", new focusChecker("emptylist")));
+
+ // current item is focused
+ gQueue.push(new synthShiftTab("emptylist", new focusChecker("orange")));
+
+ // collapsed combobox keeps a focus
+ gQueue.push(new synthFocus("combobox", new focusChecker("combobox")));
+ gQueue.push(new synthDownKey("combobox", new nofocusChecker("combobox")));
+
+ // selected item is focused for expanded combobox
+ gQueue.push(new synthOpenComboboxKey("combobox", new focusChecker("cb_apple")));
+ gQueue.push(new synthUpKey("combobox", new focusChecker("cb_orange")));
+
+ // collapsed combobx keeps a focus
+ gQueue.push(new synthEscapeKey("combobox", new focusChecker("combobox")));
+
+ // no focus events for unfocused list controls when current item is
+ // changed
+ gQueue.push(new synthFocus("emptylist"));
+
+ gQueue.push(new changeCurrentItem("list", "orange"));
+ gQueue.push(new changeCurrentItem("combobox", "cb_apple"));
+
+ 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=433418"
+ title="Accessibles for focused HTML Select elements are not getting focused state">
+ Mozilla Bug 433418
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=474893"
+ title="List controls should fire a focus event on the selected child when tabbing or when the selected child changes while the list is focused">
+ Mozilla Bug 474893
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="list" size="5" multiple="">
+ <option id="orange">Orange</option>
+ <option id="apple">Apple</option>
+ </select>
+
+ <select id="emptylist" size="5"></select>
+
+ <select id="combobox">
+ <option id="cb_orange">Orange</option>
+ <option id="cb_apple">Apple</option>
+ </select>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_focus_tabbox.xul b/accessible/tests/mochitest/events/test_focus_tabbox.xul
new file mode 100644
index 000000000..c51546405
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_tabbox.xul
@@ -0,0 +1,103 @@
+<?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="Tabbox focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTests()
+ {
+ if (MAC) {
+ todo(false, "Tests disabled because of imminent failure.");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Test focus events.
+ gQueue = new eventQueue();
+
+ var textbox = getNode("textbox").inputField;
+ gQueue.push(new synthClick("tab1", new focusChecker("tab1")));
+ gQueue.push(new synthTab("tab1", new focusChecker("checkbox1")));
+ gQueue.push(new synthKey("tab1", "VK_TAB", { ctrlKey: true },
+ new focusChecker(textbox)));
+ gQueue.push(new synthKey("tab2", "VK_TAB", { ctrlKey: true },
+ new focusChecker("tab3")));
+ gQueue.push(new synthKey("tab3", "VK_TAB", { ctrlKey: true },
+ new focusChecker("tab1")));
+
+ 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=370396"
+ title="Control+Tab to an empty tab panel in a tabbox causes focus to leave the tabbox">
+ Mozilla Bug 370396
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <tabbox>
+ <tabs>
+ <tab id="tab1" label="Tab1" selected="true"/>
+ <tab id="tab2" label="Tab2" />
+ <tab id="tab3" label="Tab3" />
+ </tabs>
+ <tabpanels>
+ <tabpanel orient="vertical">
+ <groupbox orient="vertical">
+ <checkbox id="checkbox1" label="Monday" width="75"/>
+ <checkbox label="Tuesday" width="75"/>
+ <checkbox label="Wednesday" width="75"/>
+ <checkbox label="Thursday" width="75"/>
+ <checkbox label="Friday" width="75"/>
+ <checkbox label="Saturday" width="75"/>
+ <checkbox label="Sunday" width="75"/>
+ </groupbox>
+
+ <spacer style="height: 10px" />
+ <label value="Label After checkboxes" />
+ </tabpanel>
+ <tabpanel orient="vertical">
+ <textbox id="textbox" />
+ </tabpanel>
+ <tabpanel orient="vertical">
+ <description>Tab 3 content</description>
+ </tabpanel>
+ </tabpanels>
+ </tabbox>
+
+ <vbox id="eventdump"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_focus_tree.xul b/accessible/tests/mochitest/events/test_focus_tree.xul
new file mode 100644
index 000000000..995b08e25
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_focus_tree.xul
@@ -0,0 +1,122 @@
+<?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="XUL tree focus testing">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.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[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function focusTree(aTreeID)
+ {
+ var checker = new focusChecker(getFirstTreeItem, aTreeID);
+ this.__proto__ = new synthFocus(aTreeID, [ checker ]);
+ }
+
+ function moveToNextItem(aTreeID)
+ {
+ var checker = new focusChecker(getSecondTreeItem, aTreeID);
+ this.__proto__ = new synthDownKey(aTreeID, [ checker ]);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Helpers
+
+ function getTreeItemAt(aTreeID, aIdx)
+ { return getAccessible(aTreeID).getChildAt(aIdx + 1); }
+
+ function getFirstTreeItem(aTreeID)
+ { return getTreeItemAt(aTreeID, 0); }
+
+ function getSecondTreeItem(aTreeID)
+ { return getTreeItemAt(aTreeID, 1); }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ var gQueue = null;
+
+ //gA11yEventDumpID = "debug"; // debugging
+ //gA11yEventDumpToConsole = true; // debugging
+
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new focusTree("tree"));
+ gQueue.push(new moveToNextItem("tree"));
+ gQueue.push(new synthFocus("emptytree"));
+
+ // no focus event for changed current item for unfocused tree
+ gQueue.push(new changeCurrentItem("tree", 0));
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yXULTreeLoadEvent(doTest, "tree", new nsTableTreeView(5));
+ ]]>
+ </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=386821"
+ title="Need better solution for firing delayed event against xul tree">
+ Mozilla Bug 386821
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=406308"
+ title="Don't fire accessible focus events if widget is not actually in focus, confuses screen readers">
+ Mozilla Bug 406308
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ <tree id="emptytree" flex="1">
+ <treecols>
+ <treecol id="emptytree_col1" flex="1" primary="true" label="column"/>
+ <treecol id="emptytree_col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="emptytree_treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/events/test_fromUserInput.html b/accessible/tests/mochitest/events/test_fromUserInput.html
new file mode 100644
index 000000000..1cfeedf0b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_fromUserInput.html
@@ -0,0 +1,127 @@
+<html>
+
+<head>
+ <title>Testing of isFromUserInput in text events</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+
+ /**
+ * Remove text data from HTML input.
+ */
+ function removeTextFromInput(aID, aStart, aEnd, aText, aFromUser)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, false, aFromUser)
+ ];
+
+ this.invoke = function removeTextFromInput_invoke()
+ {
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ this.DOMNode.focus();
+ this.DOMNode.setSelectionRange(aStart, aEnd);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromInput_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove text data from text node.
+ */
+ function removeTextFromContentEditable(aID, aStart, aEnd, aText, aFromUser)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, false, aFromUser)
+ ];
+
+ this.invoke = function removeTextFromContentEditable_invoke()
+ {
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ this.DOMNode.focus();
+ this.textNode = getNode(aID).firstChild;
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.setStart(this.textNode, aStart);
+ range.setEnd(this.textNode, aEnd);
+ selection.addRange(range);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromContentEditable_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Focused editable text node
+ gQueue.push(new removeTextFromContentEditable("div", 0, 3, "hel", true));
+
+ // Focused editable HTML input
+ gQueue.push(new removeTextFromInput("input", 1, 2, "n", true));
+
+ 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=686909"
+ title="isFromUserInput flag on accessible text change events not correct">
+ Mozilla Bug 686909
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <div id="eventdump"></div>
+
+ <div id="div" contentEditable="true">hello</div>
+ <input id="input" value="input">
+
+</body>
+
+</html>
diff --git a/accessible/tests/mochitest/events/test_label.xul b/accessible/tests/mochitest/events/test_label.xul
new file mode 100644
index 000000000..e82763a7c
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_label.xul
@@ -0,0 +1,177 @@
+<?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="Tests: accessible XUL label/description events">
+
+ <script type="application/javascript"
+ 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
+
+ const kRecreated = 0;
+ const kTextRemoved = 1;
+ const kTextChanged = 2;
+
+ const kNoValue = 0;
+
+ /**
+ * Set/remove @value attribute.
+ */
+ function setValue(aID, aValue, aResult, aOldValue)
+ {
+ this.labelNode = getNode(aID);
+
+ this.eventSeq = [];
+
+ switch (aResult) {
+ case kRecreated:
+ this.eventSeq.push(new invokerChecker(EVENT_HIDE, this.labelNode));
+ this.eventSeq.push(new invokerChecker(EVENT_SHOW, this.labelNode));
+ break;
+ case kTextRemoved:
+ this.eventSeq.push(
+ new textChangeChecker(this.labelNode, 0, aOldValue.length,
+ aOldValue, false));
+ break;
+ case kTextChanged:
+ this.eventSeq.push(
+ new textChangeChecker(this.labelNode, 0, aOldValue.length,
+ aOldValue, false));
+ this.eventSeq.push(
+ new textChangeChecker(this.labelNode, 0, aValue.length,
+ aValue, true));
+ break;
+ }
+
+ this.invoke = function setValue_invoke()
+ {
+ if (aValue === kNoValue)
+ this.labelNode.removeAttribute("value");
+ else
+ this.labelNode.setAttribute("value", aValue);
+ }
+
+ this.finalCheck = function setValue_finalCheck()
+ {
+ var tree =
+ { LABEL: [
+ { TEXT_LEAF: [ ] }
+ ] };
+ testAccessibleTree(aID, tree);
+ }
+
+ this.getID = function setValue_getID()
+ {
+ return "set @value='" + aValue + "' for label " + prettyName(aID);
+ }
+ }
+
+ /**
+ * Change @crop attribute.
+ */
+ function setCrop(aID, aCropValue, aRemovedText, aInsertedText)
+ {
+ this.labelNode = getNode(aID);
+ this.width = this.labelNode.boxObject.width;
+ this.charWidth = this.width / this.labelNode.value.length;
+
+ this.eventSeq = [
+ new textChangeChecker(this.labelNode, 0, -1, aRemovedText, false),
+ new textChangeChecker(this.labelNode, 0, -1, aInsertedText, true)
+ ];
+
+ this.invoke = function setCrop_invoke()
+ {
+ if (!this.labelNode.hasAttribute("crop"))
+ this.labelNode.width = Math.floor(this.width - 2 * this.charWidth);
+
+ this.labelNode.setAttribute("crop", aCropValue);
+ }
+
+ this.getID = function setCrop_finalCheck()
+ {
+ return "set crop " + aCropValue;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setValue("label", "shiroka strana", kRecreated));
+ gQueue.push(new setValue("label", "?<>!+_", kTextChanged, "shiroka strana"));
+ gQueue.push(new setValue("label", "", kTextRemoved, "?<>!+_"));
+ gQueue.push(new setValue("label", kNoValue, kRecreated));
+
+ gQueue.push(new setValue("descr", "hello world", kRecreated));
+ gQueue.push(new setValue("descr", "si_ya", kTextChanged, "hello world"));
+ gQueue.push(new setValue("descr", "", kTextRemoved, "si_ya"));
+ gQueue.push(new setValue("descr", kNoValue, kRecreated));
+
+ if (MAC) {
+ // "valuetocro" -> "…etocro"
+ gQueue.push(new setCrop("croplabel", "left", "valu", "…"));
+ // "…etocro", "val…cro"
+ gQueue.push(new setCrop("croplabel", "center", "…eto", "val…"));
+ } else {
+ // "valuetocro" -> "…uetocro"
+ gQueue.push(new setCrop("croplabel", "left", "val", "…"));
+ // "…uetocro" -> "valu…cro"
+ gQueue.push(new setCrop("croplabel", "center", "…ueto", "valu…"));
+ }
+
+ 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=396166"
+ title="xul:label@value accessible should implement nsIAccessibleText">
+ Bug 396166
+ </a>
+ <br/>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <label id="label">hello</label>
+ <description id="descr">hello</description>
+
+ <hbox>
+ <label id="croplabel" value="valuetocro"
+ style="font-family: monospace;"/>
+ </hbox>
+ </vbox>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/events/test_menu.xul b/accessible/tests/mochitest/events/test_menu.xul
new file mode 100644
index 000000000..bae36fb71
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_menu.xul
@@ -0,0 +1,202 @@
+<?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 menu events testing for XUL menu">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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">
+ function openFileMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENU_START, getNode("menubar")),
+ new invokerChecker(EVENT_MENUPOPUP_START, getNode("menupopup-file"))
+ // new invokerChecker(EVENT_FOCUS, getNode("menuitem-newtab")) intermitent failure
+ ];
+
+ this.invoke = function openFileMenu_invoke()
+ {
+ synthesizeKey("F", { altKey: true, shiftKey: true });
+ }
+
+ this.getID = function openFileMenu_getID()
+ {
+ return "open file menu by alt+F press";
+ }
+ }
+
+ function openEditMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENUPOPUP_END, getNode("menupopup-file")),
+ new invokerChecker(EVENT_MENUPOPUP_START, getNode("menupopup-edit"))
+ // new invokerChecker(EVENT_FOCUS, getNode("menuitem-undo")) intermitent failure
+ ];
+
+ this.invoke = function openEditMenu_invoke()
+ {
+ synthesizeKey("VK_RIGHT", { });
+ }
+
+ this.getID = function openEditMenu_getID()
+ {
+ return "open edit menu by lef arrow press";
+ }
+ }
+
+ function closeEditMenu()
+ {
+ this.eventSeq = [
+ //new invokerChecker(EVENT_FOCUS, document), intermitent failure
+ new invokerChecker(EVENT_MENUPOPUP_END, getNode("menupopup-edit")),
+ new invokerChecker(EVENT_MENU_END, getNode("menubar"))
+ ];
+
+ this.invoke = function closeEditMenu_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function closeEditMenu_getID()
+ {
+ return "close edit menu, leave menubar";
+ }
+ }
+
+ function focusFileMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_MENU_START, getNode("menubar"))
+ // new invokerChecker(EVENT_FOCUS, getNode("menuitem-file")) //intermitent failure
+ ];
+
+ this.invoke = function focusFileMenu_invoke()
+ {
+ synthesizeKey("VK_ALT", { });
+ }
+
+ this.getID = function focusFileMenu_getID()
+ {
+ return "activate menubar, focus file menu (atl press)";
+ }
+ }
+
+ function focusEditMenu()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_FOCUS, getNode("menuitem-edit"))
+ ];
+
+ this.invoke = function focusEditMenu_invoke()
+ {
+ synthesizeKey("VK_RIGHT", { });
+ }
+
+ this.getID = function focusEditMenu_getID()
+ {
+ return "focus edit menu";
+ }
+ }
+
+ function leaveMenubar()
+ {
+ this.eventSeq = [
+ //new invokerChecker(EVENT_FOCUS, document), intermitent failure
+ new invokerChecker(EVENT_MENU_END, getNode("menubar"))
+ ];
+
+ this.invoke = function leaveMenubar_invoke()
+ {
+ synthesizeKey("VK_ESCAPE", { });
+ }
+
+ this.getID = function leaveMenubar_getID()
+ {
+ return "leave menubar";
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+
+ //gA11yEventDumpID = "eventdump";
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+
+ function doTests()
+ {
+ if (!WIN) {
+ todo(false, "Enable this test on other platforms.");
+ SimpleTest.finish();
+ return;
+ }
+
+ todo(false,
+ "Fix intermitent failures. Focus may randomly occur before or after menupopup events!");
+
+ gQueue = new eventQueue();
+
+ gQueue.push(new openFileMenu());
+ gQueue.push(new openEditMenu());
+ gQueue.push(new closeEditMenu());
+
+ // Alt key is used to active menubar and focus menu item on Windows,
+ // other platforms requires setting a ui.key.menuAccessKeyFocuses
+ // preference.
+ if (WIN || LINUX) {
+ gQueue.push(new focusFileMenu());
+ gQueue.push(new focusEditMenu());
+ gQueue.push(new leaveMenubar());
+ }
+
+ 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=615189"
+ title="Clean up FireAccessibleFocusEvent">
+ Mozilla Bug 615189
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox flex="1">
+ <menubar id="menubar">
+ <menu id="menuitem-file" label="File" accesskey="F">
+ <menupopup id="menupopup-file">
+ <menuitem id="menuitem-newtab" label="New Tab"/>
+ </menupopup>
+ </menu>
+ <menu id="menuitem-edit" label="Edit" accesskey="E">
+ <menupopup id="menupopup-edit">
+ <menuitem id="menuitem-undo" label="Undo"/>
+ </menupopup>
+ </menu>
+ </menubar>
+
+ <vbox id="eventdump" role="log"/>
+ </vbox>
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_mutation.html b/accessible/tests/mochitest/events/test_mutation.html
new file mode 100644
index 000000000..232a09727
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_mutation.html
@@ -0,0 +1,632 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <style>
+ div.displayNone a { display:none; }
+ div.visibilityHidden a { visibility:hidden; }
+</style>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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.
+ */
+ var kNoEvents = 0;
+
+ var kShowEvent = 1;
+ var kHideEvent = 2;
+ var kReorderEvent = 4;
+ var kShowEvents = kShowEvent | kReorderEvent;
+ var kHideEvents = kHideEvent | kReorderEvent;
+ var kHideAndShowEvents = kHideEvents | kShowEvent;
+
+ /**
+ * Base class to test mutation a11y events.
+ *
+ * @param aNodeOrID [in] node invoker's action is executed for
+ * @param aEventTypes [in] events to register (see constants above)
+ * @param aDoNotExpectEvents [in] boolean indicates if events are expected
+ */
+ function mutateA11yTree(aNodeOrID, aEventTypes, aDoNotExpectEvents)
+ {
+ // Interface
+ this.DOMNode = getNode(aNodeOrID);
+ this.doNotExpectEvents = aDoNotExpectEvents;
+ this.eventSeq = [];
+ this.unexpectedEventSeq = [];
+
+ /**
+ * Change default target (aNodeOrID) registered for the given event type.
+ */
+ this.setTarget = function mutateA11yTree_setTarget(aEventType, aTarget)
+ {
+ var type = this.getA11yEventType(aEventType);
+ for (var idx = 0; idx < this.getEventSeq().length; idx++) {
+ if (this.getEventSeq()[idx].type == type) {
+ this.getEventSeq()[idx].target = aTarget;
+ return idx;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Replace the default target currently registered for a given event type
+ * with the nodes in the targets array.
+ */
+ this.setTargets = function mutateA11yTree_setTargets(aEventType, aTargets) {
+ var targetIdx = this.setTarget(aEventType, aTargets[0]);
+
+ var type = this.getA11yEventType(aEventType);
+ for (var i = 1; i < aTargets.length; i++) {
+ var checker = new invokerChecker(type, aTargets[i]);
+ this.getEventSeq().splice(++targetIdx, 0, checker);
+ }
+ }
+
+ // Implementation
+ this.getA11yEventType = function mutateA11yTree_getA11yEventType(aEventType)
+ {
+ if (aEventType == kReorderEvent)
+ return nsIAccessibleEvent.EVENT_REORDER;
+
+ if (aEventType == kHideEvent)
+ return nsIAccessibleEvent.EVENT_HIDE;
+
+ if (aEventType == kShowEvent)
+ return nsIAccessibleEvent.EVENT_SHOW;
+ }
+
+ this.getEventSeq = function mutateA11yTree_getEventSeq()
+ {
+ return this.doNotExpectEvents ? this.unexpectedEventSeq : this.eventSeq;
+ }
+
+ if (aEventTypes & kHideEvent) {
+ var checker = new invokerChecker(this.getA11yEventType(kHideEvent),
+ this.DOMNode);
+ this.getEventSeq().push(checker);
+ }
+
+ if (aEventTypes & kShowEvent) {
+ var checker = new invokerChecker(this.getA11yEventType(kShowEvent),
+ this.DOMNode);
+ this.getEventSeq().push(checker);
+ }
+
+ if (aEventTypes & kReorderEvent) {
+ var checker = new invokerChecker(this.getA11yEventType(kReorderEvent),
+ this.DOMNode.parentNode);
+ this.getEventSeq().push(checker);
+ }
+ }
+
+ /**
+ * Change CSS style for the given node.
+ */
+ function changeStyle(aNodeOrID, aProp, aValue, aEventTypes)
+ {
+ this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
+
+ this.invoke = function changeStyle_invoke()
+ {
+ this.DOMNode.style[aProp] = aValue;
+ }
+
+ this.getID = function changeStyle_getID()
+ {
+ return aNodeOrID + " change style " + aProp + " on value " + aValue;
+ }
+ }
+
+ /**
+ * Change class name for the given node.
+ */
+ function changeClass(aParentNodeOrID, aNodeOrID, aClassName, aEventTypes)
+ {
+ this.__proto__ = new mutateA11yTree(aNodeOrID, aEventTypes, false);
+
+ this.invoke = function changeClass_invoke()
+ {
+ this.parentDOMNode.className = aClassName;
+ }
+
+ this.getID = function changeClass_getID()
+ {
+ return aNodeOrID + " change class " + aClassName;
+ }
+
+ this.parentDOMNode = getNode(aParentNodeOrID);
+ }
+
+ /**
+ * Clone the node and append it to its parent.
+ */
+ function cloneAndAppendToDOM(aNodeOrID, aEventTypes,
+ aTargetsFunc, aReorderTargetFunc)
+ {
+ var eventTypes = aEventTypes || kShowEvents;
+ var doNotExpectEvents = (aEventTypes == kNoEvents);
+
+ this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
+ doNotExpectEvents);
+
+ this.invoke = function cloneAndAppendToDOM_invoke()
+ {
+ var newElm = this.DOMNode.cloneNode(true);
+ newElm.removeAttribute('id');
+
+ var targets = aTargetsFunc ?
+ aTargetsFunc.call(null, newElm) : [newElm];
+ this.setTargets(kShowEvent, targets);
+
+ if (aReorderTargetFunc) {
+ var reorderTarget = aReorderTargetFunc.call(null, this.DOMNode);
+ this.setTarget(kReorderEvent, reorderTarget);
+ }
+
+ this.DOMNode.parentNode.appendChild(newElm);
+ }
+
+ this.getID = function cloneAndAppendToDOM_getID()
+ {
+ return aNodeOrID + " clone and append to DOM.";
+ }
+ }
+
+ /**
+ * Removes the node from DOM.
+ */
+ function removeFromDOM(aNodeOrID, aEventTypes,
+ aTargetsFunc, aReorderTargetFunc)
+ {
+ var eventTypes = aEventTypes || kHideEvents;
+ var doNotExpectEvents = (aEventTypes == kNoEvents);
+
+ this.__proto__ = new mutateA11yTree(aNodeOrID, eventTypes,
+ doNotExpectEvents);
+
+ this.invoke = function removeFromDOM_invoke()
+ {
+ this.DOMNode.parentNode.removeChild(this.DOMNode);
+ }
+
+ this.getID = function removeFromDOM_getID()
+ {
+ return prettyName(aNodeOrID) + " remove from DOM.";
+ }
+
+ if (aTargetsFunc && (eventTypes & kHideEvent))
+ this.setTargets(kHideEvent, aTargetsFunc.call(null, this.DOMNode));
+
+ if (aReorderTargetFunc && (eventTypes & kReorderEvent))
+ this.setTarget(kReorderEvent,
+ aReorderTargetFunc.call(null, this.DOMNode));
+ }
+
+ /**
+ * Clone the node and replace the original node by cloned one.
+ */
+ function cloneAndReplaceInDOM(aNodeOrID)
+ {
+ this.__proto__ = new mutateA11yTree(aNodeOrID, kHideAndShowEvents,
+ false);
+
+ this.invoke = function cloneAndReplaceInDOM_invoke()
+ {
+ this.DOMNode.parentNode.replaceChild(this.newElm, this.DOMNode);
+ }
+
+ this.getID = function cloneAndReplaceInDOM_getID()
+ {
+ return aNodeOrID + " clone and replace in DOM.";
+ }
+
+ this.newElm = this.DOMNode.cloneNode(true);
+ this.newElm.removeAttribute('id');
+ this.setTarget(kShowEvent, this.newElm);
+ }
+
+ /**
+ * Trigger content insertion (flush layout), removal and insertion of
+ * the same element for the same parent.
+ */
+ function test1(aContainerID)
+ {
+ this.divNode = document.createElement("div");
+ this.divNode.setAttribute("id", "div-test1");
+ this.containerNode = getNode(aContainerID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.divNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function test1_invoke()
+ {
+ this.containerNode.appendChild(this.divNode);
+ getComputedStyle(this.divNode, "").color;
+ this.containerNode.removeChild(this.divNode);
+ this.containerNode.appendChild(this.divNode);
+ }
+
+ this.getID = function test1_getID()
+ {
+ return "fuzzy test #1: content insertion (flush layout), removal and" +
+ "reinsertion";
+ }
+ }
+
+ /**
+ * Trigger content insertion (flush layout), removal and insertion of
+ * the same element for the different parents.
+ */
+ function test2(aContainerID, aTmpContainerID)
+ {
+ this.divNode = document.createElement("div");
+ this.divNode.setAttribute("id", "div-test2");
+ this.containerNode = getNode(aContainerID);
+ this.tmpContainerNode = getNode(aTmpContainerID);
+ this.container = getAccessible(this.containerNode);
+ this.tmpContainer = getAccessible(this.tmpContainerNode);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, this.divNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_REORDER, this.tmpContainerNode)
+ ];
+
+ this.invoke = function test2_invoke()
+ {
+ this.tmpContainerNode.appendChild(this.divNode);
+ getComputedStyle(this.divNode, "").color;
+ this.tmpContainerNode.removeChild(this.divNode);
+ this.containerNode.appendChild(this.divNode);
+ }
+
+ this.getID = function test2_getID()
+ {
+ return "fuzzy test #2: content insertion (flush layout), removal and" +
+ "reinsertion under another container";
+ }
+ }
+
+ /**
+ * Content insertion (flush layout) and then removal (nothing was changed).
+ */
+ function test3(aContainerID)
+ {
+ this.divNode = document.createElement("div");
+ this.divNode.setAttribute("id", "div-test3");
+ this.containerNode = getNode(aContainerID);
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_SHOW, this.divNode),
+ new invokerChecker(EVENT_HIDE, this.divNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function test3_invoke()
+ {
+ this.containerNode.appendChild(this.divNode);
+ getComputedStyle(this.divNode, "").color;
+ this.containerNode.removeChild(this.divNode);
+ }
+
+ this.getID = function test3_getID()
+ {
+ return "fuzzy test #3: content insertion (flush layout) and removal";
+ }
+ }
+
+ function insertReferredElm(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.firstChild; }, this.containerNode),
+ new invokerChecker(EVENT_SHOW, function(aNode) { return aNode.lastChild; }, this.containerNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function insertReferredElm_invoke()
+ {
+ this.containerNode.innerHTML =
+ "<span id='insertReferredElms_span'></span><input aria-labelledby='insertReferredElms_span'>";
+ }
+
+ this.getID = function insertReferredElm_getID()
+ {
+ return "insert inaccessible element and then insert referring element to make it accessible";
+ }
+ }
+
+ function showHiddenParentOfVisibleChild()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getNode("c4_child")),
+ new invokerChecker(EVENT_SHOW, getNode("c4_middle")),
+ new invokerChecker(EVENT_REORDER, getNode("c4"))
+ ];
+
+ this.invoke = function showHiddenParentOfVisibleChild_invoke()
+ {
+ getNode("c4_middle").style.visibility = 'visible';
+ }
+
+ this.getID = function showHiddenParentOfVisibleChild_getID()
+ {
+ return "show hidden parent of visible child";
+ }
+ }
+
+ function hideNDestroyDoc()
+ {
+ this.txt = null;
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, function() { return this.txt; }.bind(this))
+ ];
+
+ this.invoke = function hideNDestroyDoc_invoke()
+ {
+ this.txt = getAccessible('c5').firstChild.firstChild;
+ this.txt.DOMNode.parentNode.removeChild(this.txt.DOMNode);
+ }
+
+ this.check = function hideNDestroyDoc_check()
+ {
+ getNode('c5').parentNode.removeChild(getNode('c5'));
+ }
+
+ this.getID = function hideNDestroyDoc_getID()
+ {
+ return "remove text node and destroy a document on hide event";
+ }
+ }
+
+ function hideHideNDestroyDoc()
+ {
+ this.target = null;
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, function() { return this.target; }.bind(this))
+ ];
+
+ this.invoke = function hideHideNDestroyDoc_invoke()
+ {
+ var doc = getAccessible('c6').firstChild;
+ var l1 = doc.firstChild;
+ this.target = l1.firstChild;
+ var l2 = doc.lastChild;
+ l1.DOMNode.removeChild(l1.DOMNode.firstChild);
+ l2.DOMNode.removeChild(l2.DOMNode.firstChild);
+ }
+
+ this.check = function hideHideNDestroyDoc_check()
+ {
+ getNode('c6').parentNode.removeChild(getNode('c6'));
+ }
+
+ this.getID = function hideHideNDestroyDoc_getID()
+ {
+ return "remove text nodes (2 events in the queue) and destroy a document on first hide event";
+ }
+ }
+
+ /**
+ * Target getters.
+ */
+ function getFirstChild(aNode)
+ {
+ return [aNode.firstChild];
+ }
+ function getLastChild(aNode)
+ {
+ return [aNode.lastChild];
+ }
+
+ function getNEnsureFirstChild(aNode)
+ {
+ var node = aNode.firstChild;
+ getAccessible(node);
+ return [node];
+ }
+
+ function getNEnsureChildren(aNode)
+ {
+ var children = [];
+ var node = aNode.firstChild;
+ do {
+ children.push(node);
+ getAccessible(node);
+ node = node.nextSibling;
+ } while (node);
+
+ return children;
+ }
+
+ function getParent(aNode)
+ {
+ return aNode.parentNode;
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+ //enableLogging("events,verbose");
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Show/hide events by changing of display style of accessible DOM node
+ // from 'inline' to 'none', 'none' to 'inline'.
+ var id = "link1";
+ getAccessible(id); // ensure accessible is created
+ gQueue.push(new changeStyle(id, "display", "none", kHideEvents));
+ gQueue.push(new changeStyle(id, "display", "inline", kShowEvents));
+
+ // Show/hide events by changing of visibility style of accessible DOM node
+ // from 'visible' to 'hidden', 'hidden' to 'visible'.
+ var id = "link2";
+ getAccessible(id);
+ gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents));
+ gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
+
+ // Show/hide events by changing of display style of accessible DOM node
+ // from 'inline' to 'block', 'block' to 'inline'.
+ var id = "link3";
+ getAccessible(id); // ensure accessible is created
+ gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents));
+ gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents));
+
+ // Show/hide events by changing of visibility style of accessible DOM node
+ // from 'collapse' to 'visible', 'visible' to 'collapse'.
+ var id = "link4";
+ gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents));
+ gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents));
+
+ // Show/hide events by adding new accessible DOM node and removing old one.
+ var id = "link5";
+ gQueue.push(new cloneAndAppendToDOM(id));
+ gQueue.push(new removeFromDOM(id));
+
+ // No show/hide events by adding new not accessible DOM node and removing
+ // old one, no reorder event for their parent.
+ var id = "child1";
+ gQueue.push(new cloneAndAppendToDOM(id, kNoEvents));
+ gQueue.push(new removeFromDOM(id, kNoEvents));
+
+ // Show/hide events by adding new accessible DOM node and removing
+ // old one, there is reorder event for their parent.
+ var id = "child2";
+ gQueue.push(new cloneAndAppendToDOM(id));
+ gQueue.push(new removeFromDOM(id));
+
+ // Show/hide events by adding new DOM node containing accessible DOM and
+ // removing old one, there is reorder event for their parent.
+ var id = "child3";
+ gQueue.push(new cloneAndAppendToDOM(id, kShowEvents, getFirstChild,
+ getParent));
+
+ // Hide event for accessible child of unaccessible removed DOM node and
+ // reorder event for its parent.
+ gQueue.push(new removeFromDOM(id, kHideEvents,
+ getNEnsureFirstChild, getParent));
+
+ // Hide events for accessible children of unaccessible removed DOM node
+ // and reorder event for its parent.
+ gQueue.push(new removeFromDOM("child4", kHideEvents,
+ getNEnsureChildren, getParent));
+
+ // Show/hide events by creating new accessible DOM node and replacing
+ // old one.
+ getAccessible("link6"); // ensure accessible is created
+ gQueue.push(new cloneAndReplaceInDOM("link6"));
+
+ // Show/hide events by changing class name on the parent node.
+ gQueue.push(new changeClass("container2", "link7", "", kShowEvents));
+ gQueue.push(new changeClass("container2", "link7", "displayNone",
+ kHideEvents));
+
+ gQueue.push(new changeClass("container3", "link8", "", kShowEvents));
+ gQueue.push(new changeClass("container3", "link8", "visibilityHidden",
+ kHideEvents));
+
+ gQueue.push(new test1("testContainer"));
+ gQueue.push(new test2("testContainer", "testContainer2"));
+ gQueue.push(new test2("testContainer", "testNestedContainer"));
+ gQueue.push(new test3("testContainer"));
+ gQueue.push(new insertReferredElm("testContainer3"));
+ gQueue.push(new showHiddenParentOfVisibleChild());
+
+ gQueue.push(new hideNDestroyDoc());
+ gQueue.push(new hideHideNDestroyDoc());
+ 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=469985"
+ title=" turn the test from bug 354745 into mochitest">
+ Mozilla Bug 469985</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=472662"
+ title="no reorder event when html:link display property is changed from 'none' to 'inline'">
+ Mozilla Bug 472662</a>
+ <a target="_blank"
+ title="Rework accessible tree update code"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275">
+ Mozilla Bug 570275</a>
+ <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>
+ <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="eventdump"></div>
+
+ <div id="testContainer">
+ <a id="link1" href="http://www.google.com">Link #1</a>
+ <a id="link2" href="http://www.google.com">Link #2</a>
+ <a id="link3" href="http://www.google.com">Link #3</a>
+ <a id="link4" href="http://www.google.com" style="visibility:collapse">Link #4</a>
+ <a id="link5" href="http://www.google.com">Link #5</a>
+
+ <div id="container" role="list">
+ <span id="child1"></span>
+ <span id="child2" role="listitem"></span>
+ <span id="child3"><span role="listitem"></span></span>
+ <span id="child4"><span id="child4_1" role="listitem"></span><span id="child4_2" role="listitem"></span></span>
+ </div>
+
+ <a id="link6" href="http://www.google.com">Link #6</a>
+
+ <div id="container2" class="displayNone"><a id="link7">Link #7</a></div>
+ <div id="container3" class="visibilityHidden"><a id="link8">Link #8</a></div>
+ <div id="testNestedContainer"></div>
+ </div>
+ <div id="testContainer2"></div>
+ <div id="testContainer3"></div>
+
+ <div id="c4">
+ <div style="visibility:hidden" id="c4_middle">
+ <div style="visibility:visible" id="c4_child"></div>
+ </div>
+
+ <iframe id="c5" src="data:text/html,hey"></iframe>
+ <iframe id="c6" src="data:text/html,<label>l</label><label>l</label>"></iframe>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_mutation.xhtml b/accessible/tests/mochitest/events/test_mutation.xhtml
new file mode 100644
index 000000000..e1aabe612
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_mutation.xhtml
@@ -0,0 +1,97 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <bindings xmlns="http://www.mozilla.org/xbl" >
+ <binding id="button">
+ <content>
+ <button xmlns="http://www.w3.org/1999/xhtml">a button</button>
+ </content>
+ </binding>
+ </bindings>
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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">
+
+ /**
+ * Insert a not accessible bound element containing an accessible element
+ * in anonymous content.
+ */
+ function insertBinding(aContainerID)
+ {
+ this.containerNode = getNode(aContainerID);
+
+ function getButtonFromBinding(aNode)
+ {
+ try { return document.getAnonymousNodes(aNode.firstChild)[0]; }
+ catch (e) { return null; }
+ }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SHOW, getButtonFromBinding, this.containerNode),
+ new invokerChecker(EVENT_REORDER, this.containerNode)
+ ];
+
+ this.invoke = function insertBinding_invoke()
+ {
+ var span = document.createElement("span");
+ span.setAttribute("style", "-moz-binding:url(#button)");
+ this.containerNode.appendChild(span);
+ }
+
+ this.getID = function insertBinding_getID()
+ {
+ return "insert button binding";
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new insertBinding("testContainer"));
+
+ 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=646369"
+ title="UpdateTree should rely on accessible tree walker rather than DOM tree traversal">
+ Mozilla Bug 646369</a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <div id="testContainer"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_namechange.html b/accessible/tests/mochitest/events/test_namechange.html
new file mode 100644
index 000000000..935d865e9
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_namechange.html
@@ -0,0 +1,123 @@
+<html>
+
+<head>
+ <title>Accessible name change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function setAttr(aID, aAttr, aValue, aChecker)
+ {
+ this.eventSeq = [ aChecker ];
+ this.invoke = function setAttr_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function setAttr_getID()
+ {
+ return "set attr '" + aAttr + "', value '" + aValue + "'";
+ }
+ }
+
+ /**
+ * No name change on an accessible, because the accessible is recreated.
+ */
+ function setAttr_recreate(aID, aAttr, aValue)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aID)),
+ new invokerChecker(EVENT_SHOW, aID)
+ ];
+ this.invoke = function setAttr_recreate_invoke()
+ {
+ todo(false, "No accessible recreation should happen, just name change event");
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function setAttr_recreate_getID()
+ {
+ return "set attr '" + aAttr + "', value '" + aValue + "'";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new setAttr("tst1", "aria-label", "hi",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "aria-labelledby", "display",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "alt", "alt",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+ gQueue.push(new setAttr("tst1", "title", "title",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+
+ gQueue.push(new setAttr("tst2", "aria-labelledby", "display",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst2")));
+ gQueue.push(new setAttr("tst2", "alt", "alt",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
+ gQueue.push(new setAttr("tst2", "title", "title",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
+
+ gQueue.push(new setAttr_recreate("tst3", "alt", "alt"));
+ gQueue.push(new setAttr("tst3", "title", "title",
+ new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst3")));
+
+ gQueue.push(new setAttr("tst4", "title", "title",
+ new invokerChecker(EVENT_NAME_CHANGE, "tst4")));
+
+ 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=991969"
+ title="Event not fired when description changes">
+ Bug 991969
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <img id="tst1">
+ <img id="tst2">
+ <img id="tst3">
+ <img id="tst4">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_namechange.xul b/accessible/tests/mochitest/events/test_namechange.xul
new file mode 100644
index 000000000..9d688585c
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_namechange.xul
@@ -0,0 +1,92 @@
+<?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">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.js"/>
+
+ <script type="application/javascript"
+ src="../common.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ /**
+ * Check name changed a11y event.
+ */
+ function nameChangeChecker(aMsg, aID)
+ {
+ this.type = EVENT_NAME_CHANGE;
+
+ function targetGetter()
+ {
+ return getAccessible(aID);
+ }
+ Object.defineProperty(this, "target", { get: targetGetter });
+
+ this.getID = function getID()
+ {
+ return aMsg + " name changed";
+ }
+ }
+
+ function changeRichListItemChild()
+ {
+ this.invoke = function changeRichListItemChild_invoke()
+ {
+ getNode('childcontent').setAttribute('value', 'Changed.');
+ }
+
+ this.eventSeq =
+ [
+ new nameChangeChecker("changeRichListItemChild: ", "listitem")
+ ];
+
+ this.getID = function changeRichListItemChild_getID()
+ {
+ return "changeRichListItemChild";
+ }
+ }
+
+ function doTest()
+ {
+ var queue = new eventQueue();
+ queue.push(new changeRichListItemChild());
+ queue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(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=986054"
+ title="Propagate name change events">
+ Mozilla Bug 986054
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <richlistbox>
+ <richlistitem id="listitem">
+ <description id="childcontent" value="This will be changed."/>
+ </richlistitem>
+ </richlistbox>
+ </vbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_scroll.xul b/accessible/tests/mochitest/events/test_scroll.xul
new file mode 100644
index 000000000..e33161376
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_scroll.xul
@@ -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">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.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"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ function getAnchorJumpInTabDocument(aTabIdx)
+ {
+ var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
+ return tabDoc.querySelector("a[name='link1']");
+ }
+
+ function loadTab(aURL)
+ {
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new asyncInvokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
+ ];
+
+ this.invoke = function loadTab_invoke()
+ {
+ tabBrowser().loadURI(aURL);
+ }
+
+ this.getID = function loadTab_getID()
+ {
+ return "load tab: " + aURL;
+ }
+ }
+
+ function loadTabInBackground(aURL)
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
+ ];
+
+ this.unexpectedEventSeq = [
+ new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument, 1)
+ ];
+
+ this.invoke = function loadTabInBackground_invoke()
+ {
+ tabBrowser().loadOneTab(aURL, null, "", null, true);
+ }
+
+ this.getID = function loadTabInBackground_getID()
+ {
+ return "load tab in background: " + aURL;
+ }
+ }
+
+ function switchToBackgroundTab()
+ {
+ this.eventSeq = [
+ new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
+ ];
+
+ this.invoke = function switchToBackgroundTab_invoke()
+ {
+ tabBrowser().selectTabAtIndex(1);
+ }
+
+ this.getID = function switchToBackgroundTab_getID()
+ {
+ return "switch to background tab";
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ var url = "http://mochi.test:8888/a11y/accessible/tests/mochitest/events/scroll.html#link1";
+ gQueue.push(new loadTab(url));
+ gQueue.push(new loadTabInBackground(url));
+ gQueue.push(new switchToBackgroundTab());
+ 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=691734"
+ title="Make sure scrolling start event is fired when document receive focus">
+ Mozilla Bug 691734
+ </a>
+
+ <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/events/test_scroll_caret.xul b/accessible/tests/mochitest/events/test_scroll_caret.xul
new file mode 100644
index 000000000..57e27747f
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_scroll_caret.xul
@@ -0,0 +1,91 @@
+<?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">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/chrome-harness.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"
+ src="../browser.js"></script>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Tests
+
+ function getAnchorJumpInTabDocument(aTabIdx)
+ {
+ var tabDoc = aTabIdx ? tabDocumentAt(aTabIdx) : currentTabDocument();
+ return tabDoc.querySelector("h1[id='heading_1']");
+ }
+
+ function loadTab(aURL)
+ {
+ this.eventSeq = [
+ new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument),
+ new asyncCaretMoveChecker(0, getAnchorJumpInTabDocument)
+ ];
+
+ this.invoke = function loadTab_invoke()
+ {
+ tabBrowser().loadURI(aURL);
+ }
+
+ this.getID = function loadTab_getID()
+ {
+ return "load tab: " + aURL;
+ }
+ }
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ var gQueue = null;
+ function doTest()
+ {
+ gQueue = new eventQueue();
+
+ var url = "http://mochi.test:8888/a11y/accessible/tests/mochitest/events/scroll.html#heading_1";
+ gQueue.push(new loadTab(url));
+ 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=1056459"
+ title="Make sure caret move event is fired when document receive focus">
+ Mozilla Bug 1056459
+ </a>
+
+ <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/events/test_selection.html b/accessible/tests/mochitest/events/test_selection.html
new file mode 100644
index 000000000..de25fedc3
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection.html
@@ -0,0 +1,118 @@
+<html>
+
+<head>
+ <title>Accessible selection event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // open combobox
+ gQueue.push(new synthClick("combobox",
+ new invokerChecker(EVENT_FOCUS, "cb1_item1")));
+ gQueue.push(new synthDownKey("cb1_item1",
+ selChangeSeq("cb1_item1", "cb1_item2")));
+
+ // closed combobox
+ gQueue.push(new synthEscapeKey("combobox",
+ new invokerChecker(EVENT_FOCUS, "combobox")));
+ gQueue.push(new synthDownKey("cb1_item2",
+ selChangeSeq("cb1_item2", "cb1_item3")));
+
+ // listbox
+ gQueue.push(new synthClick("lb1_item1",
+ new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+ gQueue.push(new synthDownKey("lb1_item1",
+ selChangeSeq("lb1_item1", "lb1_item2")));
+
+ // multiselectable listbox
+ gQueue.push(new synthClick("lb2_item1",
+ selChangeSeq(null, "lb2_item1")));
+ gQueue.push(new synthDownKey("lb2_item1",
+ selAddSeq("lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthUpKey("lb2_item2",
+ selRemoveSeq("lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+ selRemoveSeq("lb2_item1")));
+
+ 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=414302"
+ title="Incorrect selection events in HTML, XUL and ARIA">
+ Bug 414302
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=810268"
+ title="There's no way to know unselected item when selection in single selection was changed">
+ Bug 810268
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <select id="combobox">
+ <option id="cb1_item1" value="mushrooms">mushrooms
+ <option id="cb1_item2" value="greenpeppers">green peppers
+ <option id="cb1_item3" value="onions" id="onions">onions
+ <option id="cb1_item4" value="tomatoes">tomatoes
+ <option id="cb1_item5" value="olives">olives
+ </select>
+
+ <select id="listbox" size=5>
+ <option id="lb1_item1" value="mushrooms">mushrooms
+ <option id="lb1_item2" value="greenpeppers">green peppers
+ <option id="lb1_item3" value="onions" id="onions">onions
+ <option id="lb1_item4" value="tomatoes">tomatoes
+ <option id="lb1_item5" value="olives">olives
+ </select>
+
+ <p>Pizza</p>
+ <select id="listbox2" multiple size=5>
+ <option id="lb2_item1" value="mushrooms">mushrooms
+ <option id="lb2_item2" value="greenpeppers">green peppers
+ <option id="lb2_item3" value="onions" id="onions">onions
+ <option id="lb2_item4" value="tomatoes">tomatoes
+ <option id="lb2_item5" value="olives">olives
+ </select>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_selection.xul b/accessible/tests/mochitest/events/test_selection.xul
new file mode 100644
index 000000000..2b0c388ff
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection.xul
@@ -0,0 +1,255 @@
+<?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="Selection event tests">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/MochiKit/packed.js" />
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+ <script type="application/javascript"
+ 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="../states.js" />
+ <script type="application/javascript"
+ src="../events.js" />
+
+ <script type="application/javascript">
+ function advanceTab(aTabsID, aDirection, aNextTabID)
+ {
+ var eventSeq1 = [
+ new invokerChecker(EVENT_SELECTION, aNextTabID)
+ ]
+ defineScenario(this, eventSeq1);
+
+ var eventSeq2 = [
+ new invokerChecker(EVENT_HIDE, getAccessible(aNextTabID)),
+ new invokerChecker(EVENT_SHOW, aNextTabID)
+ ];
+ defineScenario(this, eventSeq2);
+
+ this.invoke = function advanceTab_invoke()
+ {
+ todo(false, "No accessible recreation should happen, just selection event");
+ getNode(aTabsID).advanceSelectedTab(aDirection, true);
+ }
+
+ this.getID = function synthFocus_getID()
+ {
+ return "advanceTab on " + prettyName(aTabsID) + " to " + prettyName(aNextTabID);
+ }
+ }
+
+ function select4FirstItems(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(1)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(2)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(3))
+ ];
+
+ this.invoke = function select4FirstItems_invoke()
+ {
+ synthesizeKey("VK_DOWN", { shiftKey: true }); // selects two items
+ synthesizeKey("VK_DOWN", { shiftKey: true });
+ synthesizeKey("VK_DOWN", { shiftKey: true });
+ }
+
+ this.getID = function select4FirstItems_getID()
+ {
+ return "select 4 first items for " + prettyName(aID);
+ }
+ }
+
+ function unselect4FirstItems(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(3)),
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(2)),
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(1)),
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0))
+ ];
+
+ this.invoke = function unselect4FirstItems_invoke()
+ {
+ synthesizeKey("VK_UP", { shiftKey: true });
+ synthesizeKey("VK_UP", { shiftKey: true });
+ synthesizeKey("VK_UP", { shiftKey: true });
+ synthesizeKey(" ", { ctrlKey: true }); // unselect first item
+ }
+
+ this.getID = function unselect4FirstItems_getID()
+ {
+ return "unselect 4 first items for " + prettyName(aID);
+ }
+ }
+
+ function selectAllItems(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+ ];
+
+ this.invoke = function selectAllItems_invoke()
+ {
+ synthesizeKey("VK_END", { shiftKey: true });
+ }
+
+ this.getID = function selectAllItems_getID()
+ {
+ return "select all items for " + prettyName(aID);
+ }
+ }
+
+ function unselectAllItemsButFirst(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_WITHIN, getAccessible(this.listboxNode))
+ ];
+
+ this.invoke = function unselectAllItemsButFirst_invoke()
+ {
+ synthesizeKey("VK_HOME", { shiftKey: true });
+ }
+
+ this.getID = function unselectAllItemsButFirst_getID()
+ {
+ return "unselect all items for " + prettyName(aID);
+ }
+ }
+
+ function unselectSelectItem(aID)
+ {
+ this.listboxNode = getNode(aID);
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION_REMOVE, this.listboxNode.getItemAtIndex(0)),
+ new invokerChecker(EVENT_SELECTION_ADD, this.listboxNode.getItemAtIndex(0))
+ ];
+
+ this.invoke = function unselectSelectItem_invoke()
+ {
+ synthesizeKey(" ", { ctrlKey: true }); // select item
+ synthesizeKey(" ", { ctrlKey: true }); // unselect item
+ }
+
+ this.getID = function unselectSelectItem_getID()
+ {
+ return "unselect and then select first item for " + prettyName(aID);
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ //enableLogging("events");
+ //gA11yEventDumpToConsole = true; // debuggin
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ //////////////////////////////////////////////////////////////////////////
+ // tabbox
+ gQueue.push(new advanceTab("tabs", 1, "tab3"));
+
+ //////////////////////////////////////////////////////////////////////////
+ // listbox
+ gQueue.push(new synthClick("lb1_item1",
+ new invokerChecker(EVENT_SELECTION, "lb1_item1")));
+ gQueue.push(new synthDownKey("lb1_item1",
+ new invokerChecker(EVENT_SELECTION, "lb1_item2")));
+
+ //////////////////////////////////////////////////////////////////////////
+ // multiselectable listbox
+ gQueue.push(new synthClick("lb2_item1",
+ new invokerChecker(EVENT_SELECTION, "lb2_item1")));
+ gQueue.push(new synthDownKey("lb2_item1",
+ new invokerChecker(EVENT_SELECTION_ADD, "lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthUpKey("lb2_item2",
+ new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item2"),
+ { shiftKey: true }));
+ gQueue.push(new synthKey("lb2_item1", " ", { ctrlKey: true },
+ new invokerChecker(EVENT_SELECTION_REMOVE, "lb2_item1")));
+
+ //////////////////////////////////////////////////////////////////////////
+ // selection event coalescence
+
+ // fire 4 selection_add events
+ gQueue.push(new select4FirstItems("listbox2"));
+ // fire 4 selection_remove events
+ gQueue.push(new unselect4FirstItems("listbox2"));
+ // fire selection_within event
+ gQueue.push(new selectAllItems("listbox2"));
+ // fire selection_within event
+ gQueue.push(new unselectAllItemsButFirst("listbox2"));
+ // fire selection_remove/add events
+ gQueue.push(new unselectSelectItem("listbox2"));
+
+ 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=414302"
+ title="Incorrect selection events in HTML, XUL and ARIA">
+ Mozilla Bug 414302
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <tabbox id="tabbox" selectedIndex="1">
+ <tabs id="tabs">
+ <tab id="tab1" label="tab1"/>
+ <tab id="tab2" label="tab2"/>
+ <tab id="tab3" label="tab3"/>
+ <tab id="tab4" label="tab4"/>
+ </tabs>
+ <tabpanels>
+ <tabpanel><!-- tabpanel First elements go here --></tabpanel>
+ <tabpanel><button id="b1" label="b1"/></tabpanel>
+ <tabpanel><button id="b2" label="b2"/></tabpanel>
+ <tabpanel></tabpanel>
+ </tabpanels>
+ </tabbox>
+
+ <listbox id="listbox">
+ <listitem id="lb1_item1" label="item1"/>
+ <listitem id="lb1_item2" label="item2"/>
+ </listbox>
+
+ <listbox id="listbox2" seltype="multiple">
+ <listitem id="lb2_item1" label="item1"/>
+ <listitem id="lb2_item2" label="item2"/>
+ <listitem id="lb2_item3" label="item3"/>
+ <listitem id="lb2_item4" label="item4"/>
+ <listitem id="lb2_item5" label="item5"/>
+ <listitem id="lb2_item6" label="item6"/>
+ <listitem id="lb2_item7" label="item7"/>
+ </listbox>
+
+ </hbox>
+</window>
diff --git a/accessible/tests/mochitest/events/test_selection_aria.html b/accessible/tests/mochitest/events/test_selection_aria.html
new file mode 100644
index 000000000..aabee46fd
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_selection_aria.html
@@ -0,0 +1,127 @@
+<html>
+
+<head>
+ <title>ARIA selection event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function selectItem(aSelectID, aItemID)
+ {
+ this.selectNode = getNode(aSelectID);
+ this.itemNode = getNode(aItemID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_SELECTION, aItemID)
+ ];
+
+ this.invoke = function selectItem_invoke() {
+ var itemNode = this.selectNode.querySelector("*[aria-selected='true']");
+ if (itemNode)
+ itemNode.removeAttribute("aria-selected");
+
+ this.itemNode.setAttribute("aria-selected", "true");
+ }
+
+ this.getID = function selectItem_getID()
+ {
+ return "select item " + prettyName(aItemID);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ gQueue.push(new selectItem("tablist", "tab1"));
+ gQueue.push(new selectItem("tablist", "tab2"));
+
+ gQueue.push(new selectItem("tree", "treeitem1"));
+ gQueue.push(new selectItem("tree", "treeitem1a"));
+ gQueue.push(new selectItem("tree", "treeitem1a1"));
+
+ gQueue.push(new selectItem("tree2", "tree2item1"));
+ gQueue.push(new selectItem("tree2", "tree2item1a"));
+ gQueue.push(new selectItem("tree2", "tree2item1a1"));
+
+ 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=569653"
+ title="Make selection events async">
+ Mozilla Bug 569653
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=804040"
+ title="Selection event not fired when selection of ARIA tab changes">
+ Mozilla Bug 804040
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div role="tablist" id="tablist">
+ <div role="tab" id="tab1">tab1</div>
+ <div role="tab" id="tab2">tab2</div>
+ </div>
+
+ <div id="tree" role="tree">
+ <div id="treeitem1" role="treeitem">Canada
+ <div id="treeitem1a" role="treeitem">- Ontario
+ <div id="treeitem1a1" role="treeitem">-- Toronto</div>
+ </div>
+ <div id="treeitem1b" role="treeitem">- Manitoba</div>
+ </div>
+ <div id="treeitem2" role="treeitem">Germany</div>
+ <div id="treeitem3" role="treeitem">Russia</div>
+ </div>
+
+ <div id="tree2" role="tree" aria-multiselectable="true">
+ <div id="tree2item1" role="treeitem">Canada
+ <div id="tree2item1a" role="treeitem">- Ontario
+ <div id="tree2item1a1" role="treeitem">-- Toronto</div>
+ </div>
+ <div id="tree2item1b" role="treeitem">- Manitoba</div>
+ </div>
+ <div id="tree2item2" role="treeitem">Germany</div>
+ <div id="tree2item3" role="treeitem">Russia</div>
+ </div>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_statechange.html b/accessible/tests/mochitest/events/test_statechange.html
new file mode 100644
index 000000000..f4557376b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_statechange.html
@@ -0,0 +1,287 @@
+<html>
+
+<head>
+ <title>Accessible state change event testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../role.js"></script>
+ <script type="application/javascript"
+ src="../states.js"></script>
+
+ <script type="application/javascript">
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ function makeEditableDoc(aDocNode, aIsEnabled)
+ {
+ this.DOMNode = aDocNode;
+
+ this.invoke = function editabledoc_invoke() {
+ // Note: this should fire an EVENT_STATE_CHANGE
+ this.DOMNode.designMode = 'on';
+ };
+
+ this.check = function editabledoc_check(aEvent) {
+
+ testStates(aDocNode, 0, EXT_STATE_EDITABLE);
+
+ var event = null;
+ try {
+ var event = aEvent.QueryInterface(nsIAccessibleStateChangeEvent);
+ } catch (e) {
+ ok(false, "State change event was expected");
+ }
+
+ if (!event) { return; }
+
+ ok(event.isExtraState, "Extra state change was expected");
+ is(event.state, EXT_STATE_EDITABLE, "Wrong state of statechange event");
+ ok(event.isEnabled, "Expected editable state to be enabled");
+ }
+
+ this.getID = function editabledoc_getID() {
+ return prettyName(aDocNode) + " editable state changed";
+ };
+ }
+
+ function invalidInput(aNodeOrID)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+
+ this.invoke = function invalidInput_invoke() {
+ // Note: this should fire an EVENT_STATE_CHANGE
+ this.DOMNode.value = "I am not an email";
+ };
+
+ this.check = function invalidInput_check() {
+ testStates(aNodeOrID, STATE_INVALID);
+ };
+
+ this.getID = function invalidInput_getID() {
+ return prettyName(aNodeOrID) + " became invalid";
+ };
+ }
+
+ function changeCheckInput(aID, aIsChecked)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new stateChangeChecker(STATE_CHECKED, false, aIsChecked, this.DOMNode)
+ ];
+
+ this.invoke = function changeCheckInput_invoke()
+ {
+ this.DOMNode.checked = aIsChecked;
+ }
+
+ this.getID = function changeCheckInput_getID()
+ {
+ return "change checked state to '" + aIsChecked + "' for " +
+ prettyName(aID);
+ }
+ }
+
+ function stateChangeOnFileInput(aID, aAttr, aValue,
+ aState, aIsExtraState, aIsEnabled)
+ {
+ this.fileControlNode = getNode(aID);
+ this.fileControl = getAccessible(this.fileControlNode);
+ this.browseButton = this.fileControl.firstChild;
+ // No state change events on the label.
+
+ this.invoke = function stateChangeOnFileInput_invoke()
+ {
+ this.fileControlNode.setAttribute(aAttr, aValue);
+ }
+
+ this.eventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.fileControl),
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, this.browseButton)
+ ];
+
+ this.getID = function stateChangeOnFileInput_getID()
+ {
+ return "inherited state change on file input on attribute '" + aAttr + "' change";
+ }
+ }
+
+ function dupeStateChange(aID, aAttr, aValue,
+ aState, aIsExtraState, aIsEnabled)
+ {
+ this.eventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, getNode(aID))
+ ];
+
+ this.invoke = function dupeStateChange_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, aValue);
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+
+ this.getID = function dupeStateChange_getID()
+ {
+ return "duped state change events";
+ }
+ }
+
+ function oppositeStateChange(aID, aAttr, aState, aIsExtraState)
+ {
+ this.eventSeq = [ ];
+ this.unexpectedEventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, false, getNode(aID)),
+ new stateChangeChecker(aState, aIsExtraState, true, getNode(aID))
+ ];
+
+ this.invoke = function oppositeStateChange_invoke()
+ {
+ getNode(aID).setAttribute(aAttr, "false");
+ getNode(aID).setAttribute(aAttr, "true");
+ }
+
+ this.getID = function oppositeStateChange_getID()
+ {
+ return "opposite state change events";
+ }
+ }
+
+ /**
+ * Change concomitant ARIA and native attribute at once.
+ */
+ function echoingStateChange(aID, aARIAAttr, aAttr, aValue,
+ aState, aIsExtraState, aIsEnabled)
+ {
+ this.eventSeq = [
+ new stateChangeChecker(aState, aIsExtraState, aIsEnabled, getNode(aID))
+ ];
+
+ this.invoke = function echoingStateChange_invoke()
+ {
+ if (aValue == null) {
+ getNode(aID).removeAttribute(aARIAAttr);
+ getNode(aID).removeAttribute(aAttr);
+
+ } else {
+ getNode(aID).setAttribute(aARIAAttr, aValue);
+ getNode(aID).setAttribute(aAttr, aValue);
+ }
+ }
+
+ this.getID = function echoingStateChange_getID()
+ {
+ return "enchoing ARIA and native attributes change";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ var gQueue = null;
+
+ // var gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true; // debug stuff
+
+ function doTests()
+ {
+ gQueue = new eventQueue(nsIAccessibleEvent.EVENT_STATE_CHANGE);
+
+ // Test delayed editable state change
+ var doc = document.getElementById("iframe").contentDocument;
+ gQueue.push(new makeEditableDoc(doc));
+
+ // invalid state change
+ gQueue.push(new invalidInput("email"));
+
+ // checked state change
+ gQueue.push(new changeCheckInput("checkbox", true));
+ gQueue.push(new changeCheckInput("checkbox", false));
+ gQueue.push(new changeCheckInput("radio", true));
+ gQueue.push(new changeCheckInput("radio", false));
+
+ // file input inherited state changes
+ gQueue.push(new stateChangeOnFileInput("file", "aria-busy", "true",
+ STATE_BUSY, false, true));
+ gQueue.push(new stateChangeOnFileInput("file", "aria-required", "true",
+ STATE_REQUIRED, false, true));
+ gQueue.push(new stateChangeOnFileInput("file", "aria-invalid", "true",
+ STATE_INVALID, false, true));
+
+ gQueue.push(new dupeStateChange("div", "aria-busy", "true",
+ STATE_BUSY, false, true));
+ gQueue.push(new oppositeStateChange("div", "aria-busy",
+ STATE_BUSY, false));
+
+ gQueue.push(new echoingStateChange("text1", "aria-disabled", "disabled", "true",
+ EXT_STATE_ENABLED, true, false));
+ gQueue.push(new echoingStateChange("text1", "aria-disabled", "disabled", null,
+ EXT_STATE_ENABLED, true, true));
+
+ 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=564471"
+ title="Make state change events async">
+ Bug 564471
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=555728"
+ title="Fire a11y event based on HTML5 constraint validation">
+ Bug 555728
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=699017"
+ title="File input control should be propogate states to descendants">
+ Bug 699017
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=788389"
+ title="Fire statechange event whenever checked state is changed not depending on focused state">
+ Bug 788389
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=926812"
+ title="State change event not fired when both disabled and aria-disabled are toggled">
+ Bug 926812
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="testContainer">
+ <iframe id="iframe"></iframe>
+ </div>
+
+ <input id="email" type='email'>
+
+ <input id="checkbox" type="checkbox">
+ <input id="radio" type="radio">
+
+ <input id="file" type="file">
+
+ <div id="div"></div>
+
+ <input id="text1">
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_text.html b/accessible/tests/mochitest/events/test_text.html
new file mode 100644
index 000000000..d992d6c64
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_text.html
@@ -0,0 +1,339 @@
+<html>
+
+<head>
+ <title>Accessible mutation events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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
+
+ /**
+ * Base text remove invoker and checker.
+ */
+ function textRemoveInvoker(aID, aStart, aEnd, aText)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, false)
+ ];
+ }
+
+ function textInsertInvoker(aID, aStart, aEnd, aText)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new textChangeChecker(aID, aStart, aEnd, aText, true)
+ ];
+ }
+
+ /**
+ * Remove inaccessible child node containing accessibles.
+ */
+ function removeChildSpan(aID)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, 0, 5, "33322");
+
+ this.invoke = function removeChildSpan_invoke()
+ {
+ // remove HTML span, a first child of the node
+ this.DOMNode.removeChild(this.DOMNode.firstChild);
+ }
+
+ this.getID = function removeChildSpan_getID()
+ {
+ return "Remove inaccessible span containing accessible nodes" + prettyName(aID);
+ }
+ }
+
+ /**
+ * Insert inaccessible child node containing accessibles.
+ */
+ function insertChildSpan(aID, aInsertAllTogether)
+ {
+ this.__proto__ = new textInsertInvoker(aID, 0, 5, "33322");
+
+ this.invoke = function insertChildSpan_invoke()
+ {
+ // <span><span>333</span><span>22</span></span>
+ if (aInsertAllTogether) {
+ var topSpan = document.createElement("span");
+ var fSpan = document.createElement("span");
+ fSpan.textContent = "333";
+ topSpan.appendChild(fSpan);
+ var sSpan = document.createElement("span");
+ sSpan.textContent = "22";
+ topSpan.appendChild(sSpan);
+
+ this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
+
+ } else {
+ var topSpan = document.createElement("span");
+ this.DOMNode.insertBefore(topSpan, this.DOMNode.childNodes[0]);
+
+ var fSpan = document.createElement("span");
+ fSpan.textContent = "333";
+ topSpan.appendChild(fSpan);
+
+ var sSpan = document.createElement("span");
+ sSpan.textContent = "22";
+ topSpan.appendChild(sSpan);
+ }
+ }
+
+ this.getID = function insertChildSpan_getID()
+ {
+ return "Insert inaccessible span containing accessibles" +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove child embedded accessible.
+ */
+ function removeChildDiv(aID)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, 5, 6, kEmbedChar);
+
+ this.invoke = function removeChildDiv_invoke()
+ {
+ var childDiv = this.DOMNode.childNodes[1];
+
+ // Ensure accessible is created to get text remove event when it's
+ // removed.
+ getAccessible(childDiv);
+
+ this.DOMNode.removeChild(childDiv);
+ }
+
+ this.getID = function removeChildDiv_getID()
+ {
+ return "Remove accessible div from the middle of text accessible " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Insert child embedded accessible.
+ */
+ function insertChildDiv(aID)
+ {
+ this.__proto__ = new textInsertInvoker(aID, 5, 6, kEmbedChar);
+
+ this.invoke = function insertChildDiv_invoke()
+ {
+ var childDiv = document.createElement("div");
+ this.DOMNode.insertBefore(childDiv, this.DOMNode.childNodes[1]);
+ }
+
+ this.getID = function insertChildDiv_getID()
+ {
+ return "Insert accessible div into the middle of text accessible " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove children from text container from first to last child or vice
+ * versa.
+ */
+ function removeChildren(aID, aLastToFirst, aStart, aEnd, aText)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
+
+ this.invoke = function removeChildren_invoke()
+ {
+ if (aLastToFirst) {
+ while (this.DOMNode.firstChild)
+ this.DOMNode.removeChild(this.DOMNode.lastChild);
+ } else {
+ while (this.DOMNode.firstChild)
+ this.DOMNode.removeChild(this.DOMNode.firstChild);
+ }
+ }
+
+ this.getID = function removeChildren_getID()
+ {
+ return "remove children of " + prettyName(aID) +
+ (aLastToFirst ? " from last to first" : " from first to last");
+ }
+ }
+
+ /**
+ * Remove text from HTML input.
+ */
+ function removeTextFromInput(aID, aStart, aEnd, aText)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
+
+ this.eventSeq.push(new invokerChecker(EVENT_TEXT_VALUE_CHANGE,
+ this.DOMNode));
+
+ this.invoke = function removeTextFromInput_invoke()
+ {
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ this.DOMNode.focus();
+ this.DOMNode.setSelectionRange(aStart, aEnd);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromInput_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+ }
+
+ /**
+ * Add text into HTML input.
+ */
+ function insertTextIntoInput(aID, aStart, aEnd, aText)
+ {
+ this.__proto__ = new textInsertInvoker(aID, aStart, aEnd, aText);
+
+ this.eventSeq.push(new invokerChecker(EVENT_TEXT_VALUE_CHANGE,
+ this.DOMNode));
+
+ this.invoke = function insertTextIntoInput_invoke()
+ {
+ this.DOMNode.focus();
+ synthesizeKey("a", {});
+ }
+
+ this.getID = function insertTextIntoInput_getID()
+ {
+ return "Insert text to " + aStart + " for " + prettyName(aID);
+ }
+ }
+
+ /**
+ * Remove text data from text node of editable area.
+ */
+ function removeTextFromEditable(aID, aStart, aEnd, aText, aTextNode)
+ {
+ this.__proto__ = new textRemoveInvoker(aID, aStart, aEnd, aText);
+
+ this.invoke = function removeTextFromEditable_invoke()
+ {
+ this.DOMNode.focus();
+
+ var selection = window.getSelection();
+ var range = document.createRange();
+ range.setStart(this.textNode, aStart);
+ range.setEnd(this.textNode, aEnd);
+ selection.addRange(range);
+
+ synthesizeKey("VK_DELETE", {});
+ }
+
+ this.getID = function removeTextFromEditable_getID()
+ {
+ return "Remove text from " + aStart + " to " + aEnd + " for " +
+ prettyName(aID);
+ }
+
+ this.textNode = getNode(aTextNode);
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+ gA11yEventDumpToConsole = true; // debugging
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ // Text remove event on inaccessible child HTML span removal containing
+ // accessible text nodes.
+ gQueue.push(new removeChildSpan("p"));
+ gQueue.push(new insertChildSpan("p"), true);
+ gQueue.push(new insertChildSpan("p"), false);
+
+ // Remove embedded character.
+ gQueue.push(new removeChildDiv("div"));
+ gQueue.push(new insertChildDiv("div"));
+
+ // Remove all children.
+ var text = kEmbedChar + "txt" + kEmbedChar;
+ gQueue.push(new removeChildren("div2", true, 0, 5, text));
+ gQueue.push(new removeChildren("div3", false, 0, 5, text));
+
+ // Text remove from text node within hypertext accessible.
+ gQueue.push(new removeTextFromInput("input", 1, 3, "al"));
+ gQueue.push(new insertTextIntoInput("input", 1, 2, "a"));
+
+ // bug 570691
+ todo(false, "Fix text change events from editable area, see bug 570691");
+ //var textNode = getNode("editable").firstChild;
+ //gQueue.push(new removeTextFromEditable("editable", 1, 3, "al", textNode));
+ //textNode = getNode("editable2").firstChild.firstChild;
+ //gQueue.push(new removeTextFromEditable("editable2", 1, 3, "al", textNode));
+
+ 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=566293"
+ title=" wrong length of text remove event when inaccessible node containing accessible nodes is removed">
+ Mozilla Bug 566293
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570710"
+ title="Avoid extra array traversal during text event creation">
+ Mozilla Bug 570710
+ </a><br>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=574003"
+ title="Coalesce text events on nodes removal">
+ Mozilla Bug 574003
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=575052"
+ title="Cache text offsets within hypertext accessible">
+ Mozilla Bug 575052
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"
+ title="Rework accessible tree update code">
+ Mozilla Bug 570275
+ </a>
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <p id="p"><span><span>333</span><span>22</span></span>1111</p>
+ <div id="div">hello<div>hello</div>hello</div>
+ <div id="div2"><div>txt</div>txt<div>txt</div></div>
+ <div id="div3"><div>txt</div>txt<div>txt</div></div>
+ <input id="input" value="value">
+ <div contentEditable="true" id="editable">value</div>
+ <div contentEditable="true" id="editable2"><span>value</span></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_text_alg.html b/accessible/tests/mochitest/events/test_text_alg.html
new file mode 100644
index 000000000..97428fb3b
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_text_alg.html
@@ -0,0 +1,249 @@
+<html>
+
+<head>
+ <title>Accessible text update algorithm testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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
+
+ const kRemoval = false;
+ const kInsertion = true;
+ const kUnexpected = true;
+
+ function changeText(aContainerID, aValue, aEventList)
+ {
+ this.containerNode = getNode(aContainerID);
+ this.textNode = this.containerNode.firstChild;
+ this.textData = this.textNode.data;
+
+ this.eventSeq = [ ];
+ this.unexpectedEventSeq = [ ];
+
+ for (var i = 0; i < aEventList.length; i++) {
+ var event = aEventList[i];
+
+ var isInserted = event[0];
+ var str = event[1];
+ var offset = event[2];
+ var checker = new textChangeChecker(this.containerNode, offset,
+ offset + str.length, str,
+ isInserted);
+
+ if (event[3] == kUnexpected)
+ this.unexpectedEventSeq.push(checker);
+ else
+ this.eventSeq.push(checker);
+ }
+
+ this.invoke = function changeText_invoke()
+ {
+ this.textNode.data = aValue;
+ }
+
+ this.getID = function changeText_getID()
+ {
+ return "change text '" + shortenString(this.textData) + "' -> '" +
+ shortenString(this.textNode.data) + "' for " +
+ prettyName(this.containerNode);
+ }
+ }
+
+ function expStr(x, doublings)
+ {
+ for (var i = 0; i < doublings; ++i)
+ x = x + x;
+ return x;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do tests
+
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ gQueue = new eventQueue();
+
+ //////////////////////////////////////////////////////////////////////////
+ // wqrema -> tqb: substitution coalesced with removal
+
+ var events = [
+ [ kRemoval, "w", 0 ], // wqrema -> qrema
+ [ kInsertion, "t", 0], // qrema -> tqrema
+ [ kRemoval, "rema", 2 ], // tqrema -> tq
+ [ kInsertion, "b", 2] // tq -> tqb
+ ];
+ gQueue.push(new changeText("p1", "tqb", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // b -> insa: substitution coalesced with insertion (complex substitution)
+
+ events = [
+ [ kRemoval, "b", 0 ], // b ->
+ [ kInsertion, "insa", 0] // -> insa
+ ];
+ gQueue.push(new changeText("p2", "insa", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abc -> def: coalesced substitutions
+
+ events = [
+ [ kRemoval, "abc", 0 ], // abc ->
+ [ kInsertion, "def", 0] // -> def
+ ];
+ gQueue.push(new changeText("p3", "def", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abcabc -> abcDEFabc: coalesced insertions
+
+ events = [
+ [ kInsertion, "DEF", 3] // abcabc -> abcDEFabc
+ ];
+ gQueue.push(new changeText("p4", "abcDEFabc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abc -> defabc: insertion into begin
+
+ events = [
+ [ kInsertion, "def", 0] // abc -> defabc
+ ];
+ gQueue.push(new changeText("p5", "defabc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abc -> abcdef: insertion into end
+
+ events = [
+ [ kInsertion, "def", 3] // abc -> abcdef
+ ];
+ gQueue.push(new changeText("p6", "abcdef", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // defabc -> abc: removal from begin
+
+ events = [
+ [ kRemoval, "def", 0] // defabc -> abc
+ ];
+ gQueue.push(new changeText("p7", "abc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abcdef -> abc: removal from the end
+
+ events = [
+ [ kRemoval, "def", 3] // abcdef -> abc
+ ];
+ gQueue.push(new changeText("p8", "abc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // abcDEFabc -> abcabc: coalesced removals
+
+ events = [
+ [ kRemoval, "DEF", 3] // abcDEFabc -> abcabc
+ ];
+ gQueue.push(new changeText("p9", "abcabc", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // !abcdef@ -> @axbcef!: insertion, deletion and substitutions
+
+ events = [
+ [ kRemoval, "!", 0 ], // !abcdef@ -> abcdef@
+ [ kInsertion, "@", 0], // abcdef@ -> @abcdef@
+ [ kInsertion, "x", 2 ], // @abcdef@ -> @axbcdef@
+ [ kRemoval, "d", 5], // @axbcdef@ -> @axbcef@
+ [ kRemoval, "@", 7 ], // @axbcef@ -> @axbcef
+ [ kInsertion, "!", 7 ], // @axbcef -> @axbcef!
+ ];
+ gQueue.push(new changeText("p10", "@axbcef!", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // meilenstein -> levenshtein: insertion, complex and simple substitutions
+
+ events = [
+ [ kRemoval, "m", 0 ], // meilenstein -> eilenstein
+ [ kInsertion, "l", 0], // eilenstein -> leilenstein
+ [ kRemoval, "il", 2 ], // leilenstein -> leenstein
+ [ kInsertion, "v", 2], // leenstein -> levenstein
+ [ kInsertion, "h", 6 ], // levenstein -> levenshtein
+ ];
+ gQueue.push(new changeText("p11", "levenshtein", events));
+
+ //////////////////////////////////////////////////////////////////////////
+ // long strings, remove/insert pair as the old string was replaced on
+ // new one
+
+ var longStr1 = expStr("x", 16);
+ var longStr2 = expStr("X", 16);
+
+ var newStr = "a" + longStr1 + "b", insStr = longStr1, rmStr = "";
+ events = [
+ [ kRemoval, rmStr, 1, kUnexpected ],
+ [ kInsertion, insStr, 1 ]
+ ];
+ gQueue.push(new changeText("p12", newStr, events));
+
+ newStr = "a" + longStr2 + "b", insStr = longStr2, rmStr = longStr1;
+ events = [
+ [ kRemoval, rmStr, 1 ],
+ [ kInsertion, insStr, 1]
+ ];
+ gQueue.push(new changeText("p12", newStr, events));
+
+ newStr = "ab", insStr = "", rmStr = longStr2;
+ events = [
+ [ kRemoval, rmStr, 1 ],
+ [ kInsertion, insStr, 1, kUnexpected ]
+ ];
+ gQueue.push(new changeText("p12", newStr, events));
+
+ 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=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>
+ <div id="eventdump"></div>
+
+ <p id="p1">wqrema</p>
+ <p id="p2">b</p>
+ <p id="p3">abc</p>
+ <p id="p4">abcabc</p>
+ <p id="p5">abc</p>
+ <p id="p6">abc</p>
+ <p id="p7">defabc</p>
+ <p id="p8">abcdef</p>
+ <p id="p9">abcDEFabc</p>
+ <p id="p10">!abcdef@</p>
+ <p id="p11">meilenstein</p>
+ <p id="p12">ab</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_textattrchange.html b/accessible/tests/mochitest/events/test_textattrchange.html
new file mode 100644
index 000000000..a69a8df15
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textattrchange.html
@@ -0,0 +1,115 @@
+<html>
+
+<head>
+ <title>Text attribute changed event for misspelled text</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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="../states.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+ <script type="application/javascript"
+ src="../attributes.js"></script>
+
+ <script type="application/javascript">
+
+ const nsIDOMNSEditableElement =
+ Components.interfaces.nsIDOMNSEditableElement;
+
+ Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm");
+
+ function spelledTextInvoker(aID)
+ {
+ this.DOMNode = getNode(aID);
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_TEXT_ATTRIBUTE_CHANGED, this.DOMNode)
+ ];
+
+ this.invoke = function spelledTextInvoker_invoke()
+ {
+ var editor = this.DOMNode.QueryInterface(nsIDOMNSEditableElement).editor;
+ var spellChecker = new InlineSpellChecker(editor);
+ spellChecker.enabled = true;
+
+ //var spellchecker = editor.getInlineSpellChecker(true);
+ //spellchecker.enableRealTimeSpell = true;
+
+ this.DOMNode.value = "valid text inalid tixt";
+ }
+
+ this.finalCheck = function spelledTextInvoker_finalCheck()
+ {
+ var defAttrs = buildDefaultTextAttrs(this.DOMNode, kInputFontSize,
+ kNormalFontWeight,
+ kInputFontFamily);
+ testDefaultTextAttrs(aID, defAttrs);
+
+ var attrs = { };
+ var misspelledAttrs = {
+ "invalid": "spelling"
+ };
+
+ testTextAttrs(aID, 0, attrs, defAttrs, 0, 11);
+ testTextAttrs(aID, 11, misspelledAttrs, defAttrs, 11, 17);
+ testTextAttrs(aID, 17, attrs, defAttrs, 17, 18);
+ testTextAttrs(aID, 18, misspelledAttrs, defAttrs, 18, 22);
+ }
+
+ this.getID = function spelledTextInvoker_getID()
+ {
+ return "text attribute change for misspelled text";
+ }
+ }
+
+ /**
+ * Do tests.
+ */
+ //gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ var gQueue = null;
+ function doTests()
+ {
+ // Synth focus before spellchecking turning on to make sure editor
+ // gets a time for initialization.
+
+ gQueue = new eventQueue();
+ gQueue.push(new synthFocus("input"));
+ gQueue.push(new spelledTextInvoker("input"));
+ 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=345759"
+ title="Implement text attributes">
+ Mozilla Bug 345759
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <input id="input"/>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_textselchange.html b/accessible/tests/mochitest/events/test_textselchange.html
new file mode 100644
index 000000000..92ac30423
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_textselchange.html
@@ -0,0 +1,86 @@
+<html>
+
+<head>
+ <title>Accessible text selection change events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+ <script type="application/javascript"
+ src="../common.js"></script>
+ <script type="application/javascript"
+ src="../text.js"></script>
+ <script type="application/javascript"
+ src="../events.js"></script>
+
+ <script type="application/javascript">
+ var gQueue = null;
+
+ // gA11yEventDumpID = "eventdump"; // debug stuff
+ //gA11yEventDumpToConsole = true;
+
+ function getOnclickSeq(aID)
+ {
+ return [
+ new caretMoveChecker(0, aID),
+ new unexpectedInvokerChecker(EVENT_TEXT_SELECTION_CHANGED, aID)
+ ];
+ }
+
+ function doTests()
+ {
+ // test caret move events and caret offsets
+ gQueue = new eventQueue();
+
+ gQueue.push(new synthClick("c1_p1", getOnclickSeq("c1_p1")));
+ gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 1), { shiftKey: true }));
+ gQueue.push(new synthDownKey("c1", new textSelectionChecker("c1", 0, 2), { shiftKey: true }));
+
+ gQueue.push(new synthClick("ta1", getOnclickSeq("ta1")));
+ gQueue.push(new synthRightKey("ta1",
+ new textSelectionChecker("ta1", 0, 1),
+ { shiftKey: true }));
+ gQueue.push(new synthLeftKey("ta1",
+ [new textSelectionChecker("ta1", 0, 0),
+ new caretMoveChecker(0, "ta1")]));
+
+ 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=762934"
+ title="Text selection change event has a wrong target when selection is spanned through several objects">
+ Bug 762934
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=956032"
+ title="Text selection change event missed when selected text becomes unselected">
+ Bug 956032
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+
+ <div id="c1" contentEditable="true">
+ <p id="c1_p1">paragraph</p>
+ <p id="c1_p2">paragraph</p>
+ </div>
+
+ <textarea id="ta1">Hello world</textarea>
+
+ <div id="eventdump"></div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/events/test_tree.xul b/accessible/tests/mochitest/events/test_tree.xul
new file mode 100644
index 000000000..797e7bf97
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_tree.xul
@@ -0,0 +1,348 @@
+<?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="DOM TreeRowCountChanged and a11y name change events.">
+
+ <script type="application/javascript"
+ 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="../events.js" />
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invoker's checkers
+
+ /**
+ * Check TreeRowCountChanged event.
+ */
+ function rowCountChangedChecker(aMsg, aIdx, aCount)
+ {
+ this.type = "TreeRowCountChanged";
+ this.target = gTree;
+ this.check = function check(aEvent)
+ {
+ var propBag = aEvent.detail.QueryInterface(Components.interfaces.nsIPropertyBag2);
+ var index = propBag.getPropertyAsInt32("index");
+ is(index, aIdx, "Wrong 'index' data of 'treeRowCountChanged' event.");
+
+ var count = propBag.getPropertyAsInt32("count");
+ is(count, aCount, "Wrong 'count' data of 'treeRowCountChanged' event.");
+ }
+ this.getID = function getID()
+ {
+ return aMsg + "TreeRowCountChanged";
+ }
+ }
+
+ /**
+ * Check TreeInvalidated event.
+ */
+ function treeInvalidatedChecker(aMsg, aStartRow, aEndRow, aStartCol, aEndCol)
+ {
+ this.type = "TreeInvalidated";
+ this.target = gTree;
+ this.check = function check(aEvent)
+ {
+ var propBag = aEvent.detail.QueryInterface(Components.interfaces.nsIPropertyBag2);
+ try {
+ var startRow = propBag.getPropertyAsInt32("startrow");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ startRow = null;
+ }
+ is(startRow, aStartRow,
+ "Wrong 'startrow' of 'treeInvalidated' event on " + aMsg);
+
+ try {
+ var endRow = propBag.getPropertyAsInt32("endrow");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ endRow = null;
+ }
+ is(endRow, aEndRow,
+ "Wrong 'endrow' of 'treeInvalidated' event on " + aMsg);
+
+ try {
+ var startCol = propBag.getPropertyAsInt32("startcolumn");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ startCol = null;
+ }
+ is(startCol, aStartCol,
+ "Wrong 'startcolumn' of 'treeInvalidated' event on " + aMsg);
+
+ try {
+ var endCol = propBag.getPropertyAsInt32("endcolumn");
+ } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
+ endCol = null;
+ }
+ is(endCol, aEndCol,
+ "Wrong 'endcolumn' of 'treeInvalidated' event on " + aMsg);
+ }
+ this.getID = function getID()
+ {
+ return "TreeInvalidated on " + aMsg;
+ }
+ }
+
+ /**
+ * Check name changed a11y event.
+ */
+ function nameChangeChecker(aMsg, aRow, aCol)
+ {
+ this.type = EVENT_NAME_CHANGE;
+
+ function targetGetter()
+ {
+ var acc = getAccessible(gTree);
+
+ var tableAcc = getAccessible(acc, [nsIAccessibleTable]);
+ return tableAcc.getCellAt(aRow, aCol);
+ }
+ Object.defineProperty(this, "target", { get: targetGetter });
+
+ this.getID = function getID()
+ {
+ return aMsg + "name changed";
+ }
+ }
+
+ /**
+ * Check name changed a11y event for a row.
+ */
+ function rowNameChangeChecker(aMsg, aRow)
+ {
+ this.type = EVENT_NAME_CHANGE;
+
+ function targetGetter()
+ {
+ var acc = getAccessible(gTree);
+ return acc.getChildAt(aRow + 1);
+ }
+ Object.defineProperty(this, "target", { get: targetGetter });
+
+ this.getID = function getID()
+ {
+ return aMsg + "name changed";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Invokers
+
+ /**
+ * Set tree view.
+ */
+ function setTreeView()
+ {
+ this.invoke = function setTreeView_invoke()
+ {
+ gTreeBox.view = gView;
+ }
+
+ this.getID = function setTreeView_getID() { return "set tree view"; }
+
+ this.eventSeq = [
+ new invokerChecker(EVENT_REORDER, gTree)
+ ];
+ };
+
+ /**
+ * Insert row at 0 index and checks TreeRowCountChanged and TreeInvalidated
+ * event.
+ */
+ function insertRow()
+ {
+ this.invoke = function insertRow_invoke()
+ {
+ gView.appendItem("last");
+ gTreeBox.rowCountChanged(0, 1);
+ }
+
+ this.eventSeq =
+ [
+ new rowCountChangedChecker("insertRow: ", 0, 1),
+ new treeInvalidatedChecker("insertRow", 0, 5, null, null)
+ ];
+
+ this.getID = function insertRow_getID()
+ {
+ return "insert row";
+ }
+ }
+
+ /**
+ * Invalidates first column and checks six name changed events for each
+ * treeitem plus TreeInvalidated event.
+ */
+ function invalidateColumn()
+ {
+ this.invoke = function invalidateColumn_invoke()
+ {
+ // Make sure accessible subtree of XUL tree is created otherwise no
+ // name change events for cell accessibles are emitted.
+ var tree = getAccessible(gTree);
+ var child = tree.firstChild;
+ var walkDown = true;
+ while (child != tree) {
+ if (walkDown) {
+ var grandChild = child.firstChild;
+ if (grandChild) {
+ child = grandChild;
+ continue;
+ }
+ }
+
+ var sibling = child.nextSibling;
+ if (sibling) {
+ child = sibling;
+ walkDown = true;
+ continue;
+ }
+
+ child = child.parent;
+ walkDown = false;
+ }
+
+ // Fire 'TreeInvalidated' event by InvalidateColumn()
+ var firstCol = gTree.columns.getFirstColumn();
+ for (var i = 0; i < gView.rowCount; i++)
+ gView.setCellText(i, firstCol, "hey " + String(i) + "x0");
+
+ gTreeBox.invalidateColumn(firstCol);
+ }
+
+ this.eventSeq =
+ [
+ new nameChangeChecker("invalidateColumn: ", 0, 0),
+ new nameChangeChecker("invalidateColumn: ", 1, 0),
+ new nameChangeChecker("invalidateColumn: ", 2, 0),
+ new nameChangeChecker("invalidateColumn: ", 3, 0),
+ new nameChangeChecker("invalidateColumn: ", 4, 0),
+ new nameChangeChecker("invalidateColumn: ", 5, 0),
+ new treeInvalidatedChecker("invalidateColumn", null, null, 0, 0)
+ ];
+
+ this.getID = function invalidateColumn_getID()
+ {
+ return "invalidate column";
+ }
+ }
+
+ /**
+ * Invalidates second row and checks name changed event for first treeitem
+ * (note, there are two name changed events on linux due to different
+ * accessible tree for xul:tree element) plus TreeInvalidated event.
+ */
+ function invalidateRow()
+ {
+ this.invoke = function invalidateRow_invoke()
+ {
+ // Fire 'TreeInvalidated' event by InvalidateRow()
+ var colCount = gTree.columns.count;
+ var column = gTree.columns.getFirstColumn();
+ while (column) {
+ gView.setCellText(1, column, "aloha 1x" + String(column.index));
+ column = column.getNext();
+ }
+
+ gTreeBox.invalidateRow(1);
+ }
+
+ this.eventSeq =
+ [
+ new nameChangeChecker("invalidateRow: ", 1, 0),
+ new nameChangeChecker("invalidateRow: ", 1, 1),
+ new rowNameChangeChecker("invalidateRow: ", 1),
+ new treeInvalidatedChecker("invalidateRow", 1, 1, null, null)
+ ];
+
+ this.getID = function invalidateRow_getID()
+ {
+ return "invalidate row";
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Test
+
+ var gTree = null;
+ var gTreeBox = null;
+ var gTreeView = null;
+ var gQueue = null;
+
+ // gA11yEventDumpID = "debug";
+ gA11yEventDumpToConsole = true; // debuggin
+
+ function doTest()
+ {
+ // Initialize the tree
+ gTree = document.getElementById("tree");
+ gTreeBox = gTree.treeBoxObject;
+ gView = new nsTableTreeView(5);
+
+ // Perform actions
+ gQueue = new eventQueue();
+
+ gQueue.push(new setTreeView());
+ gQueue.push(new insertRow());
+ gQueue.push(new invalidateColumn());
+ gQueue.push(new invalidateRow());
+
+ gQueue.invoke();
+ }
+
+ 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=368835"
+ title="Fire TreeViewChanged/TreeRowCountChanged events.">
+ Mozilla Bug 368835
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=308564"
+ title="No accessibility events when data in a tree row changes.">
+ Mozilla Bug 308564
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=739524"
+ title="replace TreeViewChanged DOM event on direct call from XUL tree.">
+ Mozilla Bug 739524
+ </a><br/>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=743568"
+ title="Thunderbird message list tree emitting incorrect focus signals after message deleted.">
+ Mozilla Bug 743568
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+ </body>
+
+ <vbox id="debug"/>
+ <tree id="tree" flex="1">
+ <treecols>
+ <treecol id="col1" flex="1" primary="true" label="column"/>
+ <treecol id="col2" flex="1" label="column 2"/>
+ </treecols>
+ <treechildren id="treechildren"/>
+ </tree>
+ </hbox>
+
+</window>
+
diff --git a/accessible/tests/mochitest/events/test_valuechange.html b/accessible/tests/mochitest/events/test_valuechange.html
new file mode 100644
index 000000000..3464ffdeb
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_valuechange.html
@@ -0,0 +1,255 @@
+<html>
+
+<head>
+ <title>Accessible value change events testing</title>
+
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript"
+ 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"
+ src="../value.js"></script>
+
+ <script type="application/javascript">
+ /**
+ * Do tests.
+ */
+ var gQueue = null;
+
+ // Value change invoker
+ function changeARIAValue(aNodeOrID, aValuenow, aValuetext)
+ {
+ this.DOMNode = getNode(aNodeOrID);
+ this.eventSeq = [ new invokerChecker(aValuetext ?
+ EVENT_TEXT_VALUE_CHANGE :
+ EVENT_VALUE_CHANGE, this.DOMNode)
+ ];
+
+ this.invoke = function changeARIAValue_invoke() {
+
+ // Note: this should not fire an EVENT_VALUE_CHANGE when aria-valuetext
+ // is not empty
+ if (aValuenow != undefined)
+ this.DOMNode.setAttribute("aria-valuenow", aValuenow);
+
+ // Note: this should always fire an EVENT_VALUE_CHANGE
+ if (aValuetext != undefined)
+ this.DOMNode.setAttribute("aria-valuetext", aValuetext);
+ }
+
+ this.check = function changeARIAValue_check() {
+ var acc = getAccessible(aNodeOrID, [nsIAccessibleValue]);
+ if (!acc)
+ return;
+
+ // Note: always test against valuetext first because the existence of
+ // aria-valuetext takes precedence over aria-valuenow in gecko.
+ is(acc.value, (aValuetext != undefined)? aValuetext : aValuenow,
+ "Wrong value of " + prettyName(aNodeOrID));
+ }
+
+ this.getID = function changeARIAValue_getID() {
+ return prettyName(aNodeOrID) + " value changed";
+ }
+ }
+
+ function changeValue(aID, aValue)
+ {
+ this.DOMNode = getNode(aID);
+ this.eventSeq = [new invokerChecker(EVENT_TEXT_VALUE_CHANGE,
+ this.DOMNode)
+ ];
+
+ this.invoke = function changeValue_invoke()
+ {
+ this.DOMNode.value = aValue;
+ }
+
+ this.check = function changeValue_check()
+ {
+ var acc = getAccessible(this.DOMNode);
+ is(acc.value, aValue, "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeValue_getID()
+ {
+ return prettyName(aID) + " value changed";
+ }
+ }
+
+ function changeProgressValue(aID, aValue)
+ {
+ this.DOMNode = getNode(aID);
+ this.eventSeq = [new invokerChecker(EVENT_VALUE_CHANGE, this.DOMNode)];
+
+ this.invoke = function changeProgressValue_invoke()
+ {
+ this.DOMNode.value = aValue;
+ }
+
+ this.check = function changeProgressValue_check()
+ {
+ var acc = getAccessible(this.DOMNode);
+ is(acc.value, aValue+"%", "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeProgressValue_getID()
+ {
+ return prettyName(aID) + " value changed";
+ }
+ }
+
+ function changeRangeValue(aID)
+ {
+ this.DOMNode = getNode(aID);
+ this.eventSeq = [new invokerChecker(EVENT_VALUE_CHANGE, this.DOMNode)];
+
+ this.invoke = function changeRangeValue_invoke()
+ {
+ synthesizeMouse(getNode(aID), 5, 5, { });
+ }
+
+ this.finalCheck = function changeRangeValue_finalCheck()
+ {
+ var acc = getAccessible(this.DOMNode);
+ is(acc.value, "0", "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeRangeValue_getID()
+ {
+ return prettyName(aID) + " range value changed";
+ }
+ }
+
+ function changeSelectValue(aID, aKey, aValue)
+ {
+ this.eventSeq =
+ [ new invokerChecker(EVENT_TEXT_VALUE_CHANGE, getAccessible(aID)) ];
+
+ this.invoke = function changeSelectValue_invoke()
+ {
+ getNode(aID).focus();
+ synthesizeKey(aKey, {}, window);
+ }
+
+ this.finalCheck = function changeSelectValue_finalCheck()
+ {
+ is(getAccessible(aID).value, aValue, "Wrong value for " + prettyName(aID));
+ }
+
+ this.getID = function changeSelectValue_getID()
+ {
+ return `${prettyName(aID)} closed select value change on '${aKey}'' key press`;
+ }
+ }
+
+ //enableLogging("DOMEvents");
+ //gA11yEventDumpToConsole = true;
+ function doTests()
+ {
+ // Test initial values
+ testValue("slider_vn", "5", 5, 0, 1000, 0);
+ testValue("slider_vnvt", "plain", 0, 0, 5, 0);
+ testValue("slider_vt", "hi", 0, 0, 3, 0);
+ testValue("scrollbar", "5", 5, 0, 1000, 0);
+ testValue("progress", "22%", 22, 0, 100, 0);
+ testValue("range", "6", 6, 0, 10, 1);
+
+ // Test value change events
+ gQueue = new eventQueue();
+
+ gQueue.push(new changeARIAValue("slider_vn", "6", undefined));
+ gQueue.push(new changeARIAValue("slider_vt", undefined, "hey!"));
+ gQueue.push(new changeARIAValue("slider_vnvt", "3", "sweet"));
+ gQueue.push(new changeARIAValue("scrollbar", "6", undefined));
+
+ gQueue.push(new changeValue("combobox", "hello"));
+
+ gQueue.push(new changeProgressValue("progress", "50"));
+ gQueue.push(new changeRangeValue("range"));
+
+ gQueue.push(new changeSelectValue("select", "VK_DOWN", "2nd"));
+ gQueue.push(new changeSelectValue("select", "3", "3rd"));
+
+ 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=478032"
+ title=" Fire delayed value changed event for aria-valuetext changes">
+ Mozilla Bug 478032
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=529289"
+ title="We dont expose new aria role 'scrollbar' and property aria-orientation">
+ Mozilla Bug 529289
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=559764"
+ title="Make HTML5 input@type=range element accessible">
+ Mozilla Bug 559764
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=703202"
+ title="ARIA comboboxes don't fire value change events">
+ Mozilla Bug 703202
+ </a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=761901"
+ title=" HTML5 progress accessible should fire value change event">
+ Mozilla Bug 761901
+ </a>
+
+
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <div id="eventdump"></div>
+
+ <!-- ARIA sliders -->
+ <div id="slider_vn" role="slider" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="1000">slider</div>
+
+ <div id="slider_vt" role="slider" aria-valuetext="hi"
+ aria-valuemin="0" aria-valuemax="3">greeting slider</div>
+
+ <div id="slider_vnvt" role="slider" aria-valuenow="0" aria-valuetext="plain"
+ aria-valuemin="0" aria-valuemax="5">sweetness slider</div>
+
+ <!-- ARIA scrollbar -->
+ <div id="scrollbar" role="scrollbar" aria-valuenow="5"
+ aria-valuemin="0" aria-valuemax="1000">slider</div>
+
+ <!-- ARIA combobox -->
+ <input id="combobox" role="combobox" aria-autocomplete="inline">
+
+ <!-- progress bar -->
+ <progress id="progress" value="22" max="100"></progress>
+
+ <!-- input@type="range" -->
+ <input type="range" id="range" min="0" max="10" value="6">
+
+ <select id="select">
+ <option>1st</option>
+ <option>2nd</option>
+ <option>3rd</option>
+ </select>
+</body>
+</html>