mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 886076 - Part 2: Support movement by granularity in AccessFu. r=eeejay
This commit is contained in:
parent
42034f7543
commit
dec1b1843d
@ -116,7 +116,7 @@ this.AccessFu = {
|
||||
Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:Focus', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:ActivateObject', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:MoveCaret', false);
|
||||
Services.obs.addObserver(this, 'Accessibility:MoveByGranularity', false);
|
||||
Utils.win.addEventListener('TabOpen', this);
|
||||
Utils.win.addEventListener('TabClose', this);
|
||||
Utils.win.addEventListener('TabSelect', this);
|
||||
@ -159,7 +159,7 @@ this.AccessFu = {
|
||||
Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
|
||||
Services.obs.removeObserver(this, 'Accessibility:Focus');
|
||||
Services.obs.removeObserver(this, 'Accessibility:ActivateObject');
|
||||
Services.obs.removeObserver(this, 'Accessibility:MoveCaret');
|
||||
Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity');
|
||||
|
||||
if (this.doneCallback) {
|
||||
this.doneCallback();
|
||||
@ -277,8 +277,8 @@ this.AccessFu = {
|
||||
this.showCurrent(true);
|
||||
}
|
||||
break;
|
||||
case 'Accessibility:MoveCaret':
|
||||
this.Input.moveCaret(JSON.parse(aData));
|
||||
case 'Accessibility:MoveByGranularity':
|
||||
this.Input.moveByGranularity(JSON.parse(aData));
|
||||
break;
|
||||
case 'remote-browser-frame-shown':
|
||||
case 'in-process-browser-or-app-frame-shown':
|
||||
@ -788,16 +788,23 @@ var Input = {
|
||||
origin: 'top', inputType: aInputType});
|
||||
},
|
||||
|
||||
moveCaret: function moveCaret(aDetails) {
|
||||
moveByGranularity: function moveByGranularity(aDetails) {
|
||||
const MOVEMENT_GRANULARITY_PARAGRAPH = 8;
|
||||
|
||||
if (!this.editState.editing) {
|
||||
return;
|
||||
if (aDetails.granularity === MOVEMENT_GRANULARITY_PARAGRAPH) {
|
||||
this.moveCursor('move' + aDetails.direction, 'Paragraph', 'gesture');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
aDetails.atStart = this.editState.atStart;
|
||||
aDetails.atEnd = this.editState.atEnd;
|
||||
}
|
||||
|
||||
aDetails.atStart = this.editState.atStart;
|
||||
aDetails.atEnd = this.editState.atEnd;
|
||||
|
||||
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
|
||||
mm.sendAsyncMessage('AccessFu:MoveCaret', aDetails);
|
||||
let type = this.editState.editing ? 'AccessFu:MoveCaret' :
|
||||
'AccessFu:MoveByGranularity';
|
||||
mm.sendAsyncMessage(type, aDetails);
|
||||
},
|
||||
|
||||
activateCurrent: function activateCurrent(aData) {
|
||||
|
@ -156,11 +156,12 @@ this.EventManager.prototype = {
|
||||
QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
|
||||
let reason = event.reason;
|
||||
|
||||
if (this.editState.editing)
|
||||
if (this.editState.editing) {
|
||||
aEvent.accessibleDocument.takeFocus();
|
||||
|
||||
}
|
||||
this.present(
|
||||
Presentation.pivotChanged(position, event.oldAccessible, reason));
|
||||
Presentation.pivotChanged(position, event.oldAccessible, reason,
|
||||
pivot.startOffset, pivot.endOffset));
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -124,9 +124,18 @@ VisualPresenter.prototype = {
|
||||
BORDER_PADDING: 2,
|
||||
|
||||
viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
|
||||
let currentAcc = this._displayedAccessibles.get(aWindow);
|
||||
let currentDisplay = this._displayedAccessibles.get(aWindow);
|
||||
if (!currentDisplay) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let currentAcc = currentDisplay.accessible;
|
||||
let start = currentDisplay.startOffset;
|
||||
let end = currentDisplay.endOffset;
|
||||
if (Utils.isAliveAndVisible(currentAcc)) {
|
||||
let bounds = Utils.getBounds(currentAcc);
|
||||
let bounds = (start === -1 && end === -1) ? Utils.getBounds(currentAcc) :
|
||||
Utils.getTextBounds(currentAcc, start, end);
|
||||
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
@ -142,7 +151,9 @@ VisualPresenter.prototype = {
|
||||
|
||||
pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
|
||||
this._displayedAccessibles.set(aContext.accessible.document.window,
|
||||
aContext.accessible);
|
||||
{ accessible: aContext.accessible,
|
||||
startOffset: aContext.startOffset,
|
||||
endOffset: aContext.endOffset });
|
||||
|
||||
if (!aContext.accessible)
|
||||
return {type: this.type, details: {method: 'hideBounds'}};
|
||||
@ -150,11 +161,16 @@ VisualPresenter.prototype = {
|
||||
try {
|
||||
aContext.accessible.scrollTo(
|
||||
Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
|
||||
|
||||
let bounds = (aContext.startOffset === -1 && aContext.endOffset === -1) ?
|
||||
aContext.bounds : Utils.getTextBounds(aContext.accessible,
|
||||
aContext.startOffset, aContext.endOffset);
|
||||
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
method: 'showBounds',
|
||||
bounds: aContext.bounds,
|
||||
bounds: bounds,
|
||||
padding: this.BORDER_PADDING
|
||||
}
|
||||
};
|
||||
@ -232,8 +248,6 @@ AndroidPresenter.prototype = {
|
||||
androidEvents.push({eventType: this.ANDROID_VIEW_HOVER_EXIT, text: []});
|
||||
}
|
||||
|
||||
let state = Utils.getStates(aContext.accessible)[0];
|
||||
|
||||
let brailleOutput = {};
|
||||
if (Utils.AndroidSdkVersion >= 16) {
|
||||
if (!this._braillePresenter) {
|
||||
@ -243,16 +257,30 @@ AndroidPresenter.prototype = {
|
||||
details;
|
||||
}
|
||||
|
||||
androidEvents.push({eventType: (isExploreByTouch) ?
|
||||
this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
|
||||
text: UtteranceGenerator.genForContext(aContext).output,
|
||||
bounds: aContext.bounds,
|
||||
clickable: aContext.accessible.actionCount > 0,
|
||||
checkable: !!(state &
|
||||
Ci.nsIAccessibleStates.STATE_CHECKABLE),
|
||||
checked: !!(state &
|
||||
Ci.nsIAccessibleStates.STATE_CHECKED),
|
||||
brailleOutput: brailleOutput});
|
||||
if (aReason === Ci.nsIAccessiblePivot.REASON_TEXT) {
|
||||
if (Utils.AndroidSdkVersion >= 16) {
|
||||
let adjustedText = aContext.textAndAdjustedOffsets;
|
||||
|
||||
androidEvents.push({
|
||||
eventType: this.ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
text: [adjustedText.text],
|
||||
fromIndex: adjustedText.startOffset,
|
||||
toIndex: adjustedText.endOffset
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let state = Utils.getStates(aContext.accessible)[0];
|
||||
androidEvents.push({eventType: (isExploreByTouch) ?
|
||||
this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
|
||||
text: UtteranceGenerator.genForContext(aContext).output,
|
||||
bounds: aContext.bounds,
|
||||
clickable: aContext.accessible.actionCount > 0,
|
||||
checkable: !!(state &
|
||||
Ci.nsIAccessibleStates.STATE_CHECKABLE),
|
||||
checked: !!(state &
|
||||
Ci.nsIAccessibleStates.STATE_CHECKED),
|
||||
brailleOutput: brailleOutput});
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
@ -494,10 +522,9 @@ this.Presentation = {
|
||||
return this.presenters;
|
||||
},
|
||||
|
||||
pivotChanged: function Presentation_pivotChanged(aPosition,
|
||||
aOldPosition,
|
||||
aReason) {
|
||||
let context = new PivotContext(aPosition, aOldPosition);
|
||||
pivotChanged: function Presentation_pivotChanged(aPosition, aOldPosition, aReason,
|
||||
aStartOffset, aEndOffset) {
|
||||
let context = new PivotContext(aPosition, aOldPosition, aStartOffset, aEndOffset);
|
||||
return [p.pivotChanged(context, aReason)
|
||||
for each (p in this.presenters)];
|
||||
},
|
||||
|
@ -43,6 +43,8 @@ const ROLE_TERM = Ci.nsIAccessibleRole.ROLE_TERM;
|
||||
const ROLE_SEPARATOR = Ci.nsIAccessibleRole.ROLE_SEPARATOR;
|
||||
const ROLE_TABLE = Ci.nsIAccessibleRole.ROLE_TABLE;
|
||||
const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
|
||||
const ROLE_PARAGRAPH = Ci.nsIAccessibleRole.ROLE_PARAGRAPH;
|
||||
const ROLE_SECTION = Ci.nsIAccessibleRole.ROLE_SECTION;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ['TraversalRules'];
|
||||
|
||||
@ -248,6 +250,19 @@ this.TraversalRules = {
|
||||
PageTab: new BaseTraversalRule(
|
||||
[ROLE_PAGETAB]),
|
||||
|
||||
Paragraph: new BaseTraversalRule(
|
||||
[ROLE_PARAGRAPH,
|
||||
ROLE_SECTION],
|
||||
function Paragraph_match(aAccessible) {
|
||||
for (let child = aAccessible.firstChild; child; child = child.nextSibling) {
|
||||
if (child.role === ROLE_TEXT_LEAF) {
|
||||
return FILTER_MATCH | FILTER_IGNORE_SUBTREE;
|
||||
}
|
||||
}
|
||||
|
||||
return FILTER_IGNORE;
|
||||
}),
|
||||
|
||||
RadioButton: new BaseTraversalRule(
|
||||
[ROLE_RADIOBUTTON,
|
||||
ROLE_RADIO_MENU_ITEM]),
|
||||
|
@ -227,6 +227,14 @@ this.Utils = {
|
||||
return new Rect(objX.value, objY.value, objW.value, objH.value);
|
||||
},
|
||||
|
||||
getTextBounds: function getTextBounds(aAccessible, aStart, aEnd) {
|
||||
let accText = aAccessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
let objX = {}, objY = {}, objW = {}, objH = {};
|
||||
accText.getRangeExtents(aStart, aEnd, objX, objY, objW, objH,
|
||||
Ci.nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE);
|
||||
return new Rect(objX.value, objY.value, objW.value, objH.value);
|
||||
},
|
||||
|
||||
inHiddenSubtree: function inHiddenSubtree(aAccessible) {
|
||||
for (let acc=aAccessible; acc; acc=acc.parent) {
|
||||
let hidden = Utils.getAttributes(acc).hidden;
|
||||
@ -413,10 +421,13 @@ this.Logger = {
|
||||
* PivotContext: An object that generates and caches context information
|
||||
* for a given accessible and its relationship with another accessible.
|
||||
*/
|
||||
this.PivotContext = function PivotContext(aAccessible, aOldAccessible) {
|
||||
this.PivotContext = function PivotContext(aAccessible, aOldAccessible,
|
||||
aStartOffset, aEndOffset) {
|
||||
this._accessible = aAccessible;
|
||||
this._oldAccessible =
|
||||
this._isDefunct(aOldAccessible) ? null : aOldAccessible;
|
||||
this.startOffset = aStartOffset;
|
||||
this.endOffset = aEndOffset;
|
||||
}
|
||||
|
||||
PivotContext.prototype = {
|
||||
@ -428,6 +439,45 @@ PivotContext.prototype = {
|
||||
return this._oldAccessible;
|
||||
},
|
||||
|
||||
get textAndAdjustedOffsets() {
|
||||
if (this.startOffset === -1 && this.endOffset === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this._textAndAdjustedOffsets) {
|
||||
let result = {startOffset: this.startOffset,
|
||||
endOffset: this.endOffset,
|
||||
text: this._accessible.QueryInterface(Ci.nsIAccessibleText).
|
||||
getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT)};
|
||||
let hypertextAcc = this._accessible.QueryInterface(Ci.nsIAccessibleHyperText);
|
||||
|
||||
// Iterate through the links in backwards order so text replacements don't
|
||||
// affect the offsets of links yet to be processed.
|
||||
for (let i = hypertextAcc.linkCount - 1; i >= 0; i--) {
|
||||
let link = hypertextAcc.getLinkAt(i);
|
||||
let linkText = '';
|
||||
if (link instanceof Ci.nsIAccessibleText) {
|
||||
linkText = link.QueryInterface(Ci.nsIAccessibleText).
|
||||
getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
|
||||
}
|
||||
|
||||
let start = link.startIndex;
|
||||
let end = link.endIndex;
|
||||
for (let offset of ['startOffset', 'endOffset']) {
|
||||
if (this[offset] >= end) {
|
||||
result[offset] += linkText.length - (end - start);
|
||||
}
|
||||
}
|
||||
result.text = result.text.substring(0, start) + linkText +
|
||||
result.text.substring(end);
|
||||
}
|
||||
|
||||
this._textAndAdjustedOffsets = result;
|
||||
}
|
||||
|
||||
return this._textAndAdjustedOffsets;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of |aAccessible|'s ancestry up to the root.
|
||||
* @param {nsIAccessible} aAccessible.
|
||||
|
@ -8,6 +8,10 @@ let Cu = Components.utils;
|
||||
const ROLE_ENTRY = Ci.nsIAccessibleRole.ROLE_ENTRY;
|
||||
const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
|
||||
|
||||
const MOVEMENT_GRANULARITY_CHARACTER = 1;
|
||||
const MOVEMENT_GRANULARITY_WORD = 2;
|
||||
const MOVEMENT_GRANULARITY_PARAGRAPH = 8;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
|
||||
'resource://gre/modules/accessibility/Utils.jsm');
|
||||
@ -117,7 +121,8 @@ function showCurrent(aMessage) {
|
||||
vc.moveFirst(TraversalRules.Simple);
|
||||
} else {
|
||||
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
|
||||
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE));
|
||||
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE,
|
||||
vc.startOffset, vc.endOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,11 +231,30 @@ function activateContextMenu(aMessage) {
|
||||
}
|
||||
}
|
||||
|
||||
function moveCaret(aMessage) {
|
||||
const MOVEMENT_GRANULARITY_CHARACTER = 1;
|
||||
const MOVEMENT_GRANULARITY_WORD = 2;
|
||||
const MOVEMENT_GRANULARITY_PARAGRAPH = 8;
|
||||
function moveByGranularity(aMessage) {
|
||||
let direction = aMessage.json.direction;
|
||||
let vc = Utils.getVirtualCursor(content.document);
|
||||
let granularity;
|
||||
|
||||
switch(aMessage.json.granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
granularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
granularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === 'Previous') {
|
||||
vc.movePreviousByText(granularity);
|
||||
} else if (direction === 'Next') {
|
||||
vc.moveNextByText(granularity);
|
||||
}
|
||||
}
|
||||
|
||||
function moveCaret(aMessage) {
|
||||
let direction = aMessage.json.direction;
|
||||
let granularity = aMessage.json.granularity;
|
||||
let accessible = Utils.getVirtualCursor(content.document).position;
|
||||
@ -373,6 +397,7 @@ addMessageListener(
|
||||
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
||||
addMessageListener('AccessFu:Scroll', scroll);
|
||||
addMessageListener('AccessFu:MoveCaret', moveCaret);
|
||||
addMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
|
||||
|
||||
if (!eventManager) {
|
||||
eventManager = new EventManager(this);
|
||||
@ -392,6 +417,7 @@ addMessageListener(
|
||||
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
||||
removeMessageListener('AccessFu:Scroll', scroll);
|
||||
removeMessageListener('AccessFu:MoveCaret', moveCaret);
|
||||
removeMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
|
||||
|
||||
eventManager.stop();
|
||||
});
|
||||
|
@ -364,7 +364,7 @@ public class GeckoAccessibility {
|
||||
return true;
|
||||
}
|
||||
GeckoAppShell.
|
||||
sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:MoveCaret", movementData.toString()));
|
||||
sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:MoveByGranularity", movementData.toString()));
|
||||
}
|
||||
return true;
|
||||
} else if (action == AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY &&
|
||||
@ -377,7 +377,7 @@ public class GeckoAccessibility {
|
||||
return true;
|
||||
}
|
||||
GeckoAppShell.
|
||||
sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:MoveCaret", movementData.toString()));
|
||||
sendEventToGecko(GeckoEvent.createBroadcastEvent("Accessibility:MoveByGranularity", movementData.toString()));
|
||||
return true;
|
||||
}
|
||||
return host.performAccessibilityAction(action, arguments);
|
||||
|
Loading…
Reference in New Issue
Block a user