Bug 888256 - Adding support for speaking landmarks. r=eeejay, marcoz

---
 accessible/src/jsat/OutputGenerator.jsm            |   76 ++++++++--
 accessible/tests/mochitest/jsat/Makefile.in        |    1 +
 .../tests/mochitest/jsat/test_landmarks.html       |  154 ++++++++++++++++++++
 .../en-US/chrome/accessibility/AccessFu.properties |    8 +
 4 files changed, 225 insertions(+), 14 deletions(-)
 create mode 100644 accessible/tests/mochitest/jsat/test_landmarks.html
This commit is contained in:
Yura Zenevich 2013-07-09 16:09:25 -04:00
parent 655d8bd8b6
commit 73dba62c25
4 changed files with 225 additions and 14 deletions

View File

@ -31,9 +31,6 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
XPCOMUtils.defineLazyModuleGetter(this, 'PluralForm',
'resource://gre/modules/PluralForm.jsm');
let gUtteranceOrder = new PrefCache('accessibility.accessfu.utterance');
var gStringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
getService(Ci.nsIStringBundleService).
createBundle('chrome://global/locale/AccessFu.properties');
@ -68,10 +65,10 @@ this.OutputGenerator = {
return (nameRule & NAME_FROM_SUBTREE_RULE) &&
(Utils.getAttributes(aAccessible)['explicit-name'] === 'true');
};
let outputOrder = this._getOutputOrder();
let contextStart = this._getContextStart(aContext);
if (outputOrder === OUTPUT_DESC_FIRST) {
if (this.outputOrder === OUTPUT_DESC_FIRST) {
contextStart.forEach(addOutput);
addOutput(aContext.accessible);
[addOutput(node) for
@ -92,7 +89,7 @@ this.OutputGenerator = {
/**
* Generates output for an object.
* @param {nsIAccessible} aAccessible accessible object to generate utterance
* @param {nsIAccessible} aAccessible accessible object to generate output
* for.
* @param {PivotContext} aContext object that generates and caches
* context information for a given accessible and its relationship with
@ -166,12 +163,49 @@ this.OutputGenerator = {
}
if (name) {
let outputOrder = this._getOutputOrder();
aOutput[outputOrder === OUTPUT_DESC_FIRST ?
aOutput[this.outputOrder === OUTPUT_DESC_FIRST ?
'push' : 'unshift'](name);
}
},
/**
* Adds a landmark role to the output if available.
* @param {Array} aOutput Output array.
* @param {nsIAccessible} aAccessible current accessible object.
*/
_addLandmark: function _addLandmark(aOutput, aAccessible) {
let getLandmarkName = function getLandmarkName(aAccessible) {
let roles = Utils.getAttributes(aAccessible)['xml-roles'];
if (!roles) {
return;
}
// Looking up a role that would match a landmark.
for (let landmark of this.gLandmarks) {
if (roles.indexOf(landmark) > -1) {
return gStringBundle.GetStringFromName(landmark);
}
}
};
let landmark = getLandmarkName.apply(this, [aAccessible]);
if (!landmark) {
return;
}
aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'unshift' : 'push'](
landmark);
},
get outputOrder() {
if (!this._utteranceOrder) {
this._utteranceOrder = new PrefCache('accessibility.accessfu.utterance');
}
return typeof this._utteranceOrder.value === 'number' ?
this._utteranceOrder.value : this.defaultOutputOrder;
},
_getOutputName: function _getOutputName(aName) {
return aName.replace(' ', '');
},
@ -186,10 +220,14 @@ this.OutputGenerator = {
return str.replace('#1', aCount);
},
_getOutputOrder: function _getOutputOrder() {
return typeof gUtteranceOrder.value === 'number' ?
gUtteranceOrder.value : this.defaultOutputOrder;
},
gLandmarks: [
'banner',
'complementary',
'contentinfo',
'main',
'navigation',
'search'
],
roleRuleMap: {
'menubar': INCLUDE_DESC,
@ -277,6 +315,7 @@ this.OutputGenerator = {
}
this._addName(output, aAccessible, aFlags);
this._addLandmark(output, aAccessible);
return output;
},
@ -291,6 +330,7 @@ this.OutputGenerator = {
output.push(desc.join(' '));
this._addName(output, aAccessible, aFlags);
this._addLandmark(output, aAccessible);
return output;
},
@ -317,6 +357,7 @@ this.OutputGenerator = {
this._getOutputName('tableInfo'), [this._getLocalizedRole(aRoleStr),
tableColumnInfo, tableRowInfo], 3));
this._addName(output, aAccessible, aFlags);
this._addLandmark(output, aAccessible);
return output;
}
}
@ -411,6 +452,7 @@ this.UtteranceGenerator = {
[gStringBundle.formatStringFromName('headingLevel', [level.value], 1)];
this._addName(utterance, aAccessible, aFlags);
this._addLandmark(utterance, aAccessible);
return utterance;
},
@ -426,6 +468,7 @@ this.UtteranceGenerator = {
utterance.push(gStringBundle.GetStringFromName('listEnd'));
this._addName(utterance, aAccessible, aFlags);
this._addLandmark(utterance, aAccessible);
return utterance;
},
@ -485,6 +528,8 @@ this.UtteranceGenerator = {
}
this._addName(utterance, aAccessible, aFlags);
this._addLandmark(utterance, aAccessible);
return utterance;
},
@ -557,6 +602,7 @@ this.UtteranceGenerator = {
let utterance = [desc.join(' ')];
this._addName(utterance, aAccessible, aFlags);
this._addLandmark(utterance, aAccessible);
return utterance;
}
@ -570,11 +616,10 @@ this.BrailleGenerator = {
genForContext: function genForContext(aContext) {
let output = OutputGenerator.genForContext.apply(this, arguments);
let outputOrder = this._getOutputOrder();
let acc = aContext.accessible;
if (acc instanceof Ci.nsIAccessibleText) {
output.endOffset = outputOrder === OUTPUT_DESC_FIRST ?
output.endOffset = this.outputOrder === OUTPUT_DESC_FIRST ?
output.output.join(' ').length : acc.characterCount;
output.startOffset = output.endOffset - acc.characterCount;
}
@ -607,6 +652,7 @@ this.BrailleGenerator = {
let braille = [];
this._addName(braille, aAccessible, aFlags);
this._addLandmark(braille, aAccessible);
return braille;
},
@ -632,6 +678,7 @@ this.BrailleGenerator = {
}
this._addName(braille, aAccessible, aFlags);
this._addLandmark(braille, aAccessible);
return braille;
},
@ -660,6 +707,7 @@ this.BrailleGenerator = {
braille.push(desc.join(' '));
this._addName(braille, aAccessible, aFlags);
this._addLandmark(braille, aAccessible);
return braille;
},

View File

@ -17,6 +17,7 @@ output.js \
test_alive.html \
test_braille.html \
test_explicit_names.html \
test_landmarks.html \
test_tables.html \
test_utterance_order.html \
$(NULL)

View File

@ -0,0 +1,154 @@
<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">
function doTest() {
// Test the following accOrElmOrID.
var tests = [{
accOrElmOrID: "nav",
expectedUtterance: [["navigation", "a nav"], ["a nav", "navigation"]],
expectedBraille: [["navigation", "a nav"], ["a nav", "navigation"]]
}, {
accOrElmOrID: "main",
expectedUtterance: [["main", "a main area"], ["a main area", "main"]],
expectedBraille: [["main", "a main area"], ["a main area", "main"]]
}, {
accOrElmOrID: "header",
expectedUtterance: [["banner", "header", "a header"], ["a header",
"header", "banner"]],
expectedBraille: [["banner", "header", "a header"], ["a header",
"header", "banner"]]
}, {
accOrElmOrID: "footer",
expectedUtterance: [["content info", "footer", "a footer"], [
"a footer", "footer", "content info"]],
expectedBraille: [["content info", "footer", "a footer"], ["a footer",
"footer", "content info"]]
}, {
accOrElmOrID: "article_header",
expectedUtterance: [["header", "a header within an article"], [
"a header within an article", "header"]],
expectedBraille: [["header", "a header within an article"], [
"a header within an article", "header"]],
}, {
accOrElmOrID: "article_footer",
expectedUtterance: [["footer", "a footer within an article"], [
"a footer within an article", "footer"]],
expectedBraille: [["footer", "a footer within an article"], [
"a footer within an article", "footer"]]
}, {
accOrElmOrID: "section_header",
expectedUtterance: [["header", "a header within a section"], [
"a header within a section", "header"]],
expectedBraille: [["header", "a header within a section"], [
"a header within a section", "header"]]
}, {
accOrElmOrID: "section_footer",
expectedUtterance: [["footer", "a footer within a section"], [
"a footer within a section", "footer"]],
expectedBraille: [["footer", "a footer within a section"], [
"a footer within a section", "footer"]]
}, {
accOrElmOrID: "aside",
expectedUtterance: [["complementary", "by the way I am an aside"], [
"by the way I am an aside", "complementary"]],
expectedBraille: [["complementary", "by the way I am an aside"], [
"by the way I am an aside", "complementary"]]
}, {
accOrElmOrID: "main_element",
expectedUtterance: [["main", "another main area"], [
"another main area", "main"]],
expectedBraille: [["main", "another main area"], ["another main area",
"main"]]
}, {
accOrElmOrID: "complementary",
expectedUtterance: [["list 1 items", "complementary", "First item",
"A complementary"], ["A complementary", "First item",
"complementary", "list 1 items"]],
// XXX: The '*' should probably come before all of the context
// utterance.
expectedBraille: [["complementary", "*", "A complementary"], ["*",
"A complementary", "complementary"]]
}, {
accOrElmOrID: "parent_main",
expectedUtterance: [["main", "a parent main", "complementary",
"a child complementary"], ["a parent main", "a child complementary",
"complementary", "main"]],
expectedBraille: [["main", "a parent main", "complementary",
"a child complementary"], ["a parent main", "a child complementary",
"complementary", "main"]]
}, {
accOrElmOrID: "child_complementary",
expectedUtterance: [["main", "complementary", "a child complementary"],
["a child complementary", "complementary", "main"]],
expectedBraille: [["complementary", "a child complementary"],
["a child complementary", "complementary"]]
}];
// Test outputs (utterance and braille) for landmarks.
tests.forEach(function run(test) {
var outputOrderValues = [0, 1];
outputOrderValues.forEach(function testOutputOrder(outputOrder) {
SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, outputOrder);
testOutput(test.expectedUtterance[outputOrder], test.accOrElmOrID,
test.oldAccOrElmOrID, 1);
testOutput(test.expectedBraille[outputOrder], test.accOrElmOrID,
test.oldAccOrElmOrID, 0);
});
});
// If there was an original utterance order preference, revert to it.
SpecialPowers.clearUserPref(PREF_UTTERANCE_ORDER);
SimpleTest.finish();
}
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>

View File

@ -80,6 +80,14 @@ listStart = First item
listEnd = Last item
listItemCount = %S items
# Landmark announcements
banner = banner
complementary = complementary
contentinfo = content info
main = main
navigation = navigation
search = search
# Description of a table or grid:
# 1 is a dynamically retrieved localized role of either 'table' or 'grid'.
# 2 is the number of columns within the table.