Bug 1011886 - [AccessFu] Introduce key echo by character, word, and character and word. r=eeejay

This commit is contained in:
Max Li 2014-10-31 08:48:21 -07:00
Родитель d5a8f0d7ec
Коммит 1c013f94e5
5 изменённых файлов: 167 добавлений и 14 удалений

Просмотреть файл

@ -402,10 +402,11 @@ this.EventManager.prototype = {
// If there are embedded objects in the text, ignore them. // If there are embedded objects in the text, ignore them.
// Assuming changes to the descendants would already be handled by the // Assuming changes to the descendants would already be handled by the
// show/hide event. // show/hide event.
let modifiedText = event.modifiedText.replace(/\uFFFC/g, '').trim(); let modifiedText = event.modifiedText.replace(/\uFFFC/g, '');
if (!modifiedText) { if (modifiedText != event.modifiedText && !modifiedText.trim()) {
return; return;
} }
if (aLiveRegion) { if (aLiveRegion) {
if (aEvent.eventType === Events.TEXT_REMOVED) { if (aEvent.eventType === Events.TEXT_REMOVED) {
this._queueLiveEvent(Events.TEXT_REMOVED, aLiveRegion, aIsPolite, this._queueLiveEvent(Events.TEXT_REMOVED, aLiveRegion, aIsPolite,
@ -416,8 +417,8 @@ this.EventManager.prototype = {
modifiedText)); modifiedText));
} }
} else { } else {
this.present(Presentation.textChanged(isInserted, event.start, this.present(Presentation.textChanged(aEvent.accessible, isInserted,
event.length, text, modifiedText)); event.start, event.length, text, modifiedText));
} }
}, },

Просмотреть файл

