mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1054454 - added support for aria-hidden attribute changes. Added output filter wh when presenting aria-hidden elements. r=eeejay
--- accessible/jsat/AccessFu.jsm | 4 + accessible/jsat/EventManager.jsm | 92 ++++++++++----- accessible/jsat/Utils.jsm | 14 ++- .../mochitest/jsat/doc_content_integration.html | 17 +++ .../mochitest/jsat/test_content_integration.html | 67 +++++++++++ .../tests/mochitest/jsat/test_live_regions.html | 129 ++++++++++++++++++++- 6 files changed, 287 insertions(+), 36 deletions(-)
This commit is contained in:
parent
5740fcfe6c
commit
d91e6148b0
@ -235,6 +235,10 @@ this.AccessFu = { // jshint ignore:line
|
||||
},
|
||||
|
||||
_output: function _output(aPresentationData, aBrowser) {
|
||||
if (!Utils.isAliveAndVisible(
|
||||
Utils.AccRetrieval.getAccessibleFor(aBrowser))) {
|
||||
return;
|
||||
}
|
||||
for (let presenter of aPresentationData) {
|
||||
if (!presenter) {
|
||||
continue;
|
||||
|
@ -225,43 +225,30 @@ this.EventManager.prototype = {
|
||||
this.editState = editState;
|
||||
break;
|
||||
}
|
||||
case Events.OBJECT_ATTRIBUTE_CHANGED:
|
||||
{
|
||||
let evt = aEvent.QueryInterface(
|
||||
Ci.nsIAccessibleObjectAttributeChangedEvent);
|
||||
if (evt.changedAttribute.toString() !== 'aria-hidden') {
|
||||
// Only handle aria-hidden attribute change.
|
||||
break;
|
||||
}
|
||||
if (Utils.isHidden(aEvent.accessible)) {
|
||||
this._handleHide(evt);
|
||||
} else {
|
||||
this._handleShow(aEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Events.SHOW:
|
||||
{
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
['additions', 'all']);
|
||||
// Only handle show if it is a relevant live region.
|
||||
if (!liveRegion) {
|
||||
break;
|
||||
}
|
||||
// Show for text is handled by the EVENT_TEXT_INSERTED handler.
|
||||
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
|
||||
break;
|
||||
}
|
||||
this._dequeueLiveEvent(Events.HIDE, liveRegion);
|
||||
this.present(Presentation.liveRegion(liveRegion, isPolite, false));
|
||||
this._handleShow(aEvent);
|
||||
break;
|
||||
}
|
||||
case Events.HIDE:
|
||||
{
|
||||
let evt = aEvent.QueryInterface(Ci.nsIAccessibleHideEvent);
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(
|
||||
evt, ['removals', 'all']);
|
||||
if (liveRegion) {
|
||||
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
|
||||
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
|
||||
break;
|
||||
}
|
||||
this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
|
||||
} else {
|
||||
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
|
||||
if (vc.position &&
|
||||
(Utils.getState(vc.position).contains(States.DEFUNCT) ||
|
||||
Utils.isInSubtree(vc.position, aEvent.accessible))) {
|
||||
this.contentControl.autoMove(
|
||||
evt.targetPrevSibling || evt.targetParent,
|
||||
{ moveToFocused: true, delay: 500 });
|
||||
}
|
||||
}
|
||||
this._handleHide(evt);
|
||||
break;
|
||||
}
|
||||
case Events.TEXT_INSERTED:
|
||||
@ -306,6 +293,51 @@ this.EventManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_handleShow: function _handleShow(aEvent) {
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
['additions', 'all']);
|
||||
// Only handle show if it is a relevant live region.
|
||||
if (!liveRegion) {
|
||||
return;
|
||||
}
|
||||
// Show for text is handled by the EVENT_TEXT_INSERTED handler.
|
||||
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
|
||||
return;
|
||||
}
|
||||
this._dequeueLiveEvent(Events.HIDE, liveRegion);
|
||||
this.present(Presentation.liveRegion(liveRegion, isPolite, false));
|
||||
},
|
||||
|
||||
_handleHide: function _handleHide(aEvent) {
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(
|
||||
aEvent, ['removals', 'all']);
|
||||
let acc = aEvent.accessible;
|
||||
if (liveRegion) {
|
||||
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
|
||||
if (acc.role === Roles.TEXT_LEAF) {
|
||||
return;
|
||||
}
|
||||
this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
|
||||
} else {
|
||||
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
|
||||
if (vc.position &&
|
||||
(Utils.getState(vc.position).contains(States.DEFUNCT) ||
|
||||
Utils.isInSubtree(vc.position, acc))) {
|
||||
let position = aEvent.targetPrevSibling || aEvent.targetParent;
|
||||
if (!position) {
|
||||
try {
|
||||
position = acc.previousSibling;
|
||||
} catch (x) {
|
||||
// Accessible is unattached from the accessible tree.
|
||||
position = acc.parent;
|
||||
}
|
||||
}
|
||||
this.contentControl.autoMove(position,
|
||||
{ moveToFocused: true, delay: 500 });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleText: function _handleText(aEvent, aLiveRegion, aIsPolite) {
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
|
||||
let isInserted = event.isInserted;
|
||||
|
@ -353,10 +353,16 @@ this.Utils = { // jshint ignore:line
|
||||
return false;
|
||||
},
|
||||
|
||||
isHidden: function isHidden(aAccessible) {
|
||||
// Need to account for aria-hidden, so can't just check for INVISIBLE
|
||||
// state.
|
||||
let hidden = Utils.getAttributes(aAccessible).hidden;
|
||||
return hidden && hidden === 'true';
|
||||
},
|
||||
|
||||
inHiddenSubtree: function inHiddenSubtree(aAccessible) {
|
||||
for (let acc=aAccessible; acc; acc=acc.parent) {
|
||||
let hidden = Utils.getAttributes(acc).hidden;
|
||||
if (hidden && JSON.parse(hidden)) {
|
||||
if (this.isHidden(acc)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -789,9 +795,7 @@ PivotContext.prototype = {
|
||||
if (this._includeInvisible) {
|
||||
include = true;
|
||||
} else {
|
||||
// Need to account for aria-hidden, so can't just check for INVISIBLE
|
||||
// state.
|
||||
include = Utils.getAttributes(child).hidden !== 'true';
|
||||
include = !Utils.isHidden(child);
|
||||
}
|
||||
if (include) {
|
||||
if (aPreorder) {
|
||||
|
@ -24,6 +24,22 @@
|
||||
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);
|
||||
}
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#windows {
|
||||
@ -58,6 +74,7 @@
|
||||
<body>
|
||||
<div>Phone status bar</div>
|
||||
<div id="windows">
|
||||
<button id="back">Back</button>
|
||||
<div id="appframe"></div>
|
||||
<div role="dialog" id="alert" hidden>
|
||||
<h1>This is an alert!</h1>
|
||||
|
@ -24,6 +24,7 @@
|
||||
function doTest() {
|
||||
var doc = currentTabDocument();
|
||||
var iframe = doc.createElement('iframe');
|
||||
iframe.id = 'iframe';
|
||||
iframe.mozbrowser = true;
|
||||
iframe.addEventListener('mozbrowserloadend', function () {
|
||||
var contentTest = new AccessFuContentTest(
|
||||
@ -33,6 +34,9 @@
|
||||
speak: ['Phone status bar', 'Traversal Rule test document'],
|
||||
focused: 'body'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['wow', {'string': 'headingLevel', 'args': [1]} ,'such app'],
|
||||
focused: 'iframe'
|
||||
@ -79,10 +83,16 @@
|
||||
[ContentMessages.simpleMovePrevious, {
|
||||
speak: ['wow', {'string': 'headingLevel', 'args': [1]}]
|
||||
}],
|
||||
[ContentMessages.simpleMovePrevious, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.simpleMovePrevious, {
|
||||
speak: ['Phone status bar']
|
||||
}],
|
||||
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
// Moving to the absolute last item from an embedded document
|
||||
// fails. Bug 972035.
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
@ -101,6 +111,9 @@
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
|
||||
}],
|
||||
@ -134,6 +147,9 @@
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
|
||||
}],
|
||||
@ -149,6 +165,54 @@
|
||||
// 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, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[doc.defaultView.ariaHideBack, {
|
||||
speak: ["wow", {"string": "headingLevel","args": [1]}, "such app"],
|
||||
}],
|
||||
// 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, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||
|
||||
// aria-hidden on the iframe that has the vc.
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
|
||||
}],
|
||||
[doc.defaultView.ariaHideIframe, {
|
||||
speak: ['Home', {'string': 'pushbutton'}]
|
||||
}],
|
||||
[doc.defaultView.ariaShowIframe],
|
||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||
|
||||
// aria-hidden element and auto Move
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
}],
|
||||
[doc.defaultView.ariaHideBack],
|
||||
[ContentMessages.focusSelector('button#back', false), {
|
||||
// Must not speak Back button as it is aria-hidden
|
||||
speak: ["wow", {"string": "headingLevel","args": [1]}, "such app"],
|
||||
}],
|
||||
[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.simpleMoveNext, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
@ -169,6 +233,9 @@
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['Phone status bar', 'Traversal Rule test document']
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ["Back", {"string": "pushbutton"}]
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
|
||||
}],
|
||||
|
@ -33,6 +33,16 @@
|
||||
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;
|
||||
@ -60,7 +70,7 @@
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
"data": [{"string": "hidden"},"I will be hidden"],
|
||||
"data": [{"string": "hidden"}, "I will be hidden"],
|
||||
"options": {
|
||||
"enqueue": true
|
||||
}
|
||||
@ -92,6 +102,54 @@
|
||||
[show(id) for (id of ["to_show_descendant1", "to_show_descendant2",
|
||||
"to_show_descendant3", "to_show_descendant4"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
"data": [{"string": "hidden"}, "I will be hidden"],
|
||||
"options": {
|
||||
"enqueue": true
|
||||
}
|
||||
},
|
||||
action: function action() {
|
||||
[ariaHide(id) for (id of ["to_hide5", "to_hide6", "to_hide7",
|
||||
"to_hide8", "to_hide9"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
"data": [{"string": "hidden"}, "I will be hidden"],
|
||||
"options": {
|
||||
"enqueue": true
|
||||
}
|
||||
},
|
||||
action: function action() {
|
||||
[ariaHide(id) for (id of ["to_hide_descendant5", "to_hide_descendant6",
|
||||
"to_hide_descendant7", "to_hide_descendant8"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
"data": ["I will be shown"],
|
||||
"options": {
|
||||
"enqueue": true
|
||||
}
|
||||
},
|
||||
action: function action() {
|
||||
[ariaShow(id) for (id of ["to_show5", "to_show6", "to_show7",
|
||||
"to_show8", "to_show9"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
"data": ["I will be shown"],
|
||||
"options": {
|
||||
"enqueue": true
|
||||
}
|
||||
},
|
||||
action: function action() {
|
||||
[ariaShow(id) for (id of ["to_show_descendant5", "to_show_descendant6",
|
||||
"to_show_descendant7", "to_show_descendant8"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
@ -103,6 +161,17 @@
|
||||
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",
|
||||
@ -114,6 +183,18 @@
|
||||
action: function action() {
|
||||
[show(id) for (id of ["to_show_live_off", "to_show_live_assertive"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
"data": ["I will be shown"],
|
||||
"options": {
|
||||
"enqueue": false
|
||||
}
|
||||
},
|
||||
action: function action() {
|
||||
[ariaShow(id) for (id of ["to_show_live_off2",
|
||||
"to_show_live_assertive2"])];
|
||||
}
|
||||
}, {
|
||||
expected: {
|
||||
"eventType": "liveregion-change",
|
||||
@ -284,6 +365,12 @@
|
||||
<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>
|
||||
@ -297,11 +384,30 @@
|
||||
<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>
|
||||
@ -315,13 +421,34 @@
|
||||
<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>
|
||||
|
Loading…
Reference in New Issue
Block a user