summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/jsat
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/mochitest/jsat')
-rw-r--r--accessible/tests/mochitest/jsat/a11y.ini30
-rw-r--r--accessible/tests/mochitest/jsat/doc_content_integration.html115
-rw-r--r--accessible/tests/mochitest/jsat/doc_content_text.html15
-rw-r--r--accessible/tests/mochitest/jsat/doc_traversal.html164
-rw-r--r--accessible/tests/mochitest/jsat/dom_helper.js209
-rw-r--r--accessible/tests/mochitest/jsat/gestures.json352
-rw-r--r--accessible/tests/mochitest/jsat/jsatcommon.js739
-rw-r--r--accessible/tests/mochitest/jsat/output.js114
-rw-r--r--accessible/tests/mochitest/jsat/test_alive.html81
-rw-r--r--accessible/tests/mochitest/jsat/test_content_integration.html343
-rw-r--r--accessible/tests/mochitest/jsat/test_content_text.html292
-rw-r--r--accessible/tests/mochitest/jsat/test_explicit_names.html191
-rw-r--r--accessible/tests/mochitest/jsat/test_gesture_tracker.html51
-rw-r--r--accessible/tests/mochitest/jsat/test_hints.html89
-rw-r--r--accessible/tests/mochitest/jsat/test_landmarks.html183
-rw-r--r--accessible/tests/mochitest/jsat/test_live_regions.html472
-rw-r--r--accessible/tests/mochitest/jsat/test_output.html673
-rw-r--r--accessible/tests/mochitest/jsat/test_output_mathml.html313
-rw-r--r--accessible/tests/mochitest/jsat/test_pointer_relay.html95
-rw-r--r--accessible/tests/mochitest/jsat/test_quicknav_modes.html107
-rw-r--r--accessible/tests/mochitest/jsat/test_tables.html579
-rw-r--r--accessible/tests/mochitest/jsat/test_traversal.html167
-rw-r--r--accessible/tests/mochitest/jsat/test_traversal_helper.html113
23 files changed, 5487 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/jsat/a11y.ini b/accessible/tests/mochitest/jsat/a11y.ini
new file mode 100644
index 000000000..5acc3df90
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/a11y.ini
@@ -0,0 +1,30 @@
+[DEFAULT]
+support-files =
+ dom_helper.js
+ gestures.json
+ jsatcommon.js
+ output.js
+ doc_traversal.html
+ doc_content_integration.html
+ doc_content_text.html
+ !/accessible/tests/mochitest/*.js
+ !/accessible/tests/mochitest/moz.png
+skip-if = (os == 'win' && (os_version == '5.1' || os_version == '5.2'))
+
+[test_alive.html]
+[test_content_integration.html]
+skip-if = buildapp == 'mulet'
+[test_content_text.html]
+skip-if = buildapp == 'mulet'
+[test_explicit_names.html]
+[test_gesture_tracker.html]
+[test_hints.html]
+[test_landmarks.html]
+[test_live_regions.html]
+[test_output_mathml.html]
+[test_output.html]
+[test_quicknav_modes.html]
+[test_tables.html]
+[test_pointer_relay.html]
+[test_traversal.html]
+[test_traversal_helper.html]
diff --git a/accessible/tests/mochitest/jsat/doc_content_integration.html b/accessible/tests/mochitest/jsat/doc_content_integration.html
new file mode 100644
index 000000000..d62c000cb
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/doc_content_integration.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Traversal Rule test document</title>
+ <meta charset="utf-8" />
+ <script>
+ var frameContents = '<html>' +
+ '<head><title>such app</title></head>' +
+ '<body>' +
+ '<h1>wow</h1>' +
+ '<ul>' +
+ '<li><label><input type="checkbox">many option</label></li>' +
+ '</ul>' +
+ '<label for="r">much range</label>' +
+ '<input min="0" max="10" value="5" type="range" id="r">' +
+ '</body>' +
+ '</html>';
+
+ function showAlert() {
+ document.getElementById('alert').hidden = false;
+ }
+
+ function hideAlert() {
+ document.getElementById('alert').hidden = true;
+ }
+
+ function ariaShowBack() {
+ document.getElementById('back').setAttribute('aria-hidden', false);
+ }
+
+ function ariaHideBack() {
+ document.getElementById('back').setAttribute('aria-hidden', true);
+ }
+
+ function ariaShowIframe() {
+ document.getElementById('iframe').setAttribute('aria-hidden', false);
+ }
+
+ function ariaHideIframe() {
+ document.getElementById('iframe').setAttribute('aria-hidden', true);
+ }
+
+ function renameFruit() {
+ document.getElementById('fruit').setAttribute('aria-label', 'banana');
+ }
+
+ function renameSlider() {
+ document.getElementById('slider').setAttribute(
+ 'aria-label', 'mover');
+ }
+
+ function changeSliderValue() {
+ document.getElementById('slider').setAttribute('aria-valuenow', '5');
+ document.getElementById('slider').setAttribute(
+ 'aria-valuetext', 'medium');
+ }
+
+ function toggleLight() {
+ var lightSwitch = document.getElementById('light');
+ lightSwitch.setAttribute('aria-checked',
+ lightSwitch.getAttribute('aria-checked') === 'true' ? 'false' : 'true');
+ }
+
+ </script>
+ <style>
+ #windows {
+ position: relative;
+ width: 320px;
+ height: 480px;
+ }
+
+ #windows > iframe {
+ z-index: 1;
+ }
+
+ #windows > div[role='dialog'] {
+ z-index: 2;
+ background-color: pink;
+ }
+
+ #windows > * {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ }
+
+ iframe {
+ width: 100%;
+ height: 100%;
+ }
+
+ </style>
+
+</head>
+<body>
+ <div>Phone status bar</div>
+ <div id="windows">
+ <button id="back">Back</button>
+ <div role="dialog" id="alert" hidden>
+ <h1>This is an alert!</h1>
+ <p>Do you agree?</p>
+ <button onclick="setTimeout(hideAlert, 500)">Yes</button>
+ <button onclick="hideAlert()">No</button>
+ </div>
+ <div id="appframe"></div>
+ </div>
+ <button id="home">Home</button>
+ <button id="fruit" aria-label="apple"></button>
+ <span id="light" role="switch" aria-label="Light" aria-checked="false" onclick="toggleLight()"></span>
+ <div id="live" aria-live="polite" aria-label="live">
+ <div id="slider" role="slider" aria-label="slider" aria-valuemin="0"
+ aria-valuemax="10" aria-valuenow="0"></div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/doc_content_text.html b/accessible/tests/mochitest/jsat/doc_content_text.html
new file mode 100644
index 000000000..4e73dc6e7
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/doc_content_text.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Text content test document</title>
+ <meta charset="utf-8" />
+ </head>
+ <body>
+ <p>These are my awards, Mother. From Army.
+ The seal is for marksmanship, and the gorilla is for sand racing.</p>
+ <p>You're a good guy, mon frere. That means brother in French.
+ I don't know how I know that. I took four years of Spanish.</p>
+ <textarea>Please refrain from Mayoneggs during this salmonella scare.</textarea>
+ <label>So we don't get dessert?</label><input type="text">
+ </body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/mochitest/jsat/doc_traversal.html b/accessible/tests/mochitest/jsat/doc_traversal.html
new file mode 100644
index 000000000..4c104b6b7
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Traversal Rule test document</title>
+ <meta charset="utf-8" />
+ <style>
+ .content:before {
+ content: "Content";
+ }
+ </style>
+</head>
+<body>
+ <header id="header-1">
+ <h3 id="heading-1">A small first heading</h3>
+ <form>
+ <label for="input-1-1">Name:</label>
+ <input id="input-1-1">
+ <label id="label-1-2">Favourite Ice Cream Flavour:<input id="input-1-2"></label>
+ <button id="button-1-1">Magic Button</button>
+ <label for="radio-1-1">Radios are old: </label>
+ <input id="radio-1-1" type="radio">
+ <label for="radio-1-2">Radios are new: </label>
+ <input id="radio-1-2" type="radio">
+ <label for="input-1-3">Password:</label>
+ <input id="input-1-3" type="password">
+ <label for="input-1-4">Unlucky number:</label>
+ <input id="input-1-4" type="tel">
+ <input id="button-1-2" type="button" value="Fun">
+ <label for="checkbox-1-1">Check me: </label>
+ <input id="checkbox-1-1" type="checkbox">
+ <select id="select-1-1">
+ <option>Value 1</option>
+ <option>Value 2</option>
+ <option>Value 3</option>
+ </select>
+ <select id="select-1-2" multiple="true">
+ <option>Value 1</option>
+ <option>Value 2</option>
+ <option>Value 3</option>
+ </select>
+ <label for="checkbox-1-2">Check me too: </label>
+ <input id="checkbox-1-2" type="checkbox">
+ <label for="checkbox-1-3">But not me: </label>
+ <input id="checkbox-1-3" type="checkbox" aria-hidden="true">
+ <label for="checkbox-1-4">Or me! </label>
+ <input id="checkbox-1-4" type="checkbox" style="visibility:hidden">
+ <select id="select-1-3" size="3">
+ <option>Value 1</option>
+ <option>Value 2</option>
+ <option>Value 3</option>
+ </select>
+ <label for="input-1-5">Electronic mailing address:</label>
+ <input id="input-1-5" type="email">
+ <input id="button-1-3" type="submit" value="Submit">
+ </form>
+ </header>
+ <main id="main-1">
+ <h2 id="heading-2">A larger second</h2>
+ <div id="heading-3" role="heading">ARIA is fun</div>
+ <input id="button-2-1" type="button" value="More Fun">
+ <div id="button-2-2" tabindex="0" role="button">ARIA fun</div>
+ <div id="button-2-3" tabindex="0" role="button" aria-pressed="false">My little togglebutton</div>
+ <div id="button-2-4" tabindex="0" role="spinbutton">ARIA fun</div>
+ </main>
+ <h1 id="heading-4" style="display:none">Invisible header</h1>
+ <dl id="list-1">
+ <dt id="listitem-1-1">Programming Language</dt>
+ <dd>A esoteric weapon wielded by only the most formidable warriors,
+ for its unrelenting strict power is unfathomable.</dd>
+ </dl>
+ <ul id="list-2" onclick="alert('hi');">
+ <li id="listitem-2-1">Lists of Programming Languages</li>
+ <li id="listitem-2-2">Lisp
+ <ol id="list-3">
+ <li id="listitem-3-1">Scheme</li>
+ <li id="listitem-3-2">Racket</li>
+ <li id="listitem-3-3">Clojure</li>
+ <li id="listitem-3-4"><strong>Standard</strong> Lisp</li>
+ <li id="listitem-3-5"><a id="link-0" href="#">Common</a> Lisp</li>
+ <li id="listitem-3-6"><input id="checkbox-1-5" type="checkbox"> LeLisp</li>
+ </ol>
+ </li>
+ <li id="listitem-2-3">JavaScript</li>
+ </ul>
+ <section>
+ <h6 id="heading-5">The last (visible) one!</h6>
+ <img id="image-1" src="http://example.com" alt="">
+ <img id="image-2" src="../moz.png" alt="stuff">
+ <div id="image-3" tabindex="0" role="img">Not actually an image</div>
+ </section>
+ <section>
+ <h4 id="heading-6" aria-hidden="true">Hidden header</h4>
+ <a id="link-1" href="http://www.mozilla.org">Link</a>
+ <a id="anchor-1">Words</a>
+ <a id="link-2" href="http://www.mozilla.org">Link the second</a>
+ <a id="anchor-2">Sentences</a>
+ <a id="link-3" href="http://www.example.com">Link the third</a>
+ </section>
+ <hr id="separator-1">
+ <h6 id="heading-6"></h6>
+ <table id="table-1">
+ <tr>
+ <td>3</td>
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>4</td>
+ <td>1</td>
+ </tr>
+ </table>
+ <section id="grid" role="grid">
+ <ol role="row">
+ <li role="presentation"></li>
+ <li role="columnheader" aria-label="Sunday">S</li>
+ <li role="columnheader">M</li>
+ </ol>
+ <ol role="row">
+ <li role="rowheader" aria-label="Week 1">1</li>
+ <li role="gridcell"><span>3</span><div></div></li>
+ <li role="gridcell"><span>4</span><div>7</div></li>
+ </ol>
+ <ol role="row">
+ <li role="rowheader">2</li>
+ <li role="gridcell"><span>5</span><div role="presentation">8</div></li>
+ <li id="gridcell4" role="gridcell">
+ <span>6</span><div aria-hidden="true"><div class="content"></div></div>
+ </li>
+ </ol>
+ </section>
+ <div id="separator-2" role="separator">Just an innocuous separator</div>
+ <table id="table-2">
+ <thead>
+ <tr>
+ <th>Dirty Words</th>
+ <th>Meaning</th>
+ </tr>
+ </thead>
+ <tfoot>
+ <tr>
+ <td>Mud</td>
+ <td>Wet Dirt</td>
+ </tr>
+ </tfoot>
+ <tbody>
+ <tr>
+ <td>Dirt</td>
+ <td>Messy Stuff</td>
+ </tr>
+ </tbody>
+ </table>
+ <footer id="footer-1">
+ <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+ <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
+ </footer>
+
+ <span id="switch-1" role="switch" aria-checked="false" aria-label="Light switch"></span>
+ <p>This is a MathML formula <math id="math-1" display="block">
+ <mfrac>
+ <mrow><mi>x</mi><mo>+</mo><mn>1</mn></mrow>
+ <msqrt><mn>3</mn><mo>/</mo><mn>4</mn></msqrt>
+ </mfrac>
+ </math> with some text after.</p>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/dom_helper.js b/accessible/tests/mochitest/jsat/dom_helper.js
new file mode 100644
index 000000000..c95d19dc1
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/dom_helper.js
@@ -0,0 +1,209 @@
+'use strict';
+
+/* global getMainChromeWindow, AccessFuTest, GestureSettings, GestureTracker,
+ SimpleTest, getBoundsForDOMElm, Point, Utils */
+/* exported loadJSON, eventMap */
+
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import('resource://gre/modules/Geometry.jsm');
+
+var win = getMainChromeWindow(window);
+
+/**
+ * Convert inch based point coordinates into pixels.
+ * @param {Array} aPoints Array of coordinates in inches.
+ * @return {Array} Array of coordinates in pixels.
+ */
+function convertPointCoordinates(aPoints) {
+ var dpi = Utils.dpi;
+ return aPoints.map(function convert(aPoint) {
+ return {
+ x: aPoint.x * dpi,
+ y: aPoint.y * dpi,
+ identifier: aPoint.identifier
+ };
+ });
+}
+
+/**
+ * For a given list of points calculate their coordinates in relation to the
+ * document body.
+ * @param {Array} aTouchPoints An array of objects of the following format: {
+ * base: {String}, // Id of an element to server as a base for the touch.
+ * x: {Number}, // An optional x offset from the base element's geometric
+ * // centre.
+ * y: {Number} // An optional y offset from the base element's geometric
+ * // centre.
+ * }
+ * @return {JSON} An array of {x, y} coordinations.
+ */
+function calculateTouchListCoordinates(aTouchPoints) {
+ var coords = [];
+ for (var i = 0, target = aTouchPoints[i]; i < aTouchPoints.length; ++i) {
+ var bounds = getBoundsForDOMElm(target.base);
+ var parentBounds = getBoundsForDOMElm('root');
+ var point = new Point(target.x || 0, target.y || 0);
+ point.scale(Utils.dpi);
+ point.add(bounds[0], bounds[1]);
+ point.add(bounds[2] / 2, bounds[3] / 2);
+ point.subtract(parentBounds[0], parentBounds[0]);
+ coords.push({
+ x: point.x,
+ y: point.y
+ });
+ }
+ return coords;
+}
+
+/**
+ * Send a touch event with specified touchPoints.
+ * @param {Array} aTouchPoints An array of points to be associated with
+ * touches.
+ * @param {String} aName A name of the touch event.
+ */
+function sendTouchEvent(aTouchPoints, aName) {
+ var touchList = sendTouchEvent.touchList;
+ if (aName === 'touchend') {
+ sendTouchEvent.touchList = null;
+ } else {
+ var coords = calculateTouchListCoordinates(aTouchPoints);
+ var touches = [];
+ for (var i = 0; i < coords.length; ++i) {
+ var {x, y} = coords[i];
+ var node = document.elementFromPoint(x, y);
+ var touch = document.createTouch(window, node, aName === 'touchstart' ?
+ 1 : touchList.item(i).identifier, x, y, x, y);
+ touches.push(touch);
+ }
+ touchList = document.createTouchList(touches);
+ sendTouchEvent.touchList = touchList;
+ }
+ var evt = document.createEvent('TouchEvent');
+ evt.initTouchEvent(aName, true, true, window, 0, false, false, false, false,
+ touchList, touchList, touchList);
+ document.dispatchEvent(evt);
+}
+
+sendTouchEvent.touchList = null;
+
+/**
+ * A map of event names to the functions that actually send them.
+ * @type {Object}
+ */
+var eventMap = {
+ touchstart: sendTouchEvent,
+ touchend: sendTouchEvent,
+ touchmove: sendTouchEvent
+};
+
+/**
+ * Attach a listener for the mozAccessFuGesture event that tests its
+ * type.
+ * @param {Array} aExpectedGestures A stack of expected event types.
+ * @param {String} aTitle Title of this sequence, if any.
+ * Note: the listener is removed once the stack reaches 0.
+ */
+function testMozAccessFuGesture(aExpectedGestures, aTitle) {
+ var types = aExpectedGestures;
+ function handleGesture(aEvent) {
+ if (aEvent.detail.type !== types[0].type) {
+ info('Got ' + aEvent.detail.type + ' waiting for ' + types[0].type);
+ // The is not the event of interest.
+ return;
+ }
+ is(!!aEvent.detail.edge, !!types[0].edge);
+ is(aEvent.detail.touches.length, types[0].fingers || 1,
+ 'failed to count fingers: ' + types[0].type);
+ ok(true, 'Received correct mozAccessFuGesture: ' +
+ JSON.stringify(types.shift()) + '. (' + aTitle + ')');
+ if (types.length === 0) {
+ win.removeEventListener('mozAccessFuGesture', handleGesture);
+ if (AccessFuTest.sequenceCleanup) {
+ AccessFuTest.sequenceCleanup();
+ }
+ AccessFuTest.nextTest();
+ }
+ }
+ win.addEventListener('mozAccessFuGesture', handleGesture);
+}
+
+/**
+ * Reset the thresholds and max delays that affect gesture rejection.
+ * @param {Number} aTimeStamp Gesture time stamp.
+ * @param {Boolean} aRemoveDwellThreshold An optional flag to reset dwell
+ * threshold.
+ * @param {Boolean} aRemoveSwipeMaxDuration An optional flag to reset swipe max
+ * duration.
+ */
+function setTimers(aTimeStamp, aRemoveDwellThreshold, aRemoveSwipeMaxDuration) {
+ if (!aRemoveDwellThreshold && !aRemoveSwipeMaxDuration) {
+ return;
+ }
+ if (aRemoveDwellThreshold) {
+ GestureSettings.dwellThreshold = 0;
+ }
+ if (aRemoveSwipeMaxDuration) {
+ GestureSettings.swipeMaxDuration = 0;
+ }
+ GestureTracker.current.clearTimer();
+ GestureTracker.current.startTimer(aTimeStamp);
+}
+
+function resetTimers(aRemoveGestureResolveDelay) {
+ GestureSettings.dwellThreshold = AccessFuTest.dwellThreshold;
+ GestureSettings.swipeMaxDuration = AccessFuTest.swipeMaxDuration;
+ GestureSettings.maxGestureResolveTimeout = aRemoveGestureResolveDelay ?
+ 0 : AccessFuTest.maxGestureResolveTimeout;
+}
+
+/**
+ * An extention to AccessFuTest that adds an ability to test a sequence of
+ * pointer events and their expected mozAccessFuGesture events.
+ * @param {Object} aSequence An object that has a list of pointer events to be
+ * generated and the expected mozAccessFuGesture events.
+ */
+AccessFuTest.addSequence = function AccessFuTest_addSequence(aSequence) {
+ AccessFuTest.addFunc(function testSequence() {
+ testMozAccessFuGesture(aSequence.expectedGestures, aSequence.title);
+ var events = aSequence.events;
+ function fireEvent(aEvent) {
+ var event = {
+ points: convertPointCoordinates(aEvent.points),
+ type: aEvent.type
+ };
+ var timeStamp = Date.now();
+ resetTimers(aEvent.removeGestureResolveDelay);
+ GestureTracker.handle(event, timeStamp);
+ setTimers(timeStamp, aEvent.removeDwellThreshold,
+ aEvent.removeSwipeMaxDuration);
+ processEvents();
+ }
+ function processEvents() {
+ if (events.length === 0) {
+ return;
+ }
+ var event = events.shift();
+ SimpleTest.executeSoon(function() {
+ fireEvent(event);
+ });
+ }
+ processEvents();
+ });
+};
+
+/**
+ * A helper function that loads JSON files.
+ * @param {String} aPath A path to a JSON file.
+ * @param {Function} aCallback A callback to be called on success.
+ */
+function loadJSON(aPath, aCallback) {
+ var request = new XMLHttpRequest();
+ request.open('GET', aPath, true);
+ request.responseType = 'json';
+ request.onload = function onload() {
+ aCallback(request.response);
+ };
+ request.send();
+}
diff --git a/accessible/tests/mochitest/jsat/gestures.json b/accessible/tests/mochitest/jsat/gestures.json
new file mode 100644
index 000000000..111994342
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/gestures.json
@@ -0,0 +1,352 @@
+[
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "tap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.03, "y": 1.03, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.03, "y": 1.03, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "tap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "dwellend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
+ {"type": "pointerup",
+ "points": [{"x": 1.03, "y": 1.02, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 0.97, "y": 1.01, "identifier": 1}]},
+ {"type": "pointerup",
+ "points": [{"x": 0.97, "y": 1.01, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "doubletap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeGestureResolveDelay": true }
+ ],
+ "expectedGestures": [{ "type": "tripletap" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "doubletaphold" },
+ { "type": "doubletapholdend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "taphold" }, { "type": "tapholdend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1.15, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1.3, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.3, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeleft" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1.5, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipedown" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeup" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.5, "y": 1.1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 0.95, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 0.95, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeleft" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 0.9, "y": 1.5, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 0.9, "y": 1.5, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipedown" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "swipeup" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "fingers": 2 }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1.5, "identifier": 2}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "fingers": 2 }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2},
+ {"x": 1, "y": 2, "identifier": 3}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2},
+ {"x": 1.5, "y": 2, "identifier": 3}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2},
+ {"x": 1.5, "y": 2, "identifier": 3}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "fingers": 3 }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+ { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 2, "y": 2, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 2, "y": 2, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+ { "type": "explore" }, { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeSwipeMaxDuration": true},
+ {"type": "pointerup", "points": [{"x": 1, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "explore" }, { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeSwipeMaxDuration": true},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "explore" }, { "type": "explore" },
+ { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 1, "y": 1, "identifier": 1}],
+ "removeDwellThreshold": true},
+ {"type": "pointermove",
+ "points": [{"x": 1.5, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.55, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.6, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.65, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.7, "y": 1.5, "identifier": 1}]},
+ {"type": "pointermove",
+ "points": [{"x": 1.75, "y": 1.5, "identifier": 1}]},
+ {"type": "pointerup", "points": [{"x": 1.75, "y": 1.5, "identifier": 1}]}
+ ],
+ "expectedGestures": [{ "type": "dwell" }, { "type": "explore" },
+ { "type": "explore" }, { "type": "exploreend" }]
+ },
+ {
+ "events": [
+ {"type": "pointerdown", "points": [{"x": 0.075, "y": 1, "identifier": 1},
+ {"x": 1, "y": 1.5, "identifier": 2}]},
+ {"type": "pointermove", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]},
+ {"type": "pointerup", "points": [{"x": 1.5, "y": 1, "identifier": 1},
+ {"x": 1.5, "y": 1.5, "identifier": 2}]}
+ ],
+ "expectedGestures": [{ "type": "swiperight", "edge": true, "fingers": 2 }]
+ },
+ {
+ "title": "Bug 1182311 - 3 finger triple tap is not reliable 1/2",
+ "events": [
+ {"points": [
+ {"y": 1.88467, "x": 0.89311, "identifier": 0},
+ {"y": 2.78481, "x": 0.56259, "identifier": 1},
+ {"y": 1.35021, "x": 1.37834, "identifier": 2}], "type": "pointerdown"},
+ {"points": [
+ {"y": 1.88467, "x": 0.89311, "identifier": 0},
+ {"y": 2.78481, "x": 0.56259, "identifier": 1},
+ {"y": 1.35021, "x": 1.37834, "identifier": 2}], "type": "pointerup"},
+ {"points": [
+ {"y": 1.76512, "x": 0.98453, "identifier": 0},
+ {"y": 1.1744, "x": 1.4346, "identifier": 1},
+ {"y": 2.5879, "x": 0.61181, "identifier": 2}], "type": "pointerdown"},
+ {"points": [
+ {"y": 1.76512, "x": 0.98453, "identifier": 0},
+ {"y": 1.1744, "x": 1.4346, "identifier": 1},
+ {"y": 2.5879, "x": 0.61181, "identifier": 2}], "type": "pointerup"},
+ {"points": [
+ {"y": 1.30098, "x": 1.52602, "identifier": 0},
+ {"y": 1.94093, "x": 1.02672, "identifier": 1},
+ {"y": 2.67229, "x": 0.75246, "identifier": 2}], "type": "pointerdown"},
+ {"points": [
+ {"y": 1.30098, "x": 1.52602, "identifier": 0},
+ {"y": 1.94093, "x": 1.02672, "identifier": 1},
+ {"y": 2.67229, "x": 0.75246, "identifier": 2}], "type": "pointerup",
+ "removeGestureResolveDelay": true}],
+ "expectedGestures": [{ "type": "tripletap", "fingers": 3 }]
+ },
+ {
+ "title": "Bug 1182311 - 3 finger triple tap is not reliable 2/2",
+ "events": [
+ {"type": "pointerdown",
+ "points": [{"identifier": 0, "x": 2.21875, "y": 1.510417}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 1, "x": 1.479167, "y": 2.53125}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 2, "x": 1.072917, "y": 3.739583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 1, "x": 1.46875, "y": 2.53125}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 1, "x": 1.447917, "y": 2.46875}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 0, "x": 2.21875, "y": 1.510417}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 1, "x": 1.447917, "y": 2.489583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 2, "x": 1.072917, "y": 3.739583}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 0, "x": 2.114583, "y": 1.572917}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 1, "x": 1.364583, "y": 2.614583}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 2, "x": 0.927083, "y": 3.864583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 1, "x": 1.364583, "y": 2.614583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 0, "x": 2.114583, "y": 1.572917}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 1, "x": 1.364583, "y": 2.614583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 2, "x": 0.927083, "y": 3.864583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 0, "x": 2.114583, "y": 1.572917}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 0, "x": 1.4375, "y": 2.59375}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 1, "x": 1.083333, "y": 3.71875}]},
+ {"type": "pointerdown",
+ "points": [{"identifier": 2, "x": 2.15625, "y": 1.489583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 0, "x": 1.4375, "y": 2.59375},
+ {"identifier": 2, "x": 2.15625, "y": 1.489583}]},
+ {"type": "pointermove",
+ "points": [{"identifier": 0, "x": 1.4375, "y": 2.59375},
+ {"identifier": 2, "x": 2.15625, "y": 1.489583}]},
+ {"type": "pointerup",
+ "points": [{"identifier": 1, "x": 1.083333, "y": 3.71875}],
+ "removeGestureResolveDelay": true}
+ ],
+ "expectedGestures": [{ "type": "tripletap", "fingers": 3 }]
+ }
+
+]
diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js
new file mode 100644
index 000000000..aa7ee74e4
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -0,0 +1,739 @@
+// A common module to run tests on the AccessFu module
+
+'use strict';
+
+/*global isDeeply, getMainChromeWindow, SimpleTest, SpecialPowers, Logger,
+ AccessFu, Utils, addMessageListener, currentTabDocument, currentBrowser*/
+
+/**
+ * A global variable holding an array of test functions.
+ */
+var gTestFuncs = [];
+/**
+ * A global Iterator for the array of test functions.
+ */
+var gIterator;
+
+Components.utils.import('resource://gre/modules/Services.jsm');
+Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
+Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
+Components.utils.import("resource://gre/modules/accessibility/Gestures.jsm");
+
+var AccessFuTest = {
+
+ addFunc: function AccessFuTest_addFunc(aFunc) {
+ if (aFunc) {
+ gTestFuncs.push(aFunc);
+ }
+ },
+
+ _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
+ var listener = {
+ observe: function observe(aMessage) {
+ // Ignore unexpected messages.
+ if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
+ return;
+ }
+ if (aMessage.message.indexOf(aWaitForMessage) < 0) {
+ return;
+ }
+ aListenerFunc.apply(listener);
+ }
+ };
+ Services.console.registerListener(listener);
+ return listener;
+ },
+
+ on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
+ return this._registerListener(aWaitForMessage, aListenerFunc);
+ },
+
+ off_log: function AccessFuTest_off_log(aListener) {
+ Services.console.unregisterListener(aListener);
+ },
+
+ once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
+ return this._registerListener(aWaitForMessage,
+ function listenAndUnregister() {
+ Services.console.unregisterListener(this);
+ aListenerFunc();
+ });
+ },
+
+ _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
+ var listener = function listener(aSubject, aTopic, aData) {
+ var data = JSON.parse(aData)[1];
+ // Ignore non-relevant outputs.
+ if (!data) {
+ return;
+ }
+ isDeeply(data.details, aWaitForData, "Data is correct");
+ aListener.apply(listener);
+ };
+ Services.obs.addObserver(listener, 'accessibility-output', false);
+ return listener;
+ },
+
+ on: function AccessFuTest_on(aWaitForData, aListener) {
+ return this._addObserver(aWaitForData, aListener);
+ },
+
+ off: function AccessFuTest_off(aListener) {
+ Services.obs.removeObserver(aListener, 'accessibility-output');
+ },
+
+ once: function AccessFuTest_once(aWaitForData, aListener) {
+ return this._addObserver(aWaitForData, function observerAndRemove() {
+ Services.obs.removeObserver(this, 'accessibility-output');
+ aListener();
+ });
+ },
+
+ _waitForExplicitFinish: false,
+
+ waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
+ this._waitForExplicitFinish = true;
+ },
+
+ finish: function AccessFuTest_finish() {
+ // Disable the console service logging.
+ Logger.test = false;
+ Logger.logLevel = Logger.INFO;
+ // Reset Gesture Settings.
+ GestureSettings.dwellThreshold = this.dwellThreshold =
+ this.originalDwellThreshold;
+ GestureSettings.swipeMaxDuration = this.swipeMaxDuration =
+ this.originalSwipeMaxDuration;
+ GestureSettings.maxGestureResolveTimeout =
+ this.maxGestureResolveTimeout =
+ this.originalMaxGestureResolveTimeout;
+ // Finish through idle callback to let AccessFu._disable complete.
+ SimpleTest.executeSoon(function () {
+ AccessFu.detach();
+ SimpleTest.finish();
+ });
+ },
+
+ nextTest: function AccessFuTest_nextTest() {
+ var result = gIterator.next();
+ if (result.done) {
+ this.finish();
+ return;
+ }
+ var testFunc = result.value;
+ testFunc();
+ },
+
+ runTests: function AccessFuTest_runTests(aAdditionalPrefs) {
+ if (gTestFuncs.length === 0) {
+ ok(false, "No tests specified!");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Create an Iterator for gTestFuncs array.
+ gIterator = (function*() {
+ for (var testFunc of gTestFuncs) {
+ yield testFunc;
+ }
+ })();
+
+ // Start AccessFu and put it in stand-by.
+ Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
+
+ AccessFu.attach(getMainChromeWindow(window));
+
+ AccessFu.readyCallback = function readyCallback() {
+ // Enable logging to the console service.
+ Logger.test = true;
+ Logger.logLevel = Logger.DEBUG;
+ };
+
+ var prefs = [['accessibility.accessfu.notify_output', 1],
+ ['dom.mozSettings.enabled', true]];
+ prefs.push.apply(prefs, aAdditionalPrefs);
+
+ this.originalDwellThreshold = GestureSettings.dwellThreshold;
+ this.originalSwipeMaxDuration = GestureSettings.swipeMaxDuration;
+ this.originalMaxGestureResolveTimeout =
+ GestureSettings.maxGestureResolveTimeout;
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
+ // SimpleTest.executeSoon timeout is bigger than the timer settings in
+ // GestureSettings that causes intermittents.
+ this.dwellThreshold = GestureSettings.dwellThreshold =
+ GestureSettings.dwellThreshold * 10;
+ this.swipeMaxDuration = GestureSettings.swipeMaxDuration =
+ GestureSettings.swipeMaxDuration * 10;
+ this.maxGestureResolveTimeout = GestureSettings.maxGestureResolveTimeout =
+ GestureSettings.maxGestureResolveTimeout * 10;
+
+ SpecialPowers.pushPrefEnv({ 'set': prefs }, function () {
+ if (AccessFuTest._waitForExplicitFinish) {
+ // Run all test functions asynchronously.
+ AccessFuTest.nextTest();
+ } else {
+ // Run all test functions synchronously.
+ gTestFuncs.forEach(testFunc => testFunc());
+ AccessFuTest.finish();
+ }
+ });
+ }
+};
+
+function AccessFuContentTest(aFuncResultPairs) {
+ this.queue = aFuncResultPairs;
+}
+
+AccessFuContentTest.prototype = {
+ expected: [],
+ currentAction: null,
+ actionNum: -1,
+
+ start: function(aFinishedCallback) {
+ Logger.logLevel = Logger.DEBUG;
+ this.finishedCallback = aFinishedCallback;
+ var self = this;
+
+ // Get top content message manager, and set it up.
+ this.mms = [Utils.getMessageManager(currentBrowser())];
+ this.setupMessageManager(this.mms[0], function () {
+ // Get child message managers and set them up
+ var frames = currentTabDocument().querySelectorAll('iframe');
+ if (frames.length === 0) {
+ self.pump();
+ return;
+ }
+
+ var toSetup = 0;
+ for (var i = 0; i < frames.length; i++ ) {
+ var mm = Utils.getMessageManager(frames[i]);
+ if (mm) {
+ toSetup++;
+ self.mms.push(mm);
+ self.setupMessageManager(mm, function () {
+ if (--toSetup === 0) {
+ // All message managers are loaded and ready to go.
+ self.pump();
+ }
+ });
+ }
+ }
+ });
+ },
+
+ finish: function() {
+ Logger.logLevel = Logger.INFO;
+ for (var mm of this.mms) {
+ mm.sendAsyncMessage('AccessFu:Stop');
+ mm.removeMessageListener('AccessFu:Present', this);
+ mm.removeMessageListener('AccessFu:Input', this);
+ mm.removeMessageListener('AccessFu:CursorCleared', this);
+ mm.removeMessageListener('AccessFu:Focused', this);
+ mm.removeMessageListener('AccessFu:AriaHidden', this);
+ mm.removeMessageListener('AccessFu:Ready', this);
+ mm.removeMessageListener('AccessFu:ContentStarted', this);
+ }
+ if (this.finishedCallback) {
+ this.finishedCallback();
+ }
+ },
+
+ setupMessageManager: function (aMessageManager, aCallback) {
+ function contentScript() {
+ addMessageListener('AccessFuTest:Focus', function (aMessage) {
+ var elem = content.document.querySelector(aMessage.json.selector);
+ if (elem) {
+ if (aMessage.json.blur) {
+ elem.blur();
+ } else {
+ elem.focus();
+ }
+ }
+ });
+ }
+
+ aMessageManager.addMessageListener('AccessFu:Present', this);
+ aMessageManager.addMessageListener('AccessFu:Input', this);
+ aMessageManager.addMessageListener('AccessFu:CursorCleared', this);
+ aMessageManager.addMessageListener('AccessFu:Focused', this);
+ aMessageManager.addMessageListener('AccessFu:AriaHidden', this);
+ aMessageManager.addMessageListener('AccessFu:Ready', function () {
+ aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
+ aMessageManager.sendAsyncMessage('AccessFu:Start',
+ { buildApp: 'browser',
+ androidSdkVersion: Utils.AndroidSdkVersion,
+ logLevel: 'DEBUG',
+ inTest: true });
+ });
+
+ aMessageManager.loadFrameScript(
+ 'chrome://global/content/accessibility/content-script.js', false);
+ aMessageManager.loadFrameScript(
+ 'data:,(' + contentScript.toString() + ')();', false);
+ },
+
+ pump: function() {
+ this.expected.shift();
+ if (this.expected.length) {
+ return;
+ }
+
+ var currentPair = this.queue.shift();
+
+ if (currentPair) {
+ this.actionNum++;
+ this.currentAction = currentPair[0];
+ if (typeof this.currentAction === 'function') {
+ this.currentAction(this.mms[0]);
+ } else if (this.currentAction) {
+ this.mms[0].sendAsyncMessage(this.currentAction.name,
+ this.currentAction.json);
+ }
+
+ this.expected = currentPair.slice(1, currentPair.length);
+
+ if (!this.expected[0]) {
+ this.pump();
+ }
+ } else {
+ this.finish();
+ }
+ },
+
+ receiveMessage: function(aMessage) {
+ var expected = this.expected[0];
+
+ if (!expected) {
+ return;
+ }
+
+ var actionsString = typeof this.currentAction === 'function' ?
+ this.currentAction.name + '()' : JSON.stringify(this.currentAction);
+
+ if (typeof expected === 'string') {
+ ok(true, 'Got ' + expected + ' after ' + actionsString);
+ this.pump();
+ } else if (expected.ignore && !expected.ignore(aMessage)) {
+ expected.is(aMessage.json, 'after ' + actionsString +
+ ' (' + this.actionNum + ')');
+ expected.is_correct_focus();
+ this.pump();
+ }
+ }
+};
+
+// Common content messages
+
+var ContentMessages = {
+ simpleMoveFirst: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveFirst',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMoveLast: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveLast',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMoveNext: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveNext',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMovePrevious: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'movePrevious',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ clearCursor: {
+ name: 'AccessFu:ClearCursor',
+ json: {
+ origin: 'top'
+ }
+ },
+
+ moveOrAdjustUp: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'movePrevious',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
+ }
+ },
+
+ moveOrAdjustDown: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'moveNext',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
+ }
+ },
+
+ androidScrollForward: function adjustUp() {
+ return {
+ name: 'AccessFu:AndroidScroll',
+ json: { origin: 'top', direction: 'forward' }
+ };
+ },
+
+ androidScrollBackward: function adjustDown() {
+ return {
+ name: 'AccessFu:AndroidScroll',
+ json: { origin: 'top', direction: 'backward' }
+ };
+ },
+
+ focusSelector: function focusSelector(aSelector, aBlur) {
+ return {
+ name: 'AccessFuTest:Focus',
+ json: {
+ selector: aSelector,
+ blur: aBlur
+ }
+ };
+ },
+
+ activateCurrent: function activateCurrent(aOffset) {
+ return {
+ name: 'AccessFu:Activate',
+ json: {
+ origin: 'top',
+ offset: aOffset
+ }
+ };
+ },
+
+ moveNextBy: function moveNextBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveByGranularity',
+ json: {
+ direction: 'Next',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ movePreviousBy: function movePreviousBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveByGranularity',
+ json: {
+ direction: 'Previous',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ moveCaretNextBy: function moveCaretNextBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveCaret',
+ json: {
+ direction: 'Next',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveCaret',
+ json: {
+ direction: 'Previous',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ _granularityMap: {
+ 'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
+ 'word': 2, // MOVEMENT_GRANULARITY_WORD
+ 'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
+ }
+};
+
+function ExpectedMessage (aName, aOptions) {
+ this.name = aName;
+ this.options = aOptions || {};
+ this.json = {};
+}
+
+ExpectedMessage.prototype.lazyCompare = function(aReceived, aExpected, aInfo) {
+ if (aExpected && !aReceived) {
+ return [false, 'Expected something but got nothing -- ' + aInfo];
+ }
+
+ var matches = true;
+ var delta = [];
+ for (var attr in aExpected) {
+ var expected = aExpected[attr];
+ var received = aReceived[attr];
+ if (typeof expected === 'object') {
+ var [childMatches, childDelta] = this.lazyCompare(received, expected);
+ if (!childMatches) {
+ delta.push(attr + ' [ ' + childDelta + ' ]');
+ matches = false;
+ }
+ } else {
+ if (received !== expected) {
+ delta.push(
+ attr + ' [ expected ' + JSON.stringify(expected) +
+ ' got ' + JSON.stringify(received) + ' ]');
+ matches = false;
+ }
+ }
+ }
+
+ var msg = delta.length ? delta.join(' ') : 'Structures lazily match';
+ return [matches, msg + ' -- ' + aInfo];
+};
+
+ExpectedMessage.prototype.is = function(aReceived, aInfo) {
+ var checkFunc = this.options.todo ? 'todo' : 'ok';
+ SimpleTest[checkFunc].apply(
+ SimpleTest, this.lazyCompare(aReceived, this.json, aInfo));
+};
+
+ExpectedMessage.prototype.is_correct_focus = function(aInfo) {
+ if (!this.options.focused) {
+ return;
+ }
+
+ var checkFunc = this.options.focused_todo ? 'todo_is' : 'is';
+ var doc = currentTabDocument();
+ SimpleTest[checkFunc].apply(SimpleTest,
+ [ doc.activeElement, doc.querySelector(this.options.focused),
+ 'Correct element is focused: ' + this.options.focused + ' -- ' + aInfo ]);
+};
+
+ExpectedMessage.prototype.ignore = function(aMessage) {
+ return aMessage.name !== this.name;
+};
+
+function ExpectedPresent(aB2g, aAndroid, aOptions) {
+ ExpectedMessage.call(this, 'AccessFu:Present', aOptions);
+ if (aB2g) {
+ this.json.b2g = aB2g;
+ }
+
+ if (aAndroid) {
+ this.json.android = aAndroid;
+ }
+}
+
+ExpectedPresent.prototype = Object.create(ExpectedMessage.prototype);
+
+ExpectedPresent.prototype.is = function(aReceived, aInfo) {
+ var received = this.extract_presenters(aReceived);
+
+ for (var presenter of ['b2g', 'android']) {
+ if (!this.options['no_' + presenter]) {
+ var todo = this.options.todo || this.options[presenter + '_todo']
+ SimpleTest[todo ? 'todo' : 'ok'].apply(
+ SimpleTest, this.lazyCompare(received[presenter],
+ this.json[presenter], aInfo + ' (' + presenter + ')'));
+ }
+ }
+};
+
+ExpectedPresent.prototype.extract_presenters = function(aReceived) {
+ var received = { count: 0 };
+ for (var presenter of aReceived) {
+ if (presenter) {
+ received[presenter.type.toLowerCase()] = presenter.details;
+ received.count++;
+ }
+ }
+
+ return received
+};
+
+ExpectedPresent.prototype.ignore = function(aMessage) {
+ if (ExpectedMessage.prototype.ignore.call(this, aMessage)) {
+ return true;
+ }
+
+ var received = this.extract_presenters(aMessage.json);
+ return received.count === 0 ||
+ (received.visual && received.visual.eventType === 'viewport-change') ||
+ (received.android &&
+ received.android[0].eventType === AndroidEvent.VIEW_SCROLLED);
+};
+
+function ExpectedCursorChange(aSpeech, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'vc-change',
+ data: aSpeech
+ }, [{
+ eventType: 0x8000, // VIEW_ACCESSIBILITY_FOCUSED
+ }], aOptions);
+}
+
+ExpectedCursorChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedCursorTextChange(aSpeech, aStartOffset, aEndOffset, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'vc-change',
+ data: aSpeech
+ }, [{
+ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ fromIndex: aStartOffset,
+ toIndex: aEndOffset
+ }], aOptions);
+
+ // bug 980509
+ this.options.b2g_todo = true;
+}
+
+ExpectedCursorTextChange.prototype =
+ Object.create(ExpectedCursorChange.prototype);
+
+function ExpectedClickAction(aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: 'clickAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED
+ }], aOptions);
+}
+
+ExpectedClickAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedCheckAction(aChecked, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: aChecked ? 'checkAction' : 'uncheckAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED,
+ checked: aChecked
+ }], aOptions);
+}
+
+ExpectedCheckAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedSwitchAction(aSwitched, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: aSwitched ? 'onAction' : 'offAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED,
+ checked: aSwitched
+ }], aOptions);
+}
+
+ExpectedSwitchAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedNameChange(aName, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'name-change',
+ data: aName
+ }, null, aOptions);
+}
+
+ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedValueChange(aValue, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'value-change',
+ data: aValue
+ }, null, aOptions);
+}
+
+ExpectedValueChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedTextChanged(aValue, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'text-change',
+ data: aValue
+ }, null, aOptions);
+}
+
+ExpectedTextChanged.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedEditState(aEditState, aOptions) {
+ ExpectedMessage.call(this, 'AccessFu:Input', aOptions);
+ this.json = aEditState;
+}
+
+ExpectedEditState.prototype = Object.create(ExpectedMessage.prototype);
+
+function ExpectedTextSelectionChanged(aStart, aEnd, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
+ brailleOutput: {
+ selectionStart: aStart,
+ selectionEnd: aEnd
+ }}], aOptions);
+}
+
+ExpectedTextSelectionChanged.prototype =
+ Object.create(ExpectedPresent.prototype);
+
+function ExpectedTextCaretChanged(aFrom, aTo, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ fromIndex: aFrom,
+ toIndex: aTo
+ }], aOptions);
+}
+
+ExpectedTextCaretChanged.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedAnnouncement(aAnnouncement, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.ANNOUNCEMENT,
+ text: [ aAnnouncement],
+ addedCount: aAnnouncement.length
+ }], aOptions);
+}
+
+ExpectedAnnouncement.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedNoMove(aOptions) {
+ ExpectedPresent.call(this, {eventType: 'no-move' }, null, aOptions);
+}
+
+ExpectedNoMove.prototype = Object.create(ExpectedPresent.prototype);
+
+var AndroidEvent = {
+ VIEW_CLICKED: 0x01,
+ VIEW_LONG_CLICKED: 0x02,
+ VIEW_SELECTED: 0x04,
+ VIEW_FOCUSED: 0x08,
+ VIEW_TEXT_CHANGED: 0x10,
+ WINDOW_STATE_CHANGED: 0x20,
+ VIEW_HOVER_ENTER: 0x80,
+ VIEW_HOVER_EXIT: 0x100,
+ VIEW_SCROLLED: 0x1000,
+ VIEW_TEXT_SELECTION_CHANGED: 0x2000,
+ ANNOUNCEMENT: 0x4000,
+ VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
+ VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
+};
diff --git a/accessible/tests/mochitest/jsat/output.js b/accessible/tests/mochitest/jsat/output.js
new file mode 100644
index 000000000..5afcc8a66
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/output.js
@@ -0,0 +1,114 @@
+var Cu = Components.utils;
+const PREF_UTTERANCE_ORDER = "accessibility.accessfu.utterance";
+
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+Cu.import("resource://gre/modules/accessibility/OutputGenerator.jsm", this);
+
+/**
+ * Test context output generation.
+ *
+ * @param expected {Array} expected output.
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ * @param aOldAccOrElmOrID optional identifier to get an accessible relative to
+ * the |aAccOrElmOrID|.
+ * @param aGenerator the output generator to use when generating accessible
+ * output
+ *
+ * Note: if |aOldAccOrElmOrID| is not provided, the |aAccOrElmOrID| must be
+ * scoped to the "root" element in markup.
+ */
+function testContextOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, aGenerator) {
+ var accessible = getAccessible(aAccOrElmOrID);
+ var oldAccessible = aOldAccOrElmOrID !== null ?
+ getAccessible(aOldAccOrElmOrID || 'root') : null;
+ var context = new PivotContext(accessible, oldAccessible);
+ var output = aGenerator.genForContext(context);
+
+ // Create a version of the output that has null members where we have
+ // null members in the expected output. Those are indexes that are not testable
+ // because of the changing nature of the test (different window names), or strings
+ // that are inaccessible to us, like the title of parent documents.
+ var masked_output = [];
+ for (var i=0; i < output.length; i++) {
+ if (expected[i] === null) {
+ masked_output.push(null);
+ } else {
+ masked_output[i] = typeof output[i] === "string" ? output[i].trim() :
+ output[i];
+ }
+ }
+
+ isDeeply(masked_output, expected,
+ "Context output is correct for " + aAccOrElmOrID +
+ " (output: " + JSON.stringify(output) + ") ==" +
+ " (expected: " + JSON.stringify(expected) + ")");
+}
+
+/**
+ * Test object output generated array that includes names.
+ * Note: test ignores outputs without the name.
+ *
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ * @param aGenerator the output generator to use when generating accessible
+ * output
+ */
+function testObjectOutput(aAccOrElmOrID, aGenerator) {
+ var accessible = getAccessible(aAccOrElmOrID);
+ if (!accessible.name || !accessible.name.trim()) {
+ return;
+ }
+ var context = new PivotContext(accessible);
+ var output = aGenerator.genForObject(accessible, context);
+ var outputOrder;
+ try {
+ outputOrder = SpecialPowers.getIntPref(PREF_UTTERANCE_ORDER);
+ } catch (ex) {
+ // PREF_UTTERANCE_ORDER not set.
+ outputOrder = 0;
+ }
+ var expectedNameIndex = outputOrder === 0 ? output.length - 1 : 0;
+ var nameIndex = output.indexOf(accessible.name);
+
+ if (nameIndex > -1) {
+ ok(output.indexOf(accessible.name) === expectedNameIndex,
+ "Object output is correct for " + aAccOrElmOrID);
+ }
+}
+
+/**
+ * Test object and context output for an accessible.
+ *
+ * @param expected {Array} expected output.
+ * @param aAccOrElmOrID identifier to get an accessible to test.
+ * @param aOldAccOrElmOrID optional identifier to get an accessible relative to
+ * the |aAccOrElmOrID|.
+ * @param aOutputKind the type of output
+ */
+function testOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, aOutputKind) {
+ var generator;
+ if (aOutputKind === 1) {
+ generator = UtteranceGenerator;
+ } else {
+ generator = BrailleGenerator;
+ }
+ testContextOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, generator);
+ // Just need to test object output for individual
+ // accOrElmOrID.
+ if (aOldAccOrElmOrID) {
+ return;
+ }
+ testObjectOutput(aAccOrElmOrID, generator);
+}
+
+function testHints(expected, aAccOrElmOrID, aOldAccOrElmOrID) {
+ var accessible = getAccessible(aAccOrElmOrID);
+ var oldAccessible = aOldAccOrElmOrID !== null ?
+ getAccessible(aOldAccOrElmOrID || 'root') : null;
+ var context = new PivotContext(accessible, oldAccessible);
+ var hints = context.interactionHints;
+
+ isDeeply(hints, expected,
+ "Context hitns are correct for " + aAccOrElmOrID +
+ " (hints: " + JSON.stringify(hints) + ") ==" +
+ " (expected: " + JSON.stringify(expected) + ")");
+}
diff --git a/accessible/tests/mochitest/jsat/test_alive.html b/accessible/tests/mochitest/jsat/test_alive.html
new file mode 100644
index 000000000..cd4eef712
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_alive.html
@@ -0,0 +1,81 @@
+<html>
+
+<head>
+ <title>AccessFu test for enabling</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="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function prefStart() {
+ AccessFuTest.once_log("AccessFu:Enabled", () =>
+ ok(AccessFu._enabled, "AccessFu was enabled again."));
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ // Start AccessFu via pref.
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 1]]});
+ }
+
+ // Listen for 'EventManager.stop' and enable AccessFu again.
+ function settingsStart() {
+ isnot(AccessFu._enabled, true, "AccessFu was disabled.");
+ // XXX: Bug 978076 - test start with SettingsManager.
+ //navigator.mozSettings.createLock().set(
+ // {'accessibility.screenreader': false});
+ AccessFuTest.once_log("EventManager.start", () => {
+ ok(AccessFu._enabled, "AccessFu was enabled again.");
+ AccessFuTest.nextTest();
+ });
+ AccessFu._enable();
+ }
+
+ // Make sure EventManager is started again.
+ function settingsStop() {
+ ok(AccessFu._enabled, "AccessFu was enabled again.");
+ // XXX: Bug 978076 - test stop with SettingsManager.
+ //navigator.mozSettings.createLock().set(
+ // {'accessibility.screenreader': false});
+ AccessFuTest.once_log("EventManager.stop", () => {
+ isnot(AccessFu._enabled, "AccessFu was disabled.");
+ AccessFuTest.finish();
+ });
+ AccessFu._disable();
+ }
+
+ // Listen for initial 'EventManager.start' and disable AccessFu.
+ function prefStop() {
+ ok(AccessFu._enabled, "AccessFu was started via preference.");
+ AccessFuTest.once_log("AccessFu:Disabled", () =>
+ isnot(AccessFu._enabled, true, "AccessFu was disabled."));
+ AccessFuTest.once_log("EventManager.stop", AccessFuTest.nextTest);
+
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 0]]});
+ }
+
+ function doTest() {
+ AccessFuTest.addFunc(prefStart);
+ AccessFuTest.addFunc(prefStop);
+ AccessFuTest.addFunc(settingsStart);
+ AccessFuTest.addFunc(settingsStop);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests(); // Will call SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+ title="[AccessFu] Add mochitest for enabling">
+ Mozilla Bug 811307
+ </a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_content_integration.html b/accessible/tests/mochitest/jsat/test_content_integration.html
new file mode 100644
index 000000000..809f79726
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -0,0 +1,343 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu content integration</title>
+ <meta charset="utf-8" />
+ <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/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.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" src="../layout.js"></script>
+ <script type="application/javascript" src="jsatcommon.js"></script>
+
+ <script type="application/javascript">
+ function doTest() {
+ var doc = currentTabDocument();
+ var iframe = doc.createElement('iframe');
+ iframe.id = 'iframe';
+ iframe.mozbrowser = true;
+ iframe.addEventListener('mozbrowserloadend', function () {
+ var contentTest = new AccessFuContentTest(
+ [
+ // Simple traversal forward
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['Traversal Rule test document', 'Phone status bar'],
+ { focused: 'body' })],
+ [ContentMessages.simpleMovePrevious, new ExpectedNoMove()],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext, new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}],
+ { focused: 'iframe' })],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+
+ // check checkbox
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedCheckAction(true)],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
+ [ContentMessages.moveOrAdjustUp(), new ExpectedValueChange('6')],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Light', {"string": "stateOff"}, {'string': 'switch'}])],
+ // switch on
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedSwitchAction(true)],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['slider', '0', {'string': 'slider'}])],
+
+ // Simple traversal backward
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['Light', {"string": "stateOn"}, {'string': 'switch'}])],
+ // switch off
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedSwitchAction(false)],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['such app', 'much range', '6', {'string': 'slider'}])],
+ [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
+ [ContentMessages.androidScrollForward(), new ExpectedValueChange('6')],
+ [ContentMessages.androidScrollBackward(), new ExpectedValueChange('5')],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['many option', {'string': 'stateChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ // uncheck checkbox
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction({ no_android: true }),
+ new ExpectedCheckAction(false)],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['Phone status bar'])],
+
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ // Moving to the absolute last item from an embedded document
+ // fails. Bug 972035.
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ // Move from an inner frame to the last element in the parent doc
+ [ContentMessages.simpleMoveLast,
+ new ExpectedCursorChange(
+ ['slider', '0', {'string': 'slider'}], { b2g_todo: true })],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['such app', 'many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
+ // Calling AdjustOrMove should adjust the range.
+ [ContentMessages.moveOrAdjustDown('FormElement'),
+ new ExpectedValueChange('4')],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedValueChange('5')],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(['much range', {'string': 'label'}])],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.moveOrAdjustUp('FormElement'),
+ new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Moving to the absolute first item from an embedded document
+ // fails. Bug 972035.
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [ContentMessages.simpleMoveNext, new ExpectedCursorChange(
+ ['many option', {'string': 'stateNotChecked'},
+ {'string': 'checkbutton'}, {'string': 'listStart'},
+ {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
+ [ContentMessages.simpleMoveFirst,
+ new ExpectedCursorChange(['Phone status bar'], { b2g_todo: true })],
+
+ // Reset cursors
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Current virtual cursor's position's name changes
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.focusSelector('button#fruit', false),
+ new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+ [doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
+
+ // Name and value changes inside a live-region (no cursor present)
+ [doc.defaultView.renameSlider,
+ new ExpectedNameChange('mover')],
+ [doc.defaultView.changeSliderValue,
+ new ExpectedValueChange('medium')],
+
+ // Blur button and reset cursor
+ [ContentMessages.focusSelector('button#fruit', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Move cursor with focus in outside document
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.focusSelector('button#home', false),
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+
+ // Blur button and reset cursor
+ [ContentMessages.focusSelector('button#home', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Set focus on element outside of embedded frame while
+ // cursor is in frame
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [ContentMessages.focusSelector('button#home', false),
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+
+ // Blur button and reset cursor
+ [ContentMessages.focusSelector('button#home', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // XXX: Set focus on iframe itself.
+ // XXX: Set focus on element in iframe when cursor is outside of it.
+ // XXX: Set focus on element in iframe when cursor is in iframe.
+
+ // aria-hidden element that the virtual cursor is positioned on
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [doc.defaultView.ariaHideBack,
+ new ExpectedCursorChange(
+ ["such app", "wow", {"string": "headingLevel","args": [1]}])],
+ // Changing aria-hidden attribute twice and making sure that the event
+ // is fired only once when the actual change happens.
+ [doc.defaultView.ariaHideBack],
+ [doc.defaultView.ariaShowBack],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // aria-hidden on the iframe that has the vc.
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [doc.defaultView.ariaHideIframe,
+ new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
+ [doc.defaultView.ariaShowIframe],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // aria-hidden element and auto Move
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [doc.defaultView.ariaHideBack],
+ [ContentMessages.focusSelector('button#back', false),
+ // Must not speak Back button as it is aria-hidden
+ new ExpectedCursorChange(
+ ["such app", "wow", {"string": "headingLevel","args": [1]}])],
+ [doc.defaultView.ariaShowBack],
+ [ContentMessages.focusSelector('button#back', true), null],
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Open dialog in outer doc, while cursor is also in outer doc
+ [ContentMessages.simpleMoveLast,
+ new ExpectedCursorChange(['Traversal Rule test document', 'mover',
+ 'medium', {'string': 'slider'}])],
+ [doc.defaultView.showAlert,
+ new ExpectedCursorChange(['This is an alert!',
+ {'string': 'headingLevel', 'args': [1]},
+ {'string': 'dialog'}])],
+
+ [doc.defaultView.hideAlert,
+ new ExpectedCursorChange(['Traversal Rule test document', 'mover',
+ 'medium', {'string': 'slider'}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Open dialog in outer doc, while cursor is in inner frame
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+ [doc.defaultView.showAlert, new ExpectedCursorChange(['This is an alert!',
+ {'string': 'headingLevel', 'args': [1]},
+ {'string': 'dialog'}])],
+
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Do you agree?'])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Yes', {'string': 'pushbutton'}])],
+ [ContentMessages.activateCurrent(),
+ new ExpectedClickAction(),
+ new ExpectedCursorChange(
+ ['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
+
+ [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
+
+ // Open dialog, then focus on something when closing
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
+ [doc.defaultView.showAlert,
+ new ExpectedCursorChange(['This is an alert!',
+ {'string': 'headingLevel', 'args': [1]}, {'string': 'dialog'}])],
+
+ [function hideAlertAndFocusHomeButton() {
+ doc.defaultView.hideAlert();
+ doc.querySelector('button#home').focus();
+ }, new ExpectedCursorChange(['Traversal Rule test document',
+ 'Home', {'string': 'pushbutton'}])],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])]
+ [ContentMessages.simpleMoveNext, new ExpectedNoMove()]
+ ]);
+
+ addA11yLoadEvent(function() {
+ contentTest.start(function () {
+ closeBrowserWindow();
+ SimpleTest.finish();
+ });
+ }, doc.defaultView)
+ });
+ iframe.src = 'data:text/html;charset=utf-8,' + doc.defaultView.frameContents;
+ doc.getElementById('appframe').appendChild(iframe);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(
+ function () {
+ openBrowserWindow(
+ function () {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ // TODO: remove this as part of bug 820712
+ ['network.disable.ipc.security', true],
+
+
+ ['dom.ipc.browser_frames.oop_by_default', true],
+ ['dom.mozBrowserFramesEnabled', true],
+ ['browser.pagethumbnails.capturing_disabled', true]
+ ]
+ }, doTest) },
+ getRootDirectory(window.location.href) + 'doc_content_integration.html');
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for OOP message handling and general integration"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=972047">Mozilla Bug 933808</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_content_text.html b/accessible/tests/mochitest/jsat/test_content_text.html
new file mode 100644
index 000000000..558b819e9
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_content_text.html
@@ -0,0 +1,292 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu content integration</title>
+ <meta charset="utf-8" />
+ <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="chrome://mochikit/content/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.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" src="../layout.js"></script>
+ <script type="application/javascript" src="jsatcommon.js"></script>
+
+ <script type="application/javascript">
+ function doTest() {
+ var doc = currentTabDocument();
+ var textTest = new AccessFuContentTest(
+ [
+ // Read-only text tests
+ [ContentMessages.simpleMoveFirst,
+ new ExpectedCursorChange(
+ ['Text content test document', 'These are my awards, Mother. ' +
+ 'From Army. The seal is for marksmanship, and the gorilla is ' +
+ 'for sand racing.'])],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('These', 0, 5)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('are', 6, 9)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('my', 10, 12)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('awards,', 13, 20)],
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('Mother.', 21, 28)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('awards,', 13, 20)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('my', 10, 12)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('are', 6, 9)],
+ [ContentMessages.movePreviousBy('word'),
+ new ExpectedCursorTextChange('These', 0, 5)],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(['You\'re a good guy, mon frere. ' +
+ 'That means brother in French. ' +
+ 'I don\'t know how I know that. ' +
+ 'I took four years of Spanish.'])],
+ // XXX: Word boundary should be past the apostraphe.
+ [ContentMessages.moveNextBy('word'),
+ new ExpectedCursorTextChange('You\'re', 0, 6,
+ { android_todo: true /* Bug 980512 */})],
+
+ // Editable text tests.
+ [ContentMessages.focusSelector('textarea'),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: true,
+ atStart: true,
+ atEnd: false
+ }),
+ new ExpectedCursorChange(
+ ['Please refrain from Mayoneggs during this salmonella scare.',
+ {string: 'textarea'}]),
+ new ExpectedTextSelectionChanged(0, 0)
+ ],
+ [ContentMessages.activateCurrent(10),
+ new ExpectedTextCaretChanged(0, 10),
+ new ExpectedEditState({ editing: true,
+ multiline: true,
+ atStart: false,
+ atEnd: false }),
+ new ExpectedTextSelectionChanged(10, 10)],
+ [ContentMessages.activateCurrent(20),
+ new ExpectedTextCaretChanged(10, 20),
+ new ExpectedTextSelectionChanged(20, 20)
+ ],
+ [ContentMessages.moveCaretNextBy('word'),
+ new ExpectedTextCaretChanged(20, 29),
+ new ExpectedTextSelectionChanged(29, 29)
+ ],
+ [ContentMessages.moveCaretNextBy('word'),
+ new ExpectedTextCaretChanged(29, 36),
+ new ExpectedTextSelectionChanged(36, 36)
+ ],
+ [ContentMessages.moveCaretNextBy('character'),
+ new ExpectedTextCaretChanged(36, 37),
+ new ExpectedTextSelectionChanged(37, 37)
+ ],
+ [ContentMessages.moveCaretNextBy('character'),
+ new ExpectedTextCaretChanged(37, 38),
+ new ExpectedTextSelectionChanged(38, 38)
+ ],
+ [ContentMessages.moveCaretNextBy('paragraph'),
+ new ExpectedTextCaretChanged(38, 59),
+ new ExpectedTextSelectionChanged(59, 59)
+ ],
+ [ContentMessages.moveCaretPreviousBy('word'),
+ new ExpectedTextCaretChanged(53, 59),
+ new ExpectedTextSelectionChanged(53, 53)
+ ],
+
+ // bug xxx
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ ['So we don\'t get dessert?', {string: 'label'}],
+ { focused: 'html'}),
+ new ExpectedAnnouncement('navigating'),
+ new ExpectedEditState({
+ editing: false,
+ multiline: false,
+ atStart: true,
+ atEnd: false })],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ [{ string : 'entry' }],
+ { focused: 'html'})],
+ [ContentMessages.activateCurrent(0),
+ new ExpectedClickAction(),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: false,
+ atStart: true,
+ atEnd: true
+ }, { focused: 'input[type=text]' }),
+ new ExpectedTextSelectionChanged(0, 0),
+ new ExpectedTextSelectionChanged(0, 0)
+ ],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(
+ ['So we don\'t get dessert?', {string: 'label'}]),
+ new ExpectedAnnouncement('navigating'),
+ new ExpectedEditState({
+ editing: false,
+ multiline: false,
+ atStart: true,
+ atEnd: false
+ },{ focused: 'html' })
+ ],
+ [ContentMessages.simpleMoveNext,
+ new ExpectedCursorChange(
+ [{ string : 'entry' }],
+ { focused: 'html'})],
+ [ContentMessages.activateCurrent(0),
+ new ExpectedClickAction(),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: false,
+ atStart: true,
+ atEnd: true
+ },
+ { focused: 'input[type=text]' }),
+ new ExpectedTextSelectionChanged(0, 0)],
+ [ContentMessages.simpleMovePrevious,
+ new ExpectedCursorChange(
+ [ 'So we don\'t get dessert?', {string: 'label'} ]),
+ new ExpectedAnnouncement('navigating'),
+ new ExpectedEditState({
+ editing: false,
+ multiline: false,
+ atStart: true,
+ atEnd: false
+ }, { focused: 'html' })],
+
+ [ContentMessages.focusSelector('input'),
+ new ExpectedAnnouncement('editing'),
+ new ExpectedEditState({
+ editing: true,
+ multiline: false,
+ atStart: true,
+ atEnd: true
+ }),
+ new ExpectedCursorChange([{string: 'entry'}]),
+ new ExpectedTextSelectionChanged(0, 0)
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 3]]}, typeKey('a')());
+ },
+ new ExpectedTextChanged('a'),
+ new ExpectedTextSelectionChanged(1, 1),
+ ],
+ [typeKey('b'),
+ new ExpectedTextChanged('b'),
+ new ExpectedTextSelectionChanged(2, 2),
+ ],
+ [typeKey('c'),
+ new ExpectedTextChanged('c'),
+ new ExpectedTextSelectionChanged(3, 3),
+ ],
+ [typeKey('d'),
+ new ExpectedTextChanged('d'),
+ new ExpectedTextSelectionChanged(4, 4),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(' abcd'),
+ new ExpectedTextSelectionChanged(5, 5),
+ ],
+ [typeKey('e'),
+ new ExpectedTextChanged('e'),
+ new ExpectedTextSelectionChanged(6, 6),
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 2]]}, typeKey('a')());
+ },
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(7, 7),
+ ],
+ [typeKey('d'),
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(8, 8),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(' ead'),
+ new ExpectedTextSelectionChanged(9, 9),
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 1]]}, typeKey('f')());
+ },
+ new ExpectedTextChanged('f'),
+ new ExpectedTextSelectionChanged(10, 10),
+ ],
+ [typeKey('g'),
+ new ExpectedTextChanged('g'),
+ new ExpectedTextSelectionChanged(11, 11),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(' '),
+ new ExpectedTextSelectionChanged(12, 12),
+ ],
+ [function() {
+ SpecialPowers.pushPrefEnv({"set": [[KEYBOARD_ECHO_SETTING, 0]]}, typeKey('f')());
+ },
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(13, 13),
+ ],
+ [typeKey('g'),
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(14, 14),
+ ],
+ [typeKey(' '),
+ new ExpectedTextChanged(''),
+ new ExpectedTextSelectionChanged(15, 15),
+ ],
+ ]);
+
+ const KEYBOARD_ECHO_SETTING = 'accessibility.accessfu.keyboard_echo';
+ function typeKey(key) {
+ return function() { synthesizeKey(key, {}, currentTabWindow()); };
+ }
+
+ addA11yLoadEvent(function() {
+ textTest.start(function () {
+ closeBrowserWindow();
+ SimpleTest.finish();
+ });
+ }, doc.defaultView);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(
+ function () {
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_content_text.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for text editing and navigating"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=972047">Mozilla Bug 933808</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_explicit_names.html b/accessible/tests/mochitest/jsat/test_explicit_names.html
new file mode 100644
index 000000000..fb7ed2022
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_explicit_names.html
@@ -0,0 +1,191 @@
+<html>
+<head>
+ <title>[AccessFu] Trust explicitly associated names when speaking certain elements</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="output.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "anchor1",
+ expected: [{"string": "link"}, "title"]
+ }, {
+ accOrElmOrID: "anchor2",
+ expected: [{"string": "link"}, "This is a link"]
+ }, {
+ accOrElmOrID: "button1",
+ expected: [{"string": "pushbutton"}, "Press me"]
+ }, {
+ accOrElmOrID: "button2",
+ expected: [{"string": "pushbutton"}, "Press me"]
+ }, {
+ accOrElmOrID: "textarea1",
+ expected: [{"string": "textarea"}, "This is the text area text.",
+ "Test Text Area"]
+ }, {
+ accOrElmOrID: "textarea2",
+ expected: [{"string": "textarea"}, "This is the text area text."]
+ }, {
+ accOrElmOrID: "heading1",
+ expected: [{"string": "headingLevel", "args": [1]}, "Test heading",
+ "This is the heading."]
+ }, {
+ accOrElmOrID: "heading1",
+ oldAccOrElmOrID: null,
+ expected: [null /* parent doc title */, document.title,
+ {"string": "headingLevel", "args": [1]}, "Test heading",
+ "This is the heading."]
+ }, {
+ accOrElmOrID: "heading2",
+ expected: [{"string": "headingLevel", "args": [1]},
+ "This is the heading."]
+ }, {
+ accOrElmOrID: "list",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listStart"}, "Top of the list",
+ {"string": "listEnd"}, "2.", "list two"]
+ }, {
+ accOrElmOrID: "dlist",
+ expected: [{"string": "definitionlist"},
+ {"string": "listItemsCount", "count": 0.5}, "Test Definition List",
+ "dd one"]
+ }, {
+ accOrElmOrID: "li_one",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listStart"}, "Top of the list"]
+ }, {
+ accOrElmOrID: "li_two",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listEnd"}, "2.", "list two"]
+ }, {
+ accOrElmOrID: "cell",
+ expected: [{"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "List of Fruits",
+ {"string": "list"}, {"string": "listItemsCount", "count": 4},
+ {"string": "listStart"}, {"string": "link"}, "Apples",
+ {"string": "link"}, "Bananas",
+ {"string": "link"}, "Peaches", {"string": "listEnd"},
+ {"string": "link"}, "Plums"]
+ }, {
+ accOrElmOrID: "app.net",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ {"string": "listStart"}, {"string": "link"}, "star",
+ {"string": "listEnd"}, {"string": "link"}, "repost"]
+ }, {
+ // Test pivot to list from li_one.
+ accOrElmOrID: "list",
+ oldAccOrElmOrID: "li_one",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+ "Test List", {"string": "listStart"}, "Top of the list",
+ {"string": "listEnd"}, "2.", "list two"]
+ }, {
+ // Test pivot to li_one from list.
+ accOrElmOrID: "li_one",
+ oldAccOrElmOrID: "list",
+ expected: [{"string": "listStart"}, "Top of the list"]
+ }, {
+ // Test pivot to "apples" link from the table cell.
+ accOrElmOrID: "apples",
+ oldAccOrElmOrID: "cell",
+ expected: [{"string": "list"}, {"string": "listItemsCount", "count": 4},
+ {"string": "listStart"}, {"string": "link"}, "Apples"]
+ }, {
+ // Test pivot to the table cell from the "apples" link.
+ accOrElmOrID: "cell",
+ oldAccOrElmOrID: "apples",
+ expected: ["List of Fruits", {"string": "list"},
+ {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+ {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
+ {"string": "link"}, "Peaches", {"string": "listEnd"},
+ {"string": "link"}, "Plums"]
+ }];
+
+ SpecialPowers.pushPrefEnv({"set": [[PREF_UTTERANCE_ORDER, 0]]}, function() {
+ // Test various explicit names vs the utterance generated from subtrees.
+ tests.forEach(function run(test) {
+ testOutput(test.expected, test.accOrElmOrID, test.oldAccOrElmOrID, 1);
+ });
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=845870"
+ title="[AccessFu] Trust explicitly associated names when speaking certain elements">
+ Mozilla Bug 845870
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <button id="button1" aria-label="Press me">This is not a name</button>
+ <button id="button2">
+ Press me
+ </button>
+ <a id="anchor1" href="#test" title="title"></a>
+ <a id="anchor2" href="#test">This is a link</a>
+ <textarea id="textarea1" title="Test Text Area" cols="80" rows="5">This is the text area text.</textarea>
+ <textarea id="textarea2" cols="80" rows="5">
+ This is the text area text.
+ </textarea>
+ <h1 id="heading1" title="Test heading">This is the heading.</h1>
+ <h1 id="heading2">
+ This is the heading.
+ </h1>
+ <ol id="list" title="Test List">
+ <li id="li_one" aria-label="Top of the list">list one</li>
+ <li id="li_two">list two</li>
+ </ol>
+ <dl id="dlist" title="Test Definition List">
+ <dd id="dd_one">dd one</dd>
+ </dl>
+ <table>
+ <caption>Fruits and vegetables</caption>
+ <tr>
+ <td id="cell" aria-label="List of Fruits">
+ <ul style="list-style-type: none;">
+ <li><a id="apples" href="#">Apples</a></li>
+ <li><a id="bananas" href="#">Bananas</a></li>
+ <li><a href="#">Peaches</a></li>
+ <li>
+ <a href="#">
+
+ Plums
+ </a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <!-- app.net -->
+ <ul id="app.net" class="unstyled ul-horizontal yui3-u fixed-right ta-right" style="list-style-type: none;">
+ <li class="yui3-u">
+ <a href="#star" data-starred="0" data-star-button="1" data-post-id="5098826" aria-label="star">
+ Garbage
+ </a>
+ </li>
+ <li class="yui3-u repost">
+ <a href="#repost" title="repost" data-repost-button="1" data-reposted="0" data-post-id="5098826">
+ <i aria-label="repost" class="icon-repost"></i>
+ </a>
+ </li>
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_gesture_tracker.html b/accessible/tests/mochitest/jsat/test_gesture_tracker.html
new file mode 100644
index 000000000..af2755455
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_gesture_tracker.html
@@ -0,0 +1,51 @@
+<html>
+
+<head>
+ <title>AccessFu tests for gesture tracker.</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="../layout.js"></script>
+ <script type="application/javascript" src="./jsatcommon.js"></script>
+ <script type="application/javascript" src="./dom_helper.js"></script>
+ <script type="application/javascript">
+
+ function startGestureTracker() {
+ GestureTracker.reset();
+ AccessFuTest.nextTest();
+ }
+
+ function stopGestureTracker() {
+ GestureTracker.reset();
+ AccessFuTest.finish();
+ }
+
+ function doTest() {
+ loadJSON("./gestures.json", function onSuccess(gestures) {
+ AccessFuTest.addFunc(startGestureTracker);
+ AccessFuTest.sequenceCleanup = GestureTracker.reset.bind(
+ GestureTracker);
+ gestures.forEach(AccessFuTest.addSequence);
+ AccessFuTest.addFunc(stopGestureTracker);
+ AccessFuTest.waitForExplicitFinish();
+ Logger.logLevel = Logger.GESTURE;
+ AccessFuTest.runTests();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=981015"
+ title="AccessFu tests for gesture tracker.">
+ Mozilla Bug 981015
+ </a>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_hints.html b/accessible/tests/mochitest/jsat/test_hints.html
new file mode 100644
index 000000000..d5691b97a
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_hints.html
@@ -0,0 +1,89 @@
+<html>
+<head>
+ <title> [AccessFu] Interaction Hints </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="output.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ var tests = [{
+ accOrElmOrID: 'can_wheel',
+ expectedHints: ['Swipe with two fingers to move between pages']
+ }, {
+ accOrElmOrID: 'nested_link',
+ expectedHints: [{string: 'link-hint'},
+ 'Swipe with two fingers to move between pages']
+ }, {
+ accOrElmOrID: 'nested_link',
+ oldAccOrElmOrID: 'can_wheel',
+ expectedHints: [{string: 'link-hint'}]
+ }, {
+ accOrElmOrID: 'link_with_default_hint',
+ expectedHints: [{string: 'link-hint'}]
+ }, {
+ accOrElmOrID: 'link_with_hint_override',
+ expectedHints: ['Tap and hold to get to menu']
+ }, {
+ accOrElmOrID: 'button_with_default_hint',
+ expectedHints: [{string: 'pushbutton-hint'}]
+ }, {
+ accOrElmOrID: 'button_with_hint_override',
+ expectedHints: ['Tap and hold to activate']
+ }, {
+ accOrElmOrID: 'nested_link2',
+ expectedHints: [{string: 'link-hint'}]
+ }, {
+ accOrElmOrID: 'nested_link3',
+ expectedHints: [{string: 'link-hint'}, {string: 'pushbutton-hint'},
+ "Double tap and hold to activate"]
+ }, {
+ accOrElmOrID: 'menuitemradio',
+ expectedHints: [{string: 'radiomenuitem-hint'}]
+ }];
+
+ // Test hints.
+ tests.forEach(function run(test) {
+ testHints(test.expectedHints, test.accOrElmOrID, test.oldAccOrElmOrID);
+ });
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1069574"
+ title="[AccessFu] Interaction Hints">
+ Mozilla Bug 1069574
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <span role="region" id="can_wheel" aria-moz-hint="Swipe with two fingers to move between pages">
+ <a href="#" id="nested_link">I can be clicked</a>
+ </span>
+ <span role="region" aria-moz-hint="">
+ <a><a href="#" id="nested_link2">I can be clicked</a></a>
+ </span>
+ <span role="region" aria-moz-hint="Double tap and hold to activate">
+ <button><a href="#" id="nested_link3">I can be clicked</a></button>
+ </span>
+ <a href="#" id="link_with_default_hint">I can be clicked</a>
+ <a href="#" id="link_with_hint_override" aria-moz-hint="Tap and hold to get to menu">I am a special link</a>
+ <button id="button_with_default_hint">Toggle</button>
+ <button id="button_with_hint_override" aria-moz-hint="Tap and hold to activate">Special</button>
+ <span id="menuitemradio" role="menuitemradio">Item 1</span>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_landmarks.html b/accessible/tests/mochitest/jsat/test_landmarks.html
new file mode 100644
index 000000000..8b1a16f83
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_landmarks.html
@@ -0,0 +1,183 @@
+<html>
+<head>
+ <title> [AccessFu] Speak landmarks</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="output.js"></script>
+ <script type="application/javascript"
+ src="jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "nav",
+ expectedUtterance: [[{"string": "navigation"}, "a nav"],
+ ["a nav", {"string": "navigation"}]],
+ expectedBraille: [[{"string": "navigation"}, "a nav"],
+ ["a nav", {"string": "navigation"}]]
+ }, {
+ accOrElmOrID: "main",
+ expectedUtterance: [[{"string": "main"}, "a main area"],
+ ["a main area", {"string": "main"}]],
+ expectedBraille: [[{"string": "main"}, "a main area"],
+ ["a main area", {"string": "main"}]]
+ }, {
+ accOrElmOrID: "header",
+ expectedUtterance: [
+ [{"string": "banner"}, {"string": "header"}, "a header"],
+ ["a header", {"string": "header"}, {"string": "banner"}]],
+ expectedBraille: [
+ [{"string": "banner"}, {"string": "headerAbbr"}, "a header"],
+ ["a header", {"string": "headerAbbr"}, {"string": "banner"}]]
+ }, {
+ accOrElmOrID: "footer",
+ expectedUtterance: [
+ [{"string": "contentinfo"}, {"string": "footer"}, "a footer"],
+ ["a footer", {"string": "footer"}, {"string": "contentinfo"}]],
+ expectedBraille: [
+ [{"string": "contentinfo"}, {"string": "footerAbbr"}, "a footer"],
+ ["a footer", {"string": "footerAbbr"}, {"string": "contentinfo"}]]
+ }, {
+ accOrElmOrID: "article_header",
+ expectedUtterance: [
+ [{"string": "header"}, "a header within an article"],
+ ["a header within an article", {"string": "header"}]],
+ expectedBraille: [
+ [{"string": "headerAbbr"}, "a header within an article"],
+ ["a header within an article", {"string": "headerAbbr"}]],
+ }, {
+ accOrElmOrID: "article_footer",
+ expectedUtterance: [
+ [{"string": "footer"}, "a footer within an article"],
+ ["a footer within an article", {"string": "footer"}]],
+ expectedBraille: [
+ [{"string": "footerAbbr"}, "a footer within an article"],
+ ["a footer within an article", {"string": "footerAbbr"}]]
+ }, {
+ accOrElmOrID: "section_header",
+ expectedUtterance: [[{"string":"header"}, "a header within a section"],
+ ["a header within a section", {"string":"header"}]],
+ expectedBraille: [
+ [{"string":"headerAbbr"}, "a header within a section"],
+ ["a header within a section", {"string":"headerAbbr"}]]
+ }, {
+ accOrElmOrID: "section_footer",
+ expectedUtterance: [
+ [{"string": "footer"}, "a footer within a section"],
+ ["a footer within a section", {"string": "footer"}]],
+ expectedBraille: [
+ [{"string": "footerAbbr"}, "a footer within a section"],
+ ["a footer within a section", {"string": "footerAbbr"}]]
+ }, {
+ accOrElmOrID: "aside",
+ expectedUtterance: [
+ [{"string": "complementary"}, "by the way I am an aside"],
+ ["by the way I am an aside", {"string": "complementary"}]],
+ expectedBraille: [
+ [{"string": "complementary"}, "by the way I am an aside"],
+ ["by the way I am an aside", {"string": "complementary"}]]
+ }, {
+ accOrElmOrID: "main_element",
+ expectedUtterance: [[{"string": "main"}, "another main area"],
+ ["another main area", {"string": "main"}]],
+ expectedBraille: [[{"string": "main"}, "another main area"],
+ ["another main area", {"string": "main"}]]
+ }, {
+ accOrElmOrID: "complementary",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "complementary"},
+ {"string": "listStart"}, "A complementary"], ["A complementary",
+ {"string": "listStart"}, {"string": "complementary"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]],
+ expectedBraille: [["*", {"string": "complementary"}, "A complementary"],
+ ["*", "A complementary", {"string": "complementary"}]]
+ }, {
+ accOrElmOrID: "parent_main",
+ expectedUtterance: [[{"string": "main"}, "a parent main",
+ {"string": "complementary"}, "a child complementary"],
+ ["a parent main", "a child complementary",
+ {"string": "complementary"}, {"string": "main"}]],
+ expectedBraille: [[{"string": "main"}, "a parent main",
+ {"string": "complementary"}, "a child complementary"],
+ ["a parent main", "a child complementary",
+ {"string": "complementary"}, {"string": "main"}]]
+ }, {
+ accOrElmOrID: "child_complementary",
+ expectedUtterance: [[{"string": "main"}, {"string": "complementary"},
+ "a child complementary"], ["a child complementary",
+ {"string": "complementary"}, {"string": "main"}]],
+ expectedBraille: [[{"string": "complementary"},
+ "a child complementary"], ["a child complementary",
+ {"string": "complementary"}]]
+ }];
+
+ // Test outputs (utterance and braille) for landmarks.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=888256"
+ title="[AccessFu] Speak landmarks">
+ Mozilla Bug 888256
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <nav id="nav">a nav</nav>
+ <header id="header">a header</header>
+ <footer id="footer">a footer</footer>
+ <article id="article_with_header_and_footer">
+ <header id="article_header">a header within an article</header>
+ <footer id="article_footer">a footer within an article</footer>
+ </article>
+ <section id="section_with_header_and_footer">
+ <header id="section_header">a header within a section</header>
+ <footer id="section_footer">a footer within a section</footer>
+ </section>
+ <aside id="aside">by the way I am an aside</aside>
+ <article id="main" role="main">a main area</article>
+ <main id="main_element">another main area</main>
+ <ul style="list-style-type: none;">
+ <li role="complementary" id="complementary">
+ A complementary
+ </li>
+ </ul>
+ <main id="parent_main">
+ a parent main
+ <p id="child_complementary" role="complementary">a child complementary</article>
+ </main>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_live_regions.html b/accessible/tests/mochitest/jsat/test_live_regions.html
new file mode 100644
index 000000000..53828f1b1
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_live_regions.html
@@ -0,0 +1,472 @@
+<html>
+
+<head>
+ <title>AccessFu tests for live regions support</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="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function startAccessFu() {
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 1]]});
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ }
+
+ function stopAccessFu() {
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 0]]});
+ AccessFuTest.once_log("EventManager.stop", () => AccessFuTest.finish());
+ }
+
+ function hide(id) {
+ var element = document.getElementById(id);
+ element.style.display = "none";
+ }
+
+ function show(id) {
+ var element = document.getElementById(id);
+ element.style.display = "block";
+ }
+
+ function ariaHide(id) {
+ var element = document.getElementById(id);
+ element.setAttribute('aria-hidden', true);
+ }
+
+ function ariaShow(id) {
+ var element = document.getElementById(id);
+ element.setAttribute('aria-hidden', false);
+ }
+
+ function udpate(id, text, property) {
+ var element = document.getElementById(id);
+ element[property] = text;
+ }
+
+ function updateText(id, text) {
+ udpate(id, text, "textContent");
+ }
+
+ function updateHTML(id, text) {
+ udpate(id, text, "innerHTML");
+ }
+
+ var tests = [{
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide1", "to_hide2", "to_hide3", "to_hide4"].forEach(id => hide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide_descendant1", "to_hide_descendant2",
+ "to_hide_descendant3", "to_hide_descendant4"].forEach(id => hide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show1", "to_show2", "to_show3", "to_show4"].forEach(id => show(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show_descendant1", "to_show_descendant2",
+ "to_show_descendant3", "to_show_descendant4"].forEach(id => show(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide5", "to_hide6", "to_hide7", "to_hide8", "to_hide9"].forEach(id => ariaHide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_hide_descendant5", "to_hide_descendant6",
+ "to_hide_descendant7", "to_hide_descendant8"].forEach(id => ariaHide(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show5", "to_show6", "to_show7", "to_show8", "to_show9"].forEach(id => ariaShow(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ ["to_show_descendant5", "to_show_descendant6",
+ "to_show_descendant7", "to_show_descendant8"].forEach(id => ariaShow(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ hide("to_hide_live_assertive");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "I will be hidden"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ ariaHide("to_hide_live_assertive2");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ ["to_show_live_off", "to_show_live_assertive"].forEach(id => show(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I will be shown"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ ["to_show_live_off2", "to_show_live_assertive2"].forEach(id => ariaShow(id));
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("text_add", "Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("text_add", "Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "Text Removed"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ updateText("text_remove", "");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("text_add_descendant", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("text_add_descendant", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "Descendant Text Removed"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ updateText("text_remove_descendant", "");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("text_add_descendant2", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["Descendant Text Added"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("text_add_descendant2", "Descendant Text Added");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": [{"string": "hidden"}, "Descendant Text Removed"],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ updateText("text_remove_descendant2", "");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I am replaced", {"string": "main"}],
+ "options": {
+ "enqueue": true
+ }
+ },
+ action: function action() {
+ var region = document.getElementById("to_replace_region");
+ var child = document.getElementById("to_replace");
+ child.setAttribute("role", "main");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I am a replaced text"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateText("to_replace_text", "I am a replaced text");
+ }
+ }, {
+ expected: {
+ "eventType": "liveregion-change",
+ "data": ["I am a replaced text"],
+ "options": {
+ "enqueue": false
+ }
+ },
+ action: function action() {
+ updateHTML("to_replace_text", "I am a replaced text");
+ }
+ }];
+
+ function doTest() {
+ AccessFuTest.addFunc(startAccessFu);
+ tests.forEach(function addTest(test) {
+ AccessFuTest.addFunc(function () {
+ AccessFuTest.once(test.expected, AccessFuTest.nextTest);
+ test.action();
+ });
+ });
+ AccessFuTest.addFunc(stopAccessFu);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=795957"
+ title="[AccessFu] Support live regions">
+ Mozilla Bug 795957
+ </a>
+ <div id="root">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+
+ <p id="to_hide1">I should not be announced 1</p>
+ <p id="to_hide2" aria-live="polite">I should not be announced 2</p>
+ <p id="to_hide3" aria-live="assertive" aria-relevant="text">I should not be announced 3</p>
+ <p id="to_hide4" aria-live="polite" aria-relevant="all">I will be hidden</p>
+
+ <p id="to_hide5" aria-hidden="true">I should not be announced 5</p>
+ <p id="to_hide6">I should not be announced 6</p>
+ <p id="to_hide7" aria-live="polite">I should not be announced 7</p>
+ <p id="to_hide8" aria-live="assertive" aria-relevant="text">I should not be announced 8</p>
+ <p id="to_hide9" aria-live="polite" aria-relevant="all">I will be hidden</p>
+
+ <div>
+ <p id="to_hide_descendant1">I should not be announced 1</p>
+ </div>
+ <div aria-live="polite">
+ <p id="to_hide_descendant2">I should not be announced 2</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_hide_descendant3">I should not be announced 3</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_hide_descendant4">I will be hidden</p>
+ </div>
+
+ <div>
+ <p id="to_hide_descendant5">I should not be announced 4</p>
+ </div>
+ <div aria-live="polite">
+ <p id="to_hide_descendant6">I should not be announced 5</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_hide_descendant7">I should not be announced 6</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_hide_descendant8">I will be hidden</p>
+ </div>
+
+ <p id="to_show1" style="display: none">I should not be announced 1</p>
+ <p id="to_show2" aria-live="assertive" aria-relevant="text" style="display: none">I should not be announced 2</p>
+ <p id="to_show3" aria-live="polite" aria-relevant="removals" style="display: none">I should not be announced 3</p>
+ <p id="to_show4" aria-live="polite" aria-relevant="all" style="display: none">I will be shown</p>
+
+ <p id="to_show5" aria-hidden="false">I should not be announced 5</p>
+ <p id="to_show6" aria-hidden="true">I should not be announced 6</p>
+ <p id="to_show7" aria-hidden="true" aria-live="assertive" aria-relevant="text">I should not be announced 7</p>
+ <p id="to_show8" aria-hidden="true" aria-live="polite" aria-relevant="removals">I should not be announced 8</p>
+ <p id="to_show9" aria-hidden="true" aria-live="polite" aria-relevant="all">I will be shown</p>
+
+ <div>
+ <p id="to_show_descendant1" style="display: none">I should not be announced 1</p>
+ </div>
+ <div aria-live="polite" aria-relevant="removals">
+ <p id="to_show_descendant2" style="display: none">I should not be announced 2</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_show_descendant3" style="display: none">I should not be announced 3</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_show_descendant4" style="display: none">I will be shown</p>
+ </div>
+
+ <div>
+ <p id="to_show_descendant5" aria-hidden="true">I should not be announced 5</p>
+ </div>
+ <div aria-live="polite" aria-relevant="removals">
+ <p id="to_show_descendant6" aria-hidden="true">I should not be announced 6</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="to_show_descendant7" aria-hidden="true">I should not be announced 7</p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="to_show_descendant8" aria-hidden="true">I will be shown</p>
+ </div>
+
+ <div aria-live="assertive" aria-relevant="all">
+ <p id="to_hide_live_assertive">I will be hidden</p>
+ </div>
+
+ <div aria-live="assertive" aria-relevant="all">
+ <p id="to_hide_live_assertive2">I will be hidden</p>
+ </div>
+ <p id="to_show_live_assertive" aria-live="assertive" style="display: none">I will be shown</p>
+
+ <p id="to_show_live_off" aria-live="off" style="display: none">I will not be shown</p>
+
+ <p id="to_show_live_assertive2" aria-live="assertive" aria-hidden="true">I will be shown</p>
+
+ <p id="to_show_live_off2" aria-live="off" aria-hidden="true">I will not be shown</p>
+
+ <div id="to_replace_region" aria-live="polite" aria-relevant="all">
+ <p id="to_replace">I am replaced</p>
+ </div>
+
+ <p id="to_replace_text" aria-live="assertive" aria-relevant="text">I am going to be replaced</p>
+
+ <p id="text_add" aria-live="assertive" aria-relevant="text"></p>
+ <p id="text_remove" aria-live="polite" aria-relevant="all">Text Removed</p>
+
+ <div aria-live="assertive" aria-relevant="all">
+ <p id="text_add_descendant"></p>
+ </div>
+ <div aria-live="polite" aria-relevant="text">
+ <p id="text_remove_descendant">Descendant Text Removed</p>
+ </div>
+ <div aria-live="assertive" aria-relevant="text">
+ <p id="text_add_descendant2"></p>
+ </div>
+ <div aria-live="polite" aria-relevant="all">
+ <p id="text_remove_descendant2">Descendant Text Removed</p>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_output.html b/accessible/tests/mochitest/jsat/test_output.html
new file mode 100644
index 000000000..ec2b289be
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -0,0 +1,673 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=753984
+-->
+ <head>
+ <title>[AccessFu] utterance order test</title>
+ <meta charset="utf-8">
+ <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="./output.js"></script>
+ <script type="application/javascript"
+ src="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID (with optional old accOrElmOrID).
+ // Note: each accOrElmOrID entry maps to a unique object utterance
+ // generator function within the UtteranceGenerator.
+ var tests = [{
+ accOrElmOrID: "anchor",
+ expectedUtterance: [[{"string": "link"}, "title"],
+ ["title", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"}, "title"],
+ ["title", {"string": "linkAbbr"}]]
+ }, {
+ accOrElmOrID: "anchor_titleandtext",
+ expectedUtterance: [[{"string": "link"}, "goes to the tests -",
+ "Tests"], ["Tests", "- goes to the tests", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"}, "goes to the tests -",
+ "Tests"], ["Tests", "- goes to the tests", {"string": "linkAbbr"}]],
+ }, {
+ accOrElmOrID: "anchor_duplicatedtitleandtext",
+ expectedUtterance: [[{"string": "link"}, "Tests"],
+ ["Tests", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"}, "Tests"],
+ ["Tests", {"string": "linkAbbr"}]]
+ }, {
+ accOrElmOrID: "anchor_arialabelandtext",
+ expectedUtterance: [[{"string": "link"}, "goes to the tests - Tests"],
+ ["Tests - goes to the tests", {"string": "link"}]],
+ expectedBraille: [[{"string": "linkAbbr"},
+ "goes to the tests - Tests"], ["Tests - goes to the tests",
+ {"string": "linkAbbr"}]],
+ }, {
+ accOrElmOrID: "textarea",
+ expectedUtterance: [[{"string": "textarea"},
+ "This is the text area text."], ["This is the text area text.",
+ {"string": "textarea"}]],
+ expectedBraille: [[{"string": "textareaAbbr"},
+ "This is the text area text."], ["This is the text area text.",
+ {"string": "textareaAbbr"}]],
+ }, {
+ accOrElmOrID: "heading",
+ expectedUtterance: [[{"string": "headingLevel", "args": [1]},
+ "Test heading"], ["Test heading",
+ {"string": "headingLevel", "args": [1]}]],
+ expectedBraille: [[{"string": "headingAbbr"}, "Test heading"],
+ ["Test heading", {"string": "headingAbbr"}]]
+ }, {
+ accOrElmOrID: "list",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count":1}, {"string": "listStart"},
+ "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count":1}]
+ ],
+ expectedBraille: [[{"string": "listAbbr"}, "list one"],
+ ["list one", {"string": "listAbbr"}]]
+ }, {
+ accOrElmOrID: "dlist",
+ expectedUtterance: [[{"string": "definitionlist"},
+ {"string": "listItemsCount", "count": 0.5}, "dd one"], ["dd one",
+ {"string": "definitionlist"},
+ {"string": "listItemsCount", "count": 0.5}]
+ ],
+ expectedBraille: [[{"string": "definitionlistAbbr"}, "dd one"],
+ ["dd one", {"string": "definitionlistAbbr"}]]
+ }, {
+ accOrElmOrID: "li_one",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+ "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]
+ ],
+ expectedBraille: [["1.", "list one"], ["1.", "list one"]]
+ },
+ {
+ accOrElmOrID: "li_two",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+ "list two"], ["list two", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]
+ ],
+ expectedBraille: [["*", "list two"], ["*", "list two"]]
+ }, {
+ accOrElmOrID: "cell",
+ expectedUtterance: [[{"string":"table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, {"string": "list"},
+ {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+ {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
+ {"string": "link"}, "Peaches", {"string": "listEnd"},
+ {"string": "link"}, "Plums"], ["Apples", {"string": "link"},
+ {"string": "listStart"}, "Bananas", {"string": "link"}, "Peaches",
+ {"string": "link"}, "Plums", {"string": "link"},
+ {"string": "listEnd"}, {"string": "list"},
+ {"string": "listItemsCount", "count": 4},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "Fruits and vegetables",
+ {"string":"table"}, {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 1}]],
+ expectedBraille: [[{"string": "cellInfoAbbr", "args": [ 1, 1]},
+ {"string": "listAbbr"}, {"string": "linkAbbr"}, "Apples",
+ {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
+ "Peaches", {"string": "linkAbbr"}, "Plums"], ["Apples",
+ {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
+ "Peaches", {"string": "linkAbbr"}, "Plums", {"string": "linkAbbr"},
+ {"string": "listAbbr"},
+ {"string": "cellInfoAbbr", "args": [ 1, 1]}]]
+ }, {
+ accOrElmOrID: "date",
+ expectedUtterance: [[{"string": "textInputType_date"},
+ {"string": "entry"}, "2011-09-29"], ["2011-09-29",
+ {"string": "textInputType_date"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_date"},
+ {"string": "entryAbbr"}, "2011-09-29"], ["2011-09-29",
+ {"string": "textInputType_date"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "email",
+ expectedUtterance: [[{"string": "textInputType_email"},
+ {"string": "entry"}, "test@example.com"], ["test@example.com",
+ {"string": "textInputType_email"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_email"},
+ {"string": "entryAbbr"}, "test@example.com"], ["test@example.com",
+ {"string": "textInputType_email"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "search",
+ expectedUtterance: [[{"string": "textInputType_search"},
+ {"string": "entry"}, "This is a search"], ["This is a search",
+ {"string": "textInputType_search"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_search"},
+ {"string": "entryAbbr"}, "This is a search"], ["This is a search",
+ {"string": "textInputType_search"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "tel",
+ expectedUtterance: [[{"string": "textInputType_tel"},
+ {"string": "entry"}, "555-5555"], ["555-5555",
+ {"string": "textInputType_tel"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_tel"},
+ {"string": "entryAbbr"}, "555-5555"], ["555-5555",
+ {"string": "textInputType_tel"}, {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "url",
+ expectedUtterance: [[{"string": "textInputType_url"},
+ {"string": "entry"}, "http://example.com"], ["http://example.com",
+ {"string": "textInputType_url"}, {"string": "entry"}]],
+ expectedBraille: [[{"string": "textInputType_url"},
+ {"string": "entryAbbr"}, "http://example.com"],
+ ["http://example.com", {"string": "textInputType_url"},
+ {"string": "entryAbbr"}]]
+ }, {
+ accOrElmOrID: "textInput",
+ expectedUtterance: [[{"string": "entry"}, "This is text."],
+ ["This is text.", {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "This is text."],
+ ["This is text.", {"string": "entryAbbr"}]]
+ }, {
+ // Test pivot to list from li_one.
+ accOrElmOrID: "list",
+ oldAccOrElmOrID: "li_one",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+ "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+ {"string": "list"}, {"string": "listItemsCount", "count": 1}]
+ ],
+ expectedBraille: [[{"string": "listAbbr"}, "list one"],
+ ["list one", {"string": "listAbbr"}]]
+ }, {
+ // Test pivot to "apples" link from the table cell.
+ accOrElmOrID: "apples",
+ oldAccOrElmOrID: "cell",
+ expectedUtterance: [[{"string": "list"},
+ {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+ {"string": "link"}, "Apples"], ["Apples", {"string": "link"},
+ {"string": "listStart"}, {"string": "list"},
+ {"string": "listItemsCount", "count": 4}]
+ ],
+ expectedBraille: [["*", {"string": "linkAbbr"}, "Apples"],
+ ["*", "Apples", {"string": "linkAbbr"}]]
+ }, {
+ // Test pivot to "bananas" link from "apples" link.
+ accOrElmOrID: "bananas",
+ oldAccOrElmOrID: "apples",
+ expectedUtterance: [[{"string": "link"}, "Bananas"],
+ ["Bananas", {"string": "link"}]],
+ expectedBraille: [["*", {"string": "linkAbbr"}, "Bananas"],
+ ["*", "Bananas", {"string": "linkAbbr"}]]
+ }, {
+ // test unavailable state utterance
+ accOrElmOrID: "unavailableButton",
+ expectedUtterance: [[{"string": "stateUnavailable"},
+ {"string": "pushbutton"}, "I am unavailable"], ["I am unavailable",
+ {"string": "stateUnavailable"}, {"string": "pushbutton"}]],
+ expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am unavailable"],
+ ["I am unavailable", {"string": "pushbuttonAbbr"}]]
+ }, {
+ // test expanded state utterance
+ accOrElmOrID: "expandedButton",
+ expectedUtterance: [[{"string": "stateExpanded"},
+ {"string": "pushbutton"}, "I am expanded"], ["I am expanded",
+ {"string": "stateExpanded"}, {"string": "pushbutton"}]],
+ expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am expanded"],
+ ["I am expanded", {"string": "pushbuttonAbbr"}]]
+ }, {
+ // test collapsed state utterance
+ accOrElmOrID: "collapsedButton",
+ expectedUtterance: [[{"string": "stateCollapsed"},
+ {"string": "pushbutton"}, "I am collapsed"], ["I am collapsed",
+ {"string": "stateCollapsed"}, {"string": "pushbutton"}]],
+ expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am collapsed"],
+ ["I am collapsed", {"string": "pushbuttonAbbr"}]]
+ }, {
+ // test required state utterance
+ accOrElmOrID: "requiredInput",
+ expectedUtterance: [[{"string": "stateRequired"}, {"string": "entry"},
+ "I am required"], ["I am required", {"string": "stateRequired"},
+ {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "I am required"],
+ ["I am required", {"string": "entryAbbr"}]]
+ }, {
+ // test unavailable state utterance on inputs
+ accOrElmOrID: "readonlyInput",
+ expectedUtterance: [[{"string": "stateReadonly"}, {"string": "entry"},
+ "No edits"], ["No edits", {"string": "stateReadonly"},
+ {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "No edits"],
+ ["No edits", {"string": "entryAbbr"}]]
+ }, {
+ // test unavailable state utterance on textareas
+ accOrElmOrID: "readonlyTextarea",
+ expectedUtterance: [[{"string": "stateReadonly"}, {"string": "textarea"},
+ "No editing"], ["No editing", {"string": "stateReadonly"},
+ {"string": "textarea"}]],
+ expectedBraille: [[{"string": "textareaAbbr"}, "No editing"],
+ ["No editing", {"string": "textareaAbbr"}]]
+ }, {
+ // test has popup state utterance
+ accOrElmOrID: "hasPopupButton",
+ expectedUtterance: [[{"string": "stateHasPopup"},
+ {"string": "buttonmenu"}, "I have a popup"], ["I have a popup",
+ {"string": "stateHasPopup"}, {"string": "buttonmenu"}]],
+ expectedBraille: [[{"string": "buttonmenuAbbr"}, "I have a popup"],
+ ["I have a popup", {"string": "buttonmenuAbbr"}]]
+ }, {
+ // Test selected tab
+ accOrElmOrID: "tab1",
+ expectedUtterance: [[{"string": "pagetablist"},
+ {"string": "stateSelected"}, {"string": "pagetab"},
+ {"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
+ {"string": "stateSelected"}, {"string": "pagetab"},
+ {"string": "objItemOfN", "args": [1, 2]}, {"string": "pagetablist"}]
+ ],
+ expectedBraille: [[{"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
+ {"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [1, 2]}]]
+ }, {
+ // Test unselected tab
+ accOrElmOrID: "tab2",
+ expectedUtterance: [[{"string": "pagetablist"}, {"string": "pagetab"},
+ {"string": "objItemOfN", "args": [2, 2]}, "Advanced"], ["Advanced",
+ {"string": "pagetab"}, {"string": "objItemOfN", "args": [2, 2]},
+ {"string": "pagetablist"}]],
+ expectedBraille: [[{"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [2, 2]}, "Advanced"], ["Advanced",
+ {"string": "pagetabAbbr"},
+ {"string": "objItemOfN", "args": [2, 2]}]]
+ }, {
+ // Landing on this label should mimic landing on the checkbox.
+ accOrElmOrID: "label1",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Orange"], ["Orange",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
+ ["Orange", {"string": "stateUncheckedAbbr"}]]
+ }, {
+ // Here we get a top-level view of the form.
+ accOrElmOrID: "form1",
+ expectedUtterance: [[{"string": "label"},
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
+ "Orange", {"string": "stateNotChecked"}, {"string": "checkbutton"},
+ "Blue", {"string": "label"}, "Blue"], ["Orange",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
+ {"string": "label"}, "Blue", {"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Blue", {"string": "label"}]],
+ expectedBraille: [[{"string": "labelAbbr"},
+ {"string": "stateUncheckedAbbr"}, "Orange", "Orange",
+ {"string": "stateUncheckedAbbr"}, "Blue", {"string": "labelAbbr"},
+ "Blue"], ["Orange", {"string": "stateUncheckedAbbr"}, "Orange",
+ {"string": "labelAbbr"}, "Blue", {"string": "stateUncheckedAbbr"},
+ "Blue", {"string": "labelAbbr"}]]
+ }, {
+ // This is a non-nesting label.
+ accOrElmOrID: "label2",
+ expectedUtterance: [[{"string": "label"}, "Blue"],
+ ["Blue", {"string": "label"}]],
+ expectedBraille: [[{"string": "labelAbbr"}, "Blue"],
+ ["Blue", {"string": "labelAbbr"}]]
+ }, {
+ // This is a distinct control.
+ accOrElmOrID: "input2",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Blue"], ["Blue",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Blue"], ["Blue",
+ {"string": "stateUncheckedAbbr"}]]
+ }, {
+ // This is a nested control.
+ accOrElmOrID: "input1",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "checkbutton"}, "Orange"], ["Orange",
+ {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
+ ["Orange", {"string": "stateUncheckedAbbr"}]]
+ }, {
+ // Landing on this label should mimic landing on the entry.
+ accOrElmOrID: "label3",
+ expectedUtterance: [[{"string": "entry"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entryAbbr"}]]
+ }, {
+ // This is a nested control with a value.
+ accOrElmOrID: "input3",
+ expectedUtterance: [[{"string": "entry"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entry"}]],
+ expectedBraille: [[{"string": "entryAbbr"}, "Joe", "First name:"],
+ ["First name:", "Joe", {"string": "entryAbbr"}]]
+ }, {
+ // This is a nested control with a value.
+ accOrElmOrID: "input4",
+ expectedUtterance: [[{"string": "slider"}, "3", "Points:"],
+ ["Points:", "3", {"string": "slider"}]],
+ expectedBraille: [[{"string": "sliderAbbr"}, "3", "Points:"],
+ ["Points:", "3", {"string": "sliderAbbr"}]]
+ }, {
+ accOrElmOrID: "password",
+ expectedUtterance: [[{"string": "passwordtext"}, "Secret Password"],
+ ["Secret Password", {"string": "passwordtext"}]],
+ expectedBraille: [[{"string": "passwordtextAbbr"}, "Secret Password"],
+ ["Secret Password", {"string": "passwordtextAbbr"}]]
+ }, {
+ accOrElmOrID: "input5",
+ expectedUtterance: [[{"string": "stateChecked"},
+ {"string": "checkbutton"}, "Boring label"], ["Boring label",
+ {"string": "stateChecked"}, {"string": "checkbutton"}]],
+ expectedBraille: [[{"string": "stateCheckedAbbr"}, "Boring label"],
+ ["Boring label", {"string": "stateCheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "radio_unselected",
+ expectedUtterance: [[{"string": "stateNotChecked"},
+ {"string": "radiobutton"}, "any old radio button"],
+ ["any old radio button", {"string": "stateNotChecked"},
+ {"string": "radiobutton"}]
+ ],
+ expectedBraille: [
+ [{"string": "stateUncheckedAbbr"}, "any old radio button"],
+ ["any old radio button", {"string": "stateUncheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "radio_selected",
+ expectedUtterance: [[{"string": "stateChecked"},
+ {"string": "radiobutton"}, "a unique radio button"],
+ ["a unique radio button", {"string": "stateChecked"},
+ {"string": "radiobutton"}]],
+ expectedBraille: [
+ [{"string": "stateCheckedAbbr"}, "a unique radio button"],
+ ["a unique radio button", {"string": "stateCheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "togglebutton_notpressed",
+ expectedUtterance: [[{"string": "togglebutton"}, "I am not pressed"],
+ ["I am not pressed", {"string": "togglebutton"}]],
+ expectedBraille: [
+ [{"string": "stateUnpressedAbbr"}, "I am not pressed"],
+ ["I am not pressed", {"string": "stateUnpressedAbbr"}]]
+ }, {
+ accOrElmOrID: "togglebutton_pressed",
+ expectedUtterance: [[{"string": "statePressed"},
+ {"string": "togglebutton"}, "I am pressed!"], ["I am pressed!",
+ {"string": "statePressed"}, {"string": "togglebutton"}]],
+ expectedBraille: [[{"string": "statePressedAbbr"}, "I am pressed!"],
+ ["I am pressed!", {"string": "statePressedAbbr"}]]
+ }, {
+ accOrElmOrID: "listbox-option",
+ expectedUtterance: [[{"string": "listbox"},
+ {"string": "listboxoption"}, "Search suggestion"],
+ ["Search suggestion", {"string": "listboxoption"},
+ {"string": "listbox"}]
+ ],
+ expectedBraille: [
+ [{"string": "listboxoptionAbbr"}, "Search suggestion"],
+ ["Search suggestion", {"string": "listboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "listbox-option2",
+ oldAccOrElmOrID: "listbox-option",
+ expectedUtterance: [[{"string": "listboxoption"}, "555-12345"],
+ ["555-12345", {"string": "listboxoption"}]],
+ expectedBraille: [[{"string": "listboxoptionAbbr"}, "555-12345"],
+ ["555-12345", {"string": "listboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "columnheader",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [[{"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args" :[1]}, "Sunday"], ["Sunday",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args" :[1]}]],
+ expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 1]},
+ "Sunday"], ["Sunday", {"string": "cellInfoAbbr", "args": [1, 1]}]]
+ }, {
+ accOrElmOrID: "rowheader",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [[{"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "Sunday", "Week 1"], ["Week 1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "Sunday"]],
+ expectedBraille: [[{"string": "cellInfoAbbr", "args": [1, 2]},
+ "Sunday", "Week 1"], ["Week 1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "Sunday"]]
+ }, {
+ accOrElmOrID: "gridcell1",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [["3"], ["3"]],
+ expectedBraille: [["3"], ["3"]]
+ }, {
+ accOrElmOrID: "gridcell2",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [["4", "7"], ["4", "7"]],
+ expectedBraille: [["4", "7"], ["4", "7"]]
+ }, {
+ accOrElmOrID: "gridcell3",
+ oldAccOrElmOrID: "grid",
+ expectedUtterance: [[{"string": "stateSelected"}, "5"],
+ ["5", {"string": "stateSelected"}]],
+ expectedBraille: [["5"], ["5"]],
+ }, {
+ accOrElmOrID: "frequency",
+ expectedUtterance: [[{"string": "stateCollapsed"},
+ {"string": "stateHasPopup"}, {"string": "combobox"}, "15 min"], [
+ "15 min", {"string": "stateCollapsed"}, {"string": "stateHasPopup"},
+ {"string": "combobox"}]],
+ expectedBraille: [[{"string": "comboboxAbbr"}, "15 min"], ["15 min",
+ {"string": "comboboxAbbr"}]]
+ }, {
+ accOrElmOrID: "selected-combobox-option",
+ oldAccOrElmOrID: "frequency",
+ expectedUtterance: [[{"string": "stateSelected"},
+ {"string": "comboboxoption"}, "15 min"], ["15 min",
+ {"string": "stateSelected"}, {"string": "comboboxoption"}]],
+ expectedBraille: [[{"string": "comboboxoptionAbbr"}, "15 min"], [
+ "15 min", {"string": "comboboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "combobox-option",
+ oldAccOrElmOrID: "frequency",
+ expectedUtterance: [[{"string": "comboboxoption"}, "30 min"], [
+ "30 min", {"string": "comboboxoption"}]],
+ expectedBraille: [[{"string": "comboboxoptionAbbr"}, "30 min"], [
+ "30 min", {"string": "comboboxoptionAbbr"}]]
+ }, {
+ accOrElmOrID: "labelled-combobox",
+ expectedUtterance: [[{"string": "stateCollapsed"},
+ {"string": "stateHasPopup"}, {"string": "combobox"}, "Never",
+ "Intervals"], ["Intervals", "Never", {"string": "stateCollapsed"},
+ {"string": "stateHasPopup"}, {"string": "combobox"}]],
+ expectedBraille: [[{"string": "comboboxAbbr"}, "Never", "Intervals"],
+ ["Intervals", "Never", {"string": "comboboxAbbr"}]]
+ }, {
+ accOrElmOrID: "statusbar-1",
+ expectedUtterance: [["Last sync:", "2 days ago"],
+ ["Last sync:", "2 days ago"]],
+ expectedBraille: [["Last sync:", "2 days ago"],
+ ["Last sync:", "2 days ago"]]
+ }, {
+ accOrElmOrID: "statusbar-2",
+ expectedUtterance: [["Last sync: 30min ago"],
+ ["Last sync: 30min ago"]],
+ expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
+ }, {
+ accOrElmOrID: "switch-1",
+ expectedUtterance: [[{"string": "stateOn"}, {"string": "switch"},
+ "Simple switch"], ["Simple switch", {"string": "stateOn"},
+ {"string": "switch"}]],
+ expectedBraille: [[{"string": "stateCheckedAbbr"}, "Simple switch"],
+ ["Simple switch", {"string": "stateCheckedAbbr"}]]
+ }, {
+ accOrElmOrID: "switch-2",
+ expectedUtterance: [[{"string": "stateOff"},
+ {"string": "switch"}, "Another switch"], ["Another switch",
+ {"string": "stateOff"}, {"string": "switch"}]],
+ expectedBraille: [
+ [{"string": "stateUncheckedAbbr"}, "Another switch"],
+ ["Another switch", {"string": "stateUncheckedAbbr"}]]
+ }];
+
+ // Test all possible utterance order preference values.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+
+ </script>
+ </head>
+ <body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=753984"
+ title="[AccessFu] utterance order test">
+ Mozilla Bug 753984</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=758675"
+ title="[AccessFu] Add support for accDescription">
+ Mozilla Bug 758675</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=876475"
+ title="[AccessFu] Make braille output less verbose">
+ Mozilla Bug 876475</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=924284"
+ title="[AccessFu] Output accessible values">
+ Mozilla Bug 924284</a>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=925845"
+ title="[AccessFu] Unify output tests">
+ Mozilla Bug 925845</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <a id="anchor" href="#test" title="title"></a>
+ <a id="anchor_titleandtext" href="#test" title="goes to the tests">Tests</a>
+ <a id="anchor_duplicatedtitleandtext" href="#test" title="Tests">Tests</a>
+ <a id="anchor_arialabelandtext" href="#test" aria-label="Tests" title="goes to the tests">Tests</a>
+ <textarea id="textarea" cols="80" rows="5">
+ This is the text area text.
+ </textarea>
+ <h1 id="heading" title="Test heading"></h1>
+ <ol id="list">
+ <li id="li_one">list one</li>
+ </ol>
+ <ul id="unorderd_list">
+ <li id="li_two">list two</li>
+ </ul>
+ <dl id="dlist">
+ <dd id="dd_one">
+ dd one
+ </dd>
+ </dl>
+ <table>
+ <caption>Fruits and vegetables</caption>
+ <tr>
+ <td id="cell">
+ <ul style="list-style-type: none;">
+ <li><a id="apples" href="#">Apples</a></li>
+ <li><a id="bananas" href="#">Bananas</a></li>
+ <li><a href="#">Peaches</a></li>
+ <li>
+ <a href="#">
+ Plums
+ </a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+ <button id="unavailableButton" disabled>I am unavailable</button>
+ <button id="expandedButton" aria-expanded="true">I am expanded</button>
+ <button id="collapsedButton" aria-expanded="false">I am collapsed</button>
+ <input id="requiredInput" required placeholder="I am required" />
+ <input id="readonlyInput" readonly value="No edits" />
+ <textarea id="readonlyTextarea" readonly>No editing</textarea>
+ <button id="hasPopupButton" aria-haspopup="true">I have a popup</button>
+ <div role="tablist">
+ <a id="tab1" href="#" role="tab" aria-selected="true">Account</a>
+ <a id="tab2" href="#" role="tab" aria-selected="false">Advanced</a>
+ </div>
+ <form id="form1">
+ <label id="label1"><input id="input1" type="checkbox">Orange</label>
+ <input id="input2" type="checkbox"><label id="label2" for="input2">Blue</label>
+ </form>
+ <label id="label3">First name: <input id="input3" value="Joe"></label>
+ <label id="label4">Points:
+ <input id="input4" type="range" name="points" min="1" max="10" value="3">
+ </label>
+ <label for="input5">Boring label</label><input id="input5" type="checkbox" checked></input>
+ <label for="password">Secret Password</label><input id="password" type="password"></input>
+ <label for="radio_unselected">any old radio button</label><input id="radio_unselected" type="radio"></input>
+ <label for="radio_selected">a unique radio button</label><input id="radio_selected" type="radio" checked></input>
+ <input id="date" type="date" value="2011-09-29" />
+ <input id="email" type="email" value="test@example.com" />
+ <input id="search" type="search" value="This is a search" />
+ <input id="tel" type="tel" value="555-5555" />
+ <input id="url" type="url" value="http://example.com" />
+ <input id="textInput" type="text" value="This is text." />
+ <label>Points: <input id="range" type="range" name="points" min="1" max="10" value="3"></label>
+ <div id="togglebutton_notpressed" aria-pressed="false" role="button" tabindex="-1">I am not pressed</div>
+ <div id="togglebutton_pressed" aria-pressed="true" role="button" tabindex="-1">I am pressed!</div>
+ <ul role="listbox" style="list-style-type: none;">
+ <li role="option" id="listbox-option">Search suggestion</li>
+ <li role="option" id="listbox-option2">
+ <label aria-hidden="true">
+ <input type="checkbox" />
+ </label>
+ 555-12345
+ </li>
+ </ul>
+ <section id="grid" role="grid">
+ <ol role="row">
+ <li role="presentation"></li>
+ <li id="columnheader" role="columnheader" aria-label="Sunday">S</li>
+ <li role="columnheader">M</li>
+ </ol>
+ <ol role="row">
+ <li id="rowheader" role="rowheader" aria-label="Week 1">1</li>
+ <li id="gridcell1" role="gridcell"><span>3</span><div></div></li>
+ <li id="gridcell2" role="gridcell"><span>4</span><div>7</div></li>
+ </ol>
+ <ol role="row">
+ <li role="rowheader">2</li>
+ <li id="gridcell3" aria-selected="true" role="gridcell">5</li>
+ <li role="gridcell">6</li>
+ </ol>
+ </section>
+ <select id="frequency">
+ <option id="selected-combobox-option" value="15">15 min</option>
+ <option id="combobox-option" value="30">30 min</option>
+ <option value="null">Manual</option>
+ </select>
+ <select id="labelled-combobox" aria-label="Intervals">
+ <option value="15">Every 15 min</option>
+ <option value="30">Every 30 min</option>
+ <option value="null" selected>Never</option>
+ </select>
+ <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
+ <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status">I should be ignored</div>
+ <span id="switch-1" role="switch" aria-label="Simple switch" aria-checked="true"></span>
+ <span id="switch-2" role="switch" aria-label="Another switch" aria-checked="false"></span>
+ </div>
+ </body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_output_mathml.html b/accessible/tests/mochitest/jsat/test_output_mathml.html
new file mode 100644
index 000000000..3fe4779b2
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_output_mathml.html
@@ -0,0 +1,313 @@
+<html>
+<head>
+ <title>[AccessFu] MathML Accessibility Support</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="output.js"></script>
+ <script type="application/javascript"
+ src="jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "math-1",
+ expectedUtterance: [
+ [{"string":"open-fence"},"(","x",",","y",{"string":"close-fence"},")"],
+ ["(",{"string":"open-fence"},"x",",","y",")",{"string":"close-fence"}]
+ ],
+ expectedBraille: [
+ [{"string":"open-fenceAbbr"},"(","x",",","y",{"string":"close-fenceAbbr"},")"],
+ ["(",{"string":"open-fenceAbbr"},"x",",","y",")",{"string":"close-fenceAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mfrac-1",
+ expectedUtterance: [
+ [{"string":"mathmlfraction"},{"string":"numerator"},"a",{"string":"denominator"},"b"],
+ ["a",{"string":"numerator"},"b",{"string":"denominator"},{"string":"mathmlfraction"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlfractionAbbr"},{"string":"numeratorAbbr"},"a",{"string":"denominatorAbbr"},"b"],
+ ["a",{"string":"numeratorAbbr"},"b",{"string":"denominatorAbbr"},{"string":"mathmlfractionAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mfrac-2",
+ expectedUtterance: [
+ [{"string":"mathmlfractionwithoutbar"},{"string":"numerator"},"a",{"string":"denominator"},"b"],
+ ["a",{"string":"numerator"},"b",{"string":"denominator"},{"string":"mathmlfractionwithoutbar"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlfractionwithoutbarAbbr"},{"string":"numeratorAbbr"},"a",{"string":"denominatorAbbr"},"b"],
+ ["a",{"string":"numeratorAbbr"},"b",{"string":"denominatorAbbr"},{"string":"mathmlfractionwithoutbarAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "msub-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"subscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "msup-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"superscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"superscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"superscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"superscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "msubsup-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b",{"string":"superscript"},"c"],
+ ["a",{"string":"base"},"b",{"string":"subscript"},"c",{"string":"superscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b",{"string":"superscriptAbbr"},"c"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},"c",{"string":"superscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mmultiscripts-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"subscript"},"b",{"string":"superscript"},"c",{"string":"superscript"},"d",{"string":"presubscript"},"e",{"string":"presubscript"},"f",{"string":"presuperscript"},"g"],
+ ["a",{"string":"base"},"b",{"string":"subscript"},"c",{"string":"superscript"},"d",{"string":"superscript"},"e",{"string":"presubscript"},"f",{"string":"presubscript"},"g",{"string":"presuperscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"subscriptAbbr"},"b",{"string":"superscriptAbbr"},"c",{"string":"superscriptAbbr"},"d",{"string":"presubscriptAbbr"},"e",{"string":"presubscriptAbbr"},"f",{"string":"presuperscriptAbbr"},"g"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"subscriptAbbr"},"c",{"string":"superscriptAbbr"},"d",{"string":"superscriptAbbr"},"e",{"string":"presubscriptAbbr"},"f",{"string":"presubscriptAbbr"},"g",{"string":"presuperscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "munder-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"underscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"underscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"underscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"underscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mover-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"overscript"},"b"],
+ ["a",{"string":"base"},"b",{"string":"overscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"overscriptAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"overscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "munderover-1",
+ expectedUtterance: [
+ [{"string":"mathmlscripted"},{"string":"base"},"a",{"string":"underscript"},"b",{"string":"overscript"},"c"],
+ ["a",{"string":"base"},"b",{"string":"underscript"},"c",{"string":"overscript"},{"string":"mathmlscripted"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlscriptedAbbr"},{"string":"baseAbbr"},"a",{"string":"underscriptAbbr"},"b",{"string":"overscriptAbbr"},"c"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"underscriptAbbr"},"c",{"string":"overscriptAbbr"},{"string":"mathmlscriptedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mroot-1",
+ expectedUtterance: [
+ [{"string":"mathmlroot"},{"string":"base"},"a",{"string":"root-index"},"b"],
+ ["a",{"string":"base"},"b",{"string":"root-index"},{"string":"mathmlroot"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlrootAbbr"},{"string":"baseAbbr"},"a",{"string":"root-indexAbbr"},"b"],
+ ["a",{"string":"baseAbbr"},"b",{"string":"root-indexAbbr"},{"string":"mathmlrootAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "mtable-1",
+ expectedUtterance: [
+ [{"string":"mathmltable"},{"string":"tblColumnInfo","count":3},{"string":"tblRowInfo","count":2},{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[1]},"a",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[1]},"b",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[1]},"c",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[2]},"d",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[2]},"e",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[2]},"f"],
+ ["a",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[1]},"b",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[1]},"c",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[1]},"d",{"string":"columnInfo","args":[1]},{"string":"rowInfo","args":[2]},"e",{"string":"columnInfo","args":[2]},{"string":"rowInfo","args":[2]},"f",{"string":"columnInfo","args":[3]},{"string":"rowInfo","args":[2]},{"string":"mathmltable"},{"string":"tblColumnInfo","count":3},{"string":"tblRowInfo","count":2}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmltableAbbr"},{"string":"tblColumnInfoAbbr","count":3},{"string":"tblRowInfoAbbr","count":2},{"string":"cellInfoAbbr","args":[1,1]},"a",{"string":"cellInfoAbbr","args":[2,1]},"b",{"string":"cellInfoAbbr","args":[3,1]},"c",{"string":"cellInfoAbbr","args":[1,2]},"d",{"string":"cellInfoAbbr","args":[2,2]},"e",{"string":"cellInfoAbbr","args":[3,2]},"f"],
+ ["a",{"string":"cellInfoAbbr","args":[1,1]},"b",{"string":"cellInfoAbbr","args":[2,1]},"c",{"string":"cellInfoAbbr","args":[3,1]},"d",{"string":"cellInfoAbbr","args":[1,2]},"e",{"string":"cellInfoAbbr","args":[2,2]},"f",{"string":"cellInfoAbbr","args":[3,2]},{"string":"mathmltableAbbr"},{"string":"tblColumnInfoAbbr","count":3},{"string":"tblRowInfoAbbr","count":2}]
+ ]
+ }, {
+ accOrElmOrID: "menclose-1",
+ expectedUtterance: [
+ [{"string":"mathmlenclosed"},{"string":"notation-longdiv"},"a"],
+ ["a",{"string":"notation-longdiv"},{"string":"mathmlenclosed"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlenclosedAbbr"},{"string":"notation-longdivAbbr"},"a"],
+ ["a",{"string":"notation-longdivAbbr"},{"string":"mathmlenclosedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "menclose-2",
+ expectedUtterance: [
+ [{"string":"mathmlenclosed"},{"string":"notation-circle"},"a"],
+ ["a",{"string":"notation-circle"},{"string":"mathmlenclosed"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlenclosedAbbr"},{"string":"notation-circleAbbr"},"a"],
+ ["a",{"string":"notation-circleAbbr"},{"string":"mathmlenclosedAbbr"}]
+ ]
+ }, {
+ accOrElmOrID: "menclose-3",
+ expectedUtterance: [
+ [{"string":"mathmlenclosed"},{"string":"notation-left"},{"string":"notation-top"},{"string":"notation-bottom"},"a"],
+ ["a",{"string":"notation-left"},{"string":"notation-top"},{"string":"notation-bottom"},{"string":"mathmlenclosed"}]
+ ],
+ expectedBraille: [
+ [{"string":"mathmlenclosedAbbr"},{"string":"notation-leftAbbr"},{"string":"notation-topAbbr"},{"string":"notation-bottomAbbr"},"a"],
+ ["a",{"string":"notation-leftAbbr"},{"string":"notation-topAbbr"},{"string":"notation-bottomAbbr"},{"string":"mathmlenclosedAbbr"}]
+ ]
+ }];
+
+ // Test all possible utterance order preference values.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1163374"
+ title="[AccessFu] MathML Accessibility Support">
+ Mozilla Bug 1163374
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+
+ <math id="math-1"><mo>(</mo><mi>x</mi><mo>,</mo><mi>y</mi><mo>)</mo></math>
+
+ <math>
+ <mfrac id="mfrac-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mfrac>
+ </math>
+
+ <math>
+ <mfrac id="mfrac-2" linethickness="0px">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mfrac>
+ </math>
+
+ <math>
+ <msub id="msub-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </msub>
+ </math>
+ <math>
+ <msup id="msup-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </msup>
+ </math>
+ <math>
+ <msubsup id="msubsup-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ <mi>c</mi>
+ </msubsup>
+ </math>
+ <math>
+ <mmultiscripts id="mmultiscripts-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ <mi>c</mi>
+ <none/>
+ <mi>d</mi>
+ <mprescripts/>
+ <mi>e</mi>
+ <none/>
+ <mi>f</mi>
+ <mi>g</mi>
+ </mmultiscripts>
+ </math>
+
+ <math>
+ <munder id="munder-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </munder>
+ </math>
+ <math>
+ <mover id="mover-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mover>
+ </math>
+ <math>
+ <munderover id="munderover-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ <mi>c</mi>
+ </munderover>
+ </math>
+
+ <math>
+ <mroot id="mroot-1">
+ <mi>a</mi>
+ <mi>b</mi>
+ </mroot>
+ </math>
+
+ <math>
+ <mtable id="mtable-1">
+ <mtr>
+ <mtd><mi>a</mi></mtd>
+ <mtd><mi>b</mi></mtd>
+ <mtd><mi>c</mi></mtd>
+ </mtr>
+ <mtr>
+ <mtd><mi>d</mi></mtd>
+ <mtd><mi>e</mi></mtd>
+ <mtd><mi>f</mi></mtd>
+ </mtr>
+ </mtable>
+ </math>
+
+ <math>
+ <menclose id="menclose-1"><mi>a</mi></menclose>
+ </math>
+ <math>
+ <menclose id="menclose-2" notation="circle"><mi>a</mi></menclose>
+ </math>
+ <math>
+ <menclose id="menclose-3" notation="left top bottom"><mi>a</mi></menclose>
+ </math>
+
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_pointer_relay.html b/accessible/tests/mochitest/jsat/test_pointer_relay.html
new file mode 100644
index 000000000..cb58fe73b
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_pointer_relay.html
@@ -0,0 +1,95 @@
+<html>
+
+<head>
+ <title>AccessFu tests for pointer relay.</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="../layout.js"></script>
+ <script type="application/javascript" src="./jsatcommon.js"></script>
+ <script type="application/javascript" src="./dom_helper.js"></script>
+ <script type="application/javascript">
+
+ Components.utils.import(
+ "resource://gre/modules/accessibility/PointerAdapter.jsm");
+
+ var tests = [
+ {
+ type: 'touchstart', target: [{base: 'button'}],
+ expected: {type: 'pointerdown', length: 1}
+ },
+ {
+ type: 'touchmove', target: [{base: 'button'}],
+ expected: {type: 'pointermove', length: 1}
+ },
+ {
+ type: 'touchend', target: [{base: 'button'}],
+ expected: {type: 'pointerup'}
+ },
+ {
+ type: 'touchstart', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3}],
+ expected: {type: 'pointerdown', length: 2}
+ },
+ {
+ type: 'touchend', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3}],
+ expected: {type: 'pointerup'}
+ },
+ {
+ type: 'touchstart', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3},
+ {base: 'button', x: 0.5, y: -0.3}],
+ expected: {type: 'pointerdown', length: 3}
+ },
+ {
+ type: 'touchend', target: [{base: 'button'},
+ {base: 'button', x: 0.5, y: 0.3},
+ {base: 'button', x: 0.5, y: -0.3}],
+ expected: {type: 'pointerup'}
+ }
+ ];
+
+ function makeTestFromSpec(test) {
+ return function runTest() {
+ PointerRelay.start(function onPointerEvent(aDetail) {
+ is(aDetail.type, test.expected.type,
+ 'mozAccessFuPointerEvent is correct.');
+ if (test.expected.length) {
+ is(aDetail.points.length, test.expected.length,
+ 'mozAccessFuPointerEvent points length is correct.');
+ }
+ PointerRelay.stop();
+ AccessFuTest.nextTest();
+ });
+ eventMap[test.type](test.target, test.type);
+ };
+ }
+
+ function doTest() {
+ tests.forEach(function addTest(test) {
+ AccessFuTest.addFunc(makeTestFromSpec(test));
+ });
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=976082"
+ title="[AccessFu] Provide tests for pointer relay.">
+ Mozilla Bug 981015
+ </a>
+ <div>
+ <button id="button">I am a button</button>
+ </div>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_quicknav_modes.html b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
new file mode 100644
index 000000000..f99b64a84
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
@@ -0,0 +1,107 @@
+<html>
+
+<head>
+ <title>AccessFu test for enabling</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="./jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function prefStart() {
+ // Start AccessFu via pref.
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 1]]});
+ AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+ }
+
+ function nextMode(aCurrentMode, aNextMode) {
+ return function() {
+ is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+ 'initial current mode is correct');
+ AccessFu.Input.quickNavMode.next();
+ _expectMode(aNextMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function prevMode(aCurrentMode, aNextMode) {
+ return function() {
+ is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+ 'initial current mode is correct');
+ AccessFu.Input.quickNavMode.previous();
+ _expectMode(aNextMode, AccessFuTest.nextTest);
+ }
+ }
+
+ function setMode(aModeIndex, aExpectedMode) {
+ return function() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [['accessibility.accessfu.quicknav_index', aModeIndex]]},
+ function() {
+ _expectMode(aExpectedMode, AccessFuTest.nextTest);
+ });
+ }
+ }
+
+ function reconfigureModes() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [['accessibility.accessfu.quicknav_modes', 'Landmark,Button,Entry,Graphic']]},
+ function() {
+ // When the modes are reconfigured, the current mode should
+ // be set to the first in the new list.
+ _expectMode('Landmark', AccessFuTest.nextTest);
+ });
+ }
+
+ function _expectMode(aExpectedMode, aCallback) {
+ if (AccessFu.Input.quickNavMode.current === aExpectedMode) {
+ ok(true, 'correct mode');
+ aCallback();
+ } else {
+ AccessFuTest.once_log('Quicknav mode: ' + aExpectedMode, function() {
+ ok(true, 'correct mode');
+ aCallback();
+ });
+ }
+ }
+
+ // Listen for initial 'EventManager.start' and disable AccessFu.
+ function prefStop() {
+ ok(AccessFu._enabled, "AccessFu was started via preference.");
+ AccessFuTest.once_log("EventManager.stop", () => AccessFuTest.finish());
+ SpecialPowers.pushPrefEnv({"set": [['accessibility.accessfu.activate', 0]]});
+ }
+
+ function doTest() {
+ AccessFuTest.addFunc(prefStart);
+ AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+ AccessFuTest.addFunc(nextMode('Heading', 'FormElement'));
+ AccessFuTest.addFunc(nextMode('FormElement', 'Link'));
+ AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+ AccessFuTest.addFunc(prevMode('Heading', 'Link'));
+ AccessFuTest.addFunc(prevMode('Link', 'FormElement'));
+ AccessFuTest.addFunc(setMode(1, 'Heading'));
+ AccessFuTest.addFunc(reconfigureModes);
+ AccessFuTest.addFunc(prefStop);
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests([ // Will call SimpleTest.finish();
+ ['accessibility.accessfu.quicknav_modes', 'Link,Heading,FormElement']]);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+ title="[AccessFu] Add mochitest for enabling">
+ Mozilla Bug 811307
+ </a>
+</body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/mochitest/jsat/test_tables.html b/accessible/tests/mochitest/jsat/test_tables.html
new file mode 100644
index 000000000..aa7f482e9
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_tables.html
@@ -0,0 +1,579 @@
+<html>
+<head>
+ <title>[AccessFu] Improve reading of table semantics</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="output.js"></script>
+ <script type="application/javascript"
+ src="jsatcommon.js"></script>
+ <script type="application/javascript">
+
+ function doTest() {
+ // Test the following accOrElmOrID.
+ var tests = [{
+ accOrElmOrID: "table1",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col1", "cell1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "cell1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col1", "cell2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [[
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell1",
+ {"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"], ["col1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "cell1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell2",
+ {"string": "cellInfoAbbr", "args": [2, 2]}, "col2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2}]]
+ }, {
+ accOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1", "cell1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2"], ["cell1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1", "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2", "col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "col2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [[{"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1",
+ {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"], ["cell1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2", "col1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col2",
+ {"string": "cellInfoAbbr", "args": [2, 2]},
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 2},
+ {"string": "tblRowInfoAbbr", "count": 2}]]
+ }, {
+ accOrElmOrID: "table3",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [[
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["colheader",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2}]]
+ }, {
+ accOrElmOrID: "table4",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 4},
+ {"string": "tblRowInfo", "count": 3},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}, "col3",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1", "row1",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [2]}, "col3", "row1", "cell1",
+ {"string": "columnInfo", "args": [4]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansRows", "args": [2]}, "row1", "cell2",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [3]}, "col1", "row2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell3",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [3]}, "col3", "row2", "cell4"], ["col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col3",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}, "row1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1", "cell1",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [2]}, "col3", "row1", "cell2",
+ {"string": "columnInfo", "args": [4]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansRows", "args": [2]}, "row1", "row2",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [3]}, "col1", "cell3",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell4",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [3]}, "col3", "row2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 4},
+ {"string": "tblRowInfo", "count": 3}]],
+ expectedBraille: [[
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 4},
+ {"string": "tblRowInfoAbbr", "count": 3},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [3, 1]}, "col3",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "row1",
+ {"string": "cellInfoAbbr", "args": [3, 2]}, "col3", "row1", "cell1",
+ {"string": "cellInfoAbbr", "args": [4, 2]}, "row1", "cell2",
+ {"string": "cellInfoAbbr", "args": [1, 3]}, "col1", "row2",
+ {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell3",
+ {"string": "cellInfoAbbr", "args": [3, 3]}, "col3", "row2", "cell4"],
+ ["col1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col2",
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col3",
+ {"string": "cellInfoAbbr", "args": [3, 1]}, "row1",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell1",
+ {"string": "cellInfoAbbr", "args": [3, 2]}, "col3", "row1", "cell2",
+ {"string": "cellInfoAbbr", "args": [4, 2]}, "row1", "row2",
+ {"string": "cellInfoAbbr", "args": [1, 3]}, "col1", "cell3",
+ {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell4",
+ {"string": "cellInfoAbbr", "args": [3, 3]}, "col3", "row2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 4},
+ {"string": "tblRowInfoAbbr", "count": 3}]]
+ }, {
+ accOrElmOrID: "table5",
+ expectedUtterance: [["Row1", "Row2"], ["Row1", "Row2"]],
+ expectedBraille: [["Row1", "Row2"], ["Row1", "Row2"]]
+ }, {
+ // Test pivot to table1_th1 from table1.
+ accOrElmOrID: "table1_th1",
+ oldAccOrElmOrID: "table1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1"], ["col1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}]],
+ expectedBraille: [[
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"], ["col1",
+ {"string": "cellInfoAbbr", "args": [1, 1]}]]
+ }, {
+ // Test pivot to table1_td2 from table1.
+ accOrElmOrID: "table1_td2",
+ oldAccOrElmOrID: "table1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
+ }, {
+ // Test pivot to table1_td2 from table1_th1.
+ accOrElmOrID: "table1_td2",
+ oldAccOrElmOrID: "table1_th1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [2]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
+ }, {
+ // Test pivot to table1_td2 from table1_td1.
+ accOrElmOrID: "table1_td2",
+ oldAccOrElmOrID: "table1_td1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [2]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
+ }, {
+ // Test pivot to table2_cell_1 from table2.
+ accOrElmOrID: "table2_cell_1",
+ oldAccOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1", "cell1"], ["cell1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "col1"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1"],
+ ["cell1", {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"]]
+ }, {
+ // Test pivot to table2_cell_2 from table2.
+ accOrElmOrID: "table2_cell_2",
+ oldAccOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["colheader",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [1]}, "bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2"]],
+ expectedBraille: [[
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["colheader",
+ {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+ {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+ {"string": "tableAbbr"},
+ {"string": "tblColumnInfoAbbr", "count": 1},
+ {"string": "tblRowInfoAbbr", "count": 2},
+ {"string": "cellInfoAbbr", "args": [2, 1]}, "col2"]]
+ }, {
+ // Test pivot to table2_cell_1 from table2_cell_2.
+ accOrElmOrID: "table2_cell_1",
+ oldAccOrElmOrID: "table2_cell_2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]}, "col1", "cell1"], ["cell1",
+ {"string": "columnInfo", "args": [1]}, "col1"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1"],
+ ["cell1", {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"]]
+ }, {
+ // Test pivot to table3_cell from table2.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table2",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
+ }, {
+ // Test pivot to table3_cell from table2_cell_1.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table2_cell_1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
+ }, {
+ // Test pivot to table3_cell from table3_ch.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table3_ch",
+ expectedUtterance: [[
+ {"string": "rowInfo", "args": [2]}, "bla"], ["bla",
+ {"string": "rowInfo", "args": [2]}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}]]
+ }, {
+ // Test pivot to table3_cell from table1_td1.
+ accOrElmOrID: "table3_cell",
+ oldAccOrElmOrID: "table1_td1",
+ expectedUtterance: [[
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]}, "colheader",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 1},
+ {"string": "tblRowInfo", "count": 2},
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [1]}, "col2",
+ {"string": "table"},
+ {"string": "tblColumnInfo", "count": 2},
+ {"string": "tblRowInfo", "count": 2}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+ ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
+ }, {
+ // Test pivot to table4_ch_3 from table4.
+ accOrElmOrID: "table4_ch_3",
+ oldAccOrElmOrID: "table4",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}, "col3"], ["col3",
+ {"string": "columnInfo", "args": [3]},
+ {"string": "rowInfo", "args": [1]}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [3, 1]}, "col3"],
+ ["col3", {"string": "cellInfoAbbr", "args": [3, 1]}]]
+ }, {
+ // Test pivot to table4_rh_1 from table4_ch_3.
+ accOrElmOrID: "table4_rh_1",
+ oldAccOrElmOrID: "table4_ch_3",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1", "row1"], ["row1",
+ {"string": "columnInfo", "args": [1]},
+ {"string": "rowInfo", "args": [2]},
+ {"string": "spansColumns", "args": [2]}, "col1"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "row1"],
+ ["row1", {"string": "cellInfoAbbr", "args": [1, 2]}, "col1"]]
+ }, {
+ // Test pivot to table4_cell_3 from table4_rh_1.
+ accOrElmOrID: "table4_cell_3",
+ oldAccOrElmOrID: "table4_rh_1",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [4]},
+ {"string": "spansRows", "args": [2]}, "cell2"], ["cell2",
+ {"string": "columnInfo", "args": [4]},
+ {"string": "spansRows", "args": [2]}]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [4, 2]}, "cell2"],
+ ["cell2", {"string": "cellInfoAbbr", "args": [4, 2]}]]
+ }, {
+ // Test pivot to table4_cell_5 from table4_cell_3.
+ accOrElmOrID: "table4_cell_5",
+ oldAccOrElmOrID: "table4_cell_3",
+ expectedUtterance: [[
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell3"],
+ ["cell3",
+ {"string": "columnInfo", "args": [2]},
+ {"string": "rowInfo", "args": [3]}, "col2", "row2"]],
+ expectedBraille: [
+ [{"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell3"],
+ ["cell3", {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2"]]
+ }];
+
+ // Test outputs (utterance and braille) for tables including their
+ // headers and cells.
+ function testOutputOrder(aOutputOrder) {
+ return function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [[PREF_UTTERANCE_ORDER, aOutputOrder]]
+ }, function() {
+ tests.forEach(function run(test) {
+ testOutput(test.expectedUtterance[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 1);
+ testOutput(test.expectedBraille[aOutputOrder], test.accOrElmOrID,
+ test.oldAccOrElmOrID, 0);
+ });
+ AccessFuTest.nextTest();
+ });
+ };
+ }
+
+ AccessFuTest.addFunc(testOutputOrder(0));
+ AccessFuTest.addFunc(testOutputOrder(1));
+ AccessFuTest.waitForExplicitFinish();
+ AccessFuTest.runTests();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addA11yLoadEvent(doTest);
+ </script>
+</head>
+<body>
+ <div id="root">
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=830748"
+ title="[AccessFu] Improve reading of table semantics">
+ Mozilla Bug 830748
+ </a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ <table id="table1">
+ <thead>
+ <tr>
+ <th id="table1_th1">col1</th>
+ <th>col2</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id="table1_td1">cell1</td>
+ <td id="table1_td2">cell2</td>
+ </tr>
+ </tbody>
+ </table>
+ <table id="table2" border="1">
+ <tr>
+ <td id="table2_cell_1" headers="table2_ch_1">cell1</td>
+ <td id="table2_cell_2" headers="table2_ch_2">
+ <table id="table3">
+ <thead>
+ <tr>
+ <th id="table3_ch">colheader</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td id="table3_cell">bla</td>
+ </tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ <tr>
+ <td id="table2_ch_1" scope="col">col1</td>
+ <td id="table2_ch_2" scope="col">col2</td>
+ </tr>
+ </table>
+ <table id="table4" border="1">
+ <thead>
+ <tr>
+ <th id="table4_ch_1">col1</th>
+ <th id="table4_ch_2">col2</th>
+ <td id="table4_ch_3" scope="col">col3</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th id="table4_rh_1" colspan="2">row1</th>
+ <td id="table4_cell_2">cell1</td>
+ <td id="table4_cell_3" rowspan="2">cell2</td>
+ </tr>
+ <tr>
+ <td id="table4_rh_2" scope="row">row2</td>
+ <td id="table4_cell_5">cell3</td>
+ <td id="table4_cell_6">cell4</td>
+ </tr>
+ </tbody>
+ </table>
+ <table id="table5">
+ <tr><td>Row1</td></tr>
+ <tr><td>Row2</td></tr>
+ </table>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/accessible/tests/mochitest/jsat/test_traversal.html b/accessible/tests/mochitest/jsat/test_traversal.html
new file mode 100644
index 000000000..533e9d89f
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -0,0 +1,167 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu TraversalRules</title>
+ <meta charset="utf-8" />
+ <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/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.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" src="../pivot.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+
+ <script type="application/javascript">
+ Components.utils.import("resource://gre/modules/accessibility/Traversal.jsm");
+ var gBrowserWnd = null;
+ var gQueue = null;
+
+ function doTest()
+ {
+ var doc = currentTabDocument();
+ var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
+
+ gQueue = new eventQueue();
+
+ gQueue.onFinish = function onFinish()
+ {
+ closeBrowserWindow();
+ }
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Heading, null,
+ ['heading-1', 'heading-2', 'heading-3', 'heading-5']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Entry, null,
+ ['input-1-1', 'label-1-2', 'input-1-3',
+ 'input-1-4', 'input-1-5']);
+
+ // move back an element to hit all the form elements, because the VC is
+ // currently at the first input element
+ gQueue.push(new setVCPosInvoker(docAcc, "movePrevious",
+ TraversalRules.Heading, "heading-1"));
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.FormElement, null,
+ ['input-1-1', 'label-1-2', 'button-1-1',
+ 'radio-1-1', 'radio-1-2', 'input-1-3',
+ 'input-1-4', 'button-1-2', 'checkbox-1-1',
+ 'select-1-1', 'select-1-2', 'checkbox-1-2',
+ 'select-1-3', 'input-1-5', 'button-1-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4', 'checkbox-1-5', 'switch-1']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Button, null,
+ ['button-1-1', 'button-1-2', 'button-1-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.RadioButton, null,
+ ['radio-1-1', 'radio-1-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Checkbox, null,
+ ['checkbox-1-1', 'checkbox-1-2', 'checkbox-1-5',
+ 'switch-1']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Combobox, null,
+ ['select-1-1', 'select-1-2', 'select-1-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.List, null,
+ ['list-1', 'list-2', 'list-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.ListItem, null,
+ ['listitem-1-1', 'listitem-2-1', 'listitem-2-2',
+ 'listitem-3-1', 'listitem-3-2', 'listitem-3-3',
+ 'listitem-3-4', 'listitem-3-5', 'listitem-3-6',
+ 'listitem-2-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Graphic, null,
+ ['image-2', 'image-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Link, null,
+ ['link-0', 'link-1', 'link-2', 'link-3']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Anchor, null,
+ ['anchor-1', 'anchor-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Separator, null,
+ ['separator-1', 'separator-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Table, null,
+ ['table-1', 'grid', 'table-2']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Simple, null,
+ ['heading-1', 'Name:', 'input-1-1', 'label-1-2',
+ 'button-1-1', 'Radios are old: ', 'radio-1-1',
+ 'Radios are new: ', 'radio-1-2', 'Password:',
+ 'input-1-3', 'Unlucky number:', 'input-1-4',
+ 'button-1-2', 'Check me: ', 'checkbox-1-1',
+ 'select-1-1', 'Value 1', 'Value 2', 'Value 3',
+ 'Check me too: ', 'checkbox-1-2', 'But not me: ',
+ 'Or me! ', 'Value 1', 'Value 2', 'Value 3',
+ 'Electronic mailing address:', 'input-1-5',
+ 'button-1-3', 'heading-2', 'heading-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4', 'Programming Language',
+ 'A esoteric weapon wielded by only the most ' +
+ 'formidable warriors, for its unrelenting strict' +
+ ' power is unfathomable.',
+ '• Lists of Programming Languages', 'Lisp ',
+ '1. Scheme', '2. Racket', '3. Clojure',
+ '4. Standard Lisp', 'link-0', ' Lisp',
+ 'checkbox-1-5', ' LeLisp', '• JavaScript',
+ 'heading-5', 'image-2', 'image-3',
+ 'Not actually an image', 'link-1', 'anchor-1',
+ 'link-2', 'anchor-2', 'link-3', '3', '1', '4',
+ '1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
+ '5 8', 'gridcell4', 'Just an innocuous separator',
+ 'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
+ 'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2',
+ 'switch-1', 'This is a MathML formula ', 'math-1',
+ 'with some text after.']);
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Landmark, null,
+ ['header-1', 'main-1', 'footer-1']);
+
+
+ queueTraversalSequence(gQueue, docAcc, TraversalRules.Control, null,
+ ['input-1-1', 'label-1-2', 'button-1-1',
+ 'radio-1-1', 'radio-1-2', 'input-1-3',
+ 'input-1-4', 'button-1-2', 'checkbox-1-1',
+ 'select-1-1', 'select-1-2', 'checkbox-1-2',
+ 'select-1-3', 'input-1-5', 'button-1-3',
+ 'button-2-1', 'button-2-2', 'button-2-3',
+ 'button-2-4', 'link-0', 'checkbox-1-5',
+ 'link-1', 'link-2', 'link-3', 'switch-1']);
+
+ gQueue.invoke();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ /* We open a new browser because we need to test with a top-level content
+ document. */
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_traversal.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for AccessFu TraversalRules"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=933808">Mozilla Bug 933808</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/accessible/tests/mochitest/jsat/test_traversal_helper.html b/accessible/tests/mochitest/jsat/test_traversal_helper.html
new file mode 100644
index 000000000..f6a347ed0
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_traversal_helper.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Tests AccessFu TraversalRules</title>
+ <meta charset="utf-8" />
+ <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/chrome-harness.js">
+ </script>
+
+ <script type="application/javascript" src="../common.js"></script>
+ <script type="application/javascript" src="../browser.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" src="../pivot.js"></script>
+ <script type="application/javascript" src="../layout.js"></script>
+
+ <script type="application/javascript">
+ Components.utils.import("resource://gre/modules/accessibility/Traversal.jsm");
+
+ var vc;
+
+ function accessibleIs(aAccessible, aExpected, aMessage) {
+ if (!aAccessible && aAccessible == aExpected) {
+ ok(true, "Accessible is null. " + aMessage);
+ } else {
+ ok(aAccessible.DOMNode.id == aExpected || aAccessible.name == aExpected,
+ "expected '" + aExpected + "', got " + prettyName(vc.position) +
+ ". " + aMessage);
+ }
+ }
+
+ function walkSequence(aMethod, aRule, aExpectedSequence) {
+ for (var expected of aExpectedSequence) {
+ ok(TraversalHelper.move(vc, aMethod, aRule),
+ "successfully did " + aMethod + " with " + aRule);
+ accessibleIs(vc.position, expected, "landed on correct accessible");
+ }
+ }
+
+ function testTraversalHelper(aRule, aExpectedSequence) {
+ vc.position = null;
+
+ walkSequence('moveNext', aRule, aExpectedSequence);
+
+ ok(!TraversalHelper.move(vc, 'moveNext', aRule), "reached end");
+
+ TraversalHelper.move(vc, 'moveLast', 'Simple');
+
+ walkSequence('movePrevious', aRule,
+ Array.from(aExpectedSequence).reverse());
+
+ ok(!TraversalHelper.move(vc, 'movePrevious', aRule), "reached start");
+
+ vc.position = null;
+
+ ok(TraversalHelper.move(vc, 'moveFirst', aRule), "moveFirst");
+
+ accessibleIs(vc.position, aExpectedSequence[0],
+ "moveFirst to correct accessible");
+
+ ok(TraversalHelper.move(vc, 'moveLast', aRule), "moveLast");
+
+ accessibleIs(vc.position, aExpectedSequence[aExpectedSequence.length - 1],
+ "moveLast to correct accessible");
+ }
+
+
+ function doTest()
+ {
+ var doc = currentTabDocument();
+ var docAcc = getAccessible(doc, [nsIAccessibleDocument]);
+ vc = docAcc.virtualCursor;
+
+ testTraversalHelper('Landmark',
+ ['heading-1', 'heading-2', 'statusbar-1']);
+
+ testTraversalHelper('List',
+ ['Programming Language', 'listitem-2-1', 'listitem-3-1']);
+
+ testTraversalHelper('Section',
+ ['heading-1', 'heading-2', 'heading-3',
+ 'heading-5', 'link-1', 'statusbar-1']);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ /* We open a new browser because we need to test with a top-level content
+ document. */
+ openBrowserWindow(
+ doTest,
+ getRootDirectory(window.location.href) + "doc_traversal.html");
+ });
+ </script>
+</head>
+<body id="body">
+
+ <a target="_blank"
+ title="Add tests for AccessFu TraversalRules"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=xxx">Mozilla Bug xxx</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+</body>
+</html>