зеркало из https://github.com/mozilla/gecko-dev.git
Bug 818342 - Introduce announcement output. r=davidb
This commit is contained in:
Родитель
b7be18150c
Коммит
f7d7e441ed
|
@ -28,3 +28,35 @@
|
||||||
top: 0px;
|
top: 0px;
|
||||||
left: 0px;
|
left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#announce-box {
|
||||||
|
position: fixed;
|
||||||
|
width: 7.5em;
|
||||||
|
height: 5em;
|
||||||
|
top: calc(100% - 50% - 2.5em);
|
||||||
|
left: calc(100% - 50% - 3.75em);
|
||||||
|
pointer-events: none;
|
||||||
|
display: table;
|
||||||
|
font-size: 28pt;
|
||||||
|
font-weight: 700;
|
||||||
|
color: orange;
|
||||||
|
background-color: black;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#announce-box:not(.showing) {
|
||||||
|
opacity: 0.0;
|
||||||
|
margin: 0.1em;
|
||||||
|
-moz-transition: opacity 0.4s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
#announce-box.showing {
|
||||||
|
opacity: 1.0;
|
||||||
|
-moz-transition: opacity 0.2s linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
#announce-box * {
|
||||||
|
text-align: center;
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ this.AccessFu = {
|
||||||
|
|
||||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||||
Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
|
Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
|
||||||
|
Cu.import('resource://gre/modules/accessibility/Presentation.jsm');
|
||||||
|
|
||||||
Logger.info('enable');
|
Logger.info('enable');
|
||||||
|
|
||||||
|
@ -145,26 +146,30 @@ this.AccessFu = {
|
||||||
|
|
||||||
switch (aMessage.name) {
|
switch (aMessage.name) {
|
||||||
case 'AccessFu:Ready':
|
case 'AccessFu:Ready':
|
||||||
let mm = Utils.getMessageManager(aMessage.target);
|
let mm = Utils.getMessageManager(aMessage.target);
|
||||||
mm.sendAsyncMessage('AccessFu:Start',
|
mm.sendAsyncMessage('AccessFu:Start',
|
||||||
{method: 'start', buildApp: Utils.MozBuildApp});
|
{method: 'start', buildApp: Utils.MozBuildApp});
|
||||||
break;
|
break;
|
||||||
case 'AccessFu:Present':
|
case 'AccessFu:Present':
|
||||||
|
this._output(aMessage.json, aMessage.target);
|
||||||
|
break;
|
||||||
|
case 'AccessFu:Input':
|
||||||
|
Input.setEditState(aMessage.json);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_output: function _output(aPresentationData, aBrowser) {
|
||||||
try {
|
try {
|
||||||
for each (let presenter in aMessage.json) {
|
for each (let presenter in aPresentationData) {
|
||||||
if (!presenter)
|
if (!presenter)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
Output[presenter.type](presenter.details, aMessage.target);
|
Output[presenter.type](presenter.details, aBrowser);
|
||||||
}
|
}
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
Logger.logException(x);
|
Logger.logException(x);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 'AccessFu:Input':
|
|
||||||
Input.setEditState(aMessage.json);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_loadFrameScript: function _loadFrameScript(aMessageManager) {
|
_loadFrameScript: function _loadFrameScript(aMessageManager) {
|
||||||
|
@ -243,6 +248,11 @@ this.AccessFu = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
announce: function announce(aAnnouncement) {
|
||||||
|
this._output(Presentation.announce(aAnnouncement),
|
||||||
|
Utils.getCurrentBrowser(this.chromeWin));
|
||||||
|
},
|
||||||
|
|
||||||
// So we don't enable/disable twice
|
// So we don't enable/disable twice
|
||||||
_enabled: false,
|
_enabled: false,
|
||||||
|
|
||||||
|
@ -262,34 +272,71 @@ var Output = {
|
||||||
},
|
},
|
||||||
|
|
||||||
Visual: function Visual(aDetails, aBrowser) {
|
Visual: function Visual(aDetails, aBrowser) {
|
||||||
if (!this.highlightBox) {
|
switch (aDetails.method) {
|
||||||
// Add highlight box
|
case 'showBounds':
|
||||||
this.highlightBox = this.chromeWin.document.
|
{
|
||||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
if (!this.highlightBox) {
|
||||||
this.chromeWin.document.documentElement.appendChild(this.highlightBox);
|
// Add highlight box
|
||||||
this.highlightBox.id = 'virtual-cursor-box';
|
this.highlightBox = this.chromeWin.document.
|
||||||
|
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||||
|
this.chromeWin.document.documentElement.appendChild(this.highlightBox);
|
||||||
|
this.highlightBox.id = 'virtual-cursor-box';
|
||||||
|
|
||||||
// Add highlight inset for inner shadow
|
// Add highlight inset for inner shadow
|
||||||
let inset = this.chromeWin.document.
|
let inset = this.chromeWin.document.
|
||||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||||
inset.id = 'virtual-cursor-inset';
|
inset.id = 'virtual-cursor-inset';
|
||||||
|
|
||||||
this.highlightBox.appendChild(inset);
|
this.highlightBox.appendChild(inset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aDetails.method == 'show') {
|
let padding = aDetails.padding;
|
||||||
let padding = aDetails.padding;
|
let r = this._adjustBounds(aDetails.bounds, aBrowser);
|
||||||
let r = this._adjustBounds(aDetails.bounds, aBrowser);
|
|
||||||
|
|
||||||
// First hide it to avoid flickering when changing the style.
|
// First hide it to avoid flickering when changing the style.
|
||||||
this.highlightBox.style.display = 'none';
|
this.highlightBox.style.display = 'none';
|
||||||
this.highlightBox.style.top = (r.top - padding) + 'px';
|
this.highlightBox.style.top = (r.top - padding) + 'px';
|
||||||
this.highlightBox.style.left = (r.left - padding) + 'px';
|
this.highlightBox.style.left = (r.left - padding) + 'px';
|
||||||
this.highlightBox.style.width = (r.width + padding*2) + 'px';
|
this.highlightBox.style.width = (r.width + padding*2) + 'px';
|
||||||
this.highlightBox.style.height = (r.height + padding*2) + 'px';
|
this.highlightBox.style.height = (r.height + padding*2) + 'px';
|
||||||
this.highlightBox.style.display = 'block';
|
this.highlightBox.style.display = 'block';
|
||||||
} else if (aDetails.method == 'hide') {
|
|
||||||
this.highlightBox.style.display = 'none';
|
break;
|
||||||
|
}
|
||||||
|
case 'hideBounds':
|
||||||
|
{
|
||||||
|
if (this.highlightBox)
|
||||||
|
this.highlightBox.style.display = 'none';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'showAnnouncement':
|
||||||
|
{
|
||||||
|
if (!this.announceBox) {
|
||||||
|
this.announceBox = this.chromeWin.document.
|
||||||
|
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||||
|
this.announceBox.id = 'announce-box';
|
||||||
|
this.chromeWin.document.documentElement.appendChild(this.announceBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.announceBox.innerHTML = '<div>' + aDetails.text + '</div>';
|
||||||
|
this.announceBox.classList.add('showing');
|
||||||
|
|
||||||
|
if (this._announceHideTimeout)
|
||||||
|
this.chromeWin.clearTimeout(this._announceHideTimeout);
|
||||||
|
|
||||||
|
if (aDetails.duration > 0)
|
||||||
|
this._announceHideTimeout = this.chromeWin.setTimeout(
|
||||||
|
function () {
|
||||||
|
this.announceBox.classList.remove('showing');
|
||||||
|
this._announceHideTimeout = 0;
|
||||||
|
}.bind(this), aDetails.duration);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'hideAnnouncement':
|
||||||
|
{
|
||||||
|
this.announceBox.classList.remove('showing');
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,12 @@ Presenter.prototype = {
|
||||||
/**
|
/**
|
||||||
* We have entered or left text editing mode.
|
* We have entered or left text editing mode.
|
||||||
*/
|
*/
|
||||||
editingModeChanged: function editingModeChanged(aIsEditing) {}
|
editingModeChanged: function editingModeChanged(aIsEditing) {},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Announce something. Typically an app state change.
|
||||||
|
*/
|
||||||
|
announce: function announce(aAnnouncement) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +130,7 @@ VisualPresenter.prototype = {
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
details: {
|
details: {
|
||||||
method: 'show',
|
method: 'showBounds',
|
||||||
bounds: context.bounds,
|
bounds: context.bounds,
|
||||||
padding: this.BORDER_PADDING
|
padding: this.BORDER_PADDING
|
||||||
}
|
}
|
||||||
|
@ -139,7 +144,7 @@ VisualPresenter.prototype = {
|
||||||
this._currentAccessible = aContext.accessible;
|
this._currentAccessible = aContext.accessible;
|
||||||
|
|
||||||
if (!aContext.accessible)
|
if (!aContext.accessible)
|
||||||
return {type: this.type, details: {method: 'hide'}};
|
return {type: this.type, details: {method: 'hideBounds'}};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aContext.accessible.scrollTo(
|
aContext.accessible.scrollTo(
|
||||||
|
@ -147,7 +152,7 @@ VisualPresenter.prototype = {
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
details: {
|
details: {
|
||||||
method: 'show',
|
method: 'showBounds',
|
||||||
bounds: aContext.bounds,
|
bounds: aContext.bounds,
|
||||||
padding: this.BORDER_PADDING
|
padding: this.BORDER_PADDING
|
||||||
}
|
}
|
||||||
|
@ -165,9 +170,20 @@ VisualPresenter.prototype = {
|
||||||
tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
|
tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
|
||||||
aPageState) {
|
aPageState) {
|
||||||
if (aPageState == 'newdoc')
|
if (aPageState == 'newdoc')
|
||||||
return {type: this.type, details: {method: 'hide'}};
|
return {type: this.type, details: {method: 'hideBounds'}};
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
announce: function VisualPresenter_announce(aAnnouncement) {
|
||||||
|
return {
|
||||||
|
type: this.type,
|
||||||
|
details: {
|
||||||
|
method: 'showAnnouncement',
|
||||||
|
text: aAnnouncement,
|
||||||
|
duration: 1000
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -257,8 +273,8 @@ AndroidPresenter.prototype = {
|
||||||
|
|
||||||
tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
|
tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
|
||||||
aPageState) {
|
aPageState) {
|
||||||
return this._appAnnounce(
|
return this.announce(
|
||||||
UtteranceGenerator.genForTabStateChange(aDocObj, aPageState));
|
UtteranceGenerator.genForTabStateChange(aDocObj, aPageState).join(' '));
|
||||||
},
|
},
|
||||||
|
|
||||||
textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
|
textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
|
||||||
|
@ -303,20 +319,18 @@ AndroidPresenter.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
|
editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
|
||||||
return this._appAnnounce(UtteranceGenerator.genForEditingMode(aIsEditing));
|
return this.announce(
|
||||||
|
UtteranceGenerator.genForEditingMode(aIsEditing).join(' '));
|
||||||
},
|
},
|
||||||
|
|
||||||
_appAnnounce: function _appAnnounce(aUtterance) {
|
announce: function AndroidPresenter_announce(aAnnouncement) {
|
||||||
if (!aUtterance.length)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: this.type,
|
type: this.type,
|
||||||
details: [{
|
details: [{
|
||||||
eventType: (Utils.AndroidSdkVersion >= 16) ?
|
eventType: (Utils.AndroidSdkVersion >= 16) ?
|
||||||
this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
|
this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
|
||||||
text: aUtterance,
|
text: [aAnnouncement],
|
||||||
addedCount: aUtterance.join(' ').length,
|
addedCount: aAnnouncement.length,
|
||||||
removedCount: 0,
|
removedCount: 0,
|
||||||
fromIndex: 0
|
fromIndex: 0
|
||||||
}]
|
}]
|
||||||
|
@ -500,7 +514,6 @@ PresenterContext.prototype = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this.Presentation = {
|
this.Presentation = {
|
||||||
get presenters() {
|
get presenters() {
|
||||||
delete this.presenters;
|
delete this.presenters;
|
||||||
|
@ -550,5 +563,12 @@ this.Presentation = {
|
||||||
editingModeChanged: function Presentation_editingModeChanged(aIsEditing) {
|
editingModeChanged: function Presentation_editingModeChanged(aIsEditing) {
|
||||||
return [p.editingModeChanged(aIsEditing)
|
return [p.editingModeChanged(aIsEditing)
|
||||||
for each (p in this.presenters)];
|
for each (p in this.presenters)];
|
||||||
|
},
|
||||||
|
|
||||||
|
announce: function Presentation_announce(aAnnouncement) {
|
||||||
|
// XXX: Typically each presenter uses the UtteranceGenerator,
|
||||||
|
// but there really isn't a point here.
|
||||||
|
return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
|
||||||
|
for each (p in this.presenters)];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -97,6 +97,20 @@ this.UtteranceGenerator = {
|
||||||
return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])];
|
return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates an utterance for an announcement. Basically attempts to localize
|
||||||
|
* the announcement string.
|
||||||
|
* @param {string} aAnnouncement unlocalized announcement.
|
||||||
|
* @return {Array} A one string array with the announcement.
|
||||||
|
*/
|
||||||
|
genForAnnouncement: function genForAnnouncement(aAnnouncement) {
|
||||||
|
try {
|
||||||
|
return [gStringBundle.GetStringFromName(aAnnouncement)];
|
||||||
|
} catch (x) {
|
||||||
|
return [aAnnouncement];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an utterance for a tab state change.
|
* Generates an utterance for a tab state change.
|
||||||
* @param {nsIAccessible} aAccessible accessible object of the tab's attached
|
* @param {nsIAccessible} aAccessible accessible object of the tab's attached
|
||||||
|
@ -309,7 +323,7 @@ this.UtteranceGenerator = {
|
||||||
|
|
||||||
return stateUtterances;
|
return stateUtterances;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getListUtterance: function _getListUtterance(aAccessible, aRoleStr, aFlags, aItemCount) {
|
_getListUtterance: function _getListUtterance(aAccessible, aRoleStr, aFlags, aItemCount) {
|
||||||
let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
|
let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
|
||||||
let desc = [];
|
let desc = [];
|
||||||
|
|
Загрузка…
Ссылка в новой задаче