mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-11 01:57:00 +00:00
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:
parent
655d8bd8b6
commit
73dba62c25
@ -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;
|
||||
},
|
||||
|
@ -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)
|
||||
|
154
accessible/tests/mochitest/jsat/test_landmarks.html
Normal file
154
accessible/tests/mochitest/jsat/test_landmarks.html
Normal 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>
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user