@ -11,8 +11,7 @@
const {utils: Cu, interfaces: Ci} = Components; const {utils: Cu, interfaces: Ci} = Components;
Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Utils', // jshint ignore:line Cu.import('resource://gre/modules/accessibility/Utils.jsm');
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Logger', // jshint ignore:line XPCOMUtils.defineLazyModuleGetter(this, 'Logger', // jshint ignore:line
'resource://gre/modules/accessibility/Utils.jsm'); 'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'PivotContext', // jshint ignore:line XPCOMUtils.defineLazyModuleGetter(this, 'PivotContext', // jshint ignore:line
@ -60,8 +59,8 @@ Presenter.prototype = {
/** /**
* Text has changed, either by the user or by the system. TODO. * Text has changed, either by the user or by the system. TODO.
*/ */
textChanged: function textChanged(aIsInserted, aStartOffset, aLength, aText, // jshint ignore:line textChanged: function textChanged(aAccessible, aIsInserted, aStartOffset, // jshint ignore:line
aModifiedText) {}, // jshint ignore:line aLength, aText, aModifiedText) {}, // jshint ignore:line
/** /**
* Text selection has changed. TODO. * Text selection has changed. TODO.
@ -344,7 +343,7 @@ AndroidPresenter.prototype.tabStateChanged =
}; };
AndroidPresenter.prototype.textChanged = function AndroidPresenter_textChanged( AndroidPresenter.prototype.textChanged = function AndroidPresenter_textChanged(
aIsInserted, aStart, aLength, aText, aModifiedText) { aAccessible, aIsInserted, aStart, aLength, aText, aModifiedText) {
let eventDetails = { let eventDetails = {
eventType: this.ANDROID_VIEW_TEXT_CHANGED, eventType: this.ANDROID_VIEW_TEXT_CHANGED,
text: [aText], text: [aText],
@ -461,6 +460,13 @@ B2GPresenter.prototype = Object.create(Presenter.prototype);
B2GPresenter.prototype.type = 'B2G'; B2GPresenter.prototype.type = 'B2G';
B2GPresenter.prototype.keyboardEchoSetting =
new PrefCache('accessibility.accessfu.keyboard_echo');
B2GPresenter.prototype.NO_ECHO = 0;
B2GPresenter.prototype.CHARACTER_ECHO = 1;
B2GPresenter.prototype.WORD_ECHO = 2;
B2GPresenter.prototype.CHARACTER_AND_WORD_ECHO = 3;
/** /**
* A pattern used for haptic feedback. * A pattern used for haptic feedback.
* @type {Array} * @type {Array}
@ -497,6 +503,12 @@ B2GPresenter.prototype.pivotChanged =
B2GPresenter.prototype.valueChanged = B2GPresenter.prototype.valueChanged =
function B2GPresenter_valueChanged(aAccessible) { function B2GPresenter_valueChanged(aAccessible) {
// the editable value changes are handled in the text changed presenter
if (Utils.getState(aAccessible).contains(States.EDITABLE)) {
return null;
}
return { return {
type: this.type, type: this.type,
details: { details: {
@ -506,6 +518,42 @@ B2GPresenter.prototype.valueChanged =
}; };
}; };
B2GPresenter.prototype.textChanged = function B2GPresenter_textChanged(
aAccessible, aIsInserted, aStart, aLength, aText, aModifiedText) {
let echoSetting = this.keyboardEchoSetting.value;
let text = '';
if (echoSetting == this.CHARACTER_ECHO ||
echoSetting == this.CHARACTER_AND_WORD_ECHO) {
text = aModifiedText;
}
// add word if word boundary is added
if ((echoSetting == this.WORD_ECHO ||
echoSetting == this.CHARACTER_AND_WORD_ECHO) &&
aIsInserted && aLength === 1) {
let accText = aAccessible.QueryInterface(Ci.nsIAccessibleText);
let startBefore = {}, endBefore = {};
let startAfter = {}, endAfter = {};
accText.getTextBeforeOffset(aStart,
Ci.nsIAccessibleText.BOUNDARY_WORD_END, startBefore, endBefore);
let maybeWord = accText.getTextBeforeOffset(aStart + 1,
Ci.nsIAccessibleText.BOUNDARY_WORD_END, startAfter, endAfter);
if (endBefore.value !== endAfter.value) {
text += maybeWord;
}
}
return {
type: this.type,
details: {
eventType: 'text-change',
data: text
}
};
};
B2GPresenter.prototype.actionInvoked = B2GPresenter.prototype.actionInvoked =
function B2GPresenter_actionInvoked(aObject, aActionName) { function B2GPresenter_actionInvoked(aObject, aActionName) {
return { return {
@ -614,11 +662,11 @@ this.Presentation = { // jshint ignore:line
for each (p in this.presenters)]; // jshint ignore:line for each (p in this.presenters)]; // jshint ignore:line
}, },
textChanged: function Presentation_textChanged(aIsInserted, aStartOffset, textChanged: function Presentation_textChanged(aAccessible, aIsInserted,
aLength, aText, aStartOffset, aLength, aText,
aModifiedText) { aModifiedText) {
return [p.textChanged(aIsInserted, aStartOffset, aLength, aText, // jshint ignore:line return [p.textChanged(aAccessible, aIsInserted, aStartOffset, aLength, // jshint ignore:line
aModifiedText) for each (p in this.presenters)]; // jshint ignore:line aText, aModifiedText) for each (p in this.presenters)]; // jshint ignore:line
}, },
textSelectionChanged: function textSelectionChanged(aText, aStart, aEnd, textSelectionChanged: function textSelectionChanged(aText, aStart, aEnd,

Просмотреть файл

@ -626,6 +626,15 @@ function ExpectedValueChange(aValue, aOptions) {
ExpectedValueChange.prototype = Object.create(ExpectedPresent.prototype); 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) { function ExpectedEditState(aEditState, aOptions) {
ExpectedMessage.call(this, 'AccessFu:Input', aOptions); ExpectedMessage.call(this, 'AccessFu:Input', aOptions);
this.json = aEditState; this.json = aEditState;

Просмотреть файл

@ -8,6 +8,9 @@
<script type="application/javascript" <script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"> src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
</script> </script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js">
</script>
<script type="application/javascript" <script type="application/javascript"
src="chrome://mochikit/content/chrome-harness.js"> src="chrome://mochikit/content/chrome-harness.js">
</script> </script>
@ -169,9 +172,98 @@
multiline: false, multiline: false,
atStart: true, atStart: true,
atEnd: false atEnd: false
}, { focused: 'html' })] }, { 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.setIntPref(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.setIntPref(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.setIntPref(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.setIntPref(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() { addA11yLoadEvent(function() {
textTest.start(function () { textTest.start(function () {
closeBrowserWindow(); closeBrowserWindow();

Просмотреть файл

@ -797,6 +797,9 @@ pref("accessibility.accessfu.quicknav_index", 0);
pref("accessibility.accessfu.utterance", 1); pref("accessibility.accessfu.utterance", 1);
// Whether to skip images with empty alt text // Whether to skip images with empty alt text
pref("accessibility.accessfu.skip_empty_images", true); pref("accessibility.accessfu.skip_empty_images", true);
// Setting to change the verbosity of entered text (0 - none, 1 - characters,
// 2 - words, 3 - both)
pref("accessibility.accessfu.keyboard_echo", 3);
// Enable hit-target fluffing // Enable hit-target fluffing
pref("ui.touch.radius.enabled", true); pref("ui.touch.radius.enabled", true);