Bug 893153 - Virtual cursor control refactor, fixes navigating in hidden frames. r=davidb r=maxli

This commit is contained in:
Eitan Isaacson 2013-07-16 11:45:17 -07:00
parent 29052f6c56
commit ca1a8ed56d
3 changed files with 150 additions and 102 deletions

View File

@ -274,9 +274,7 @@ this.AccessFu = {
case 'Accessibility:Focus':
this._focused = JSON.parse(aData);
if (this._focused) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:VirtualCursor',
{action: 'whereIsIt', move: true});
this.showCurrent(true);
}
break;
case 'Accessibility:MoveCaret':
@ -327,20 +325,24 @@ this.AccessFu = {
case 'TabSelect':
{
if (this._focused) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
// We delay this for half a second so the awesomebar could close,
// and we could use the current coordinates for the content item.
// XXX TODO figure out how to avoid magic wait here.
Utils.win.setTimeout(
function () {
mm.sendAsyncMessage('AccessFu:VirtualCursor', {action: 'whereIsIt'});
}, 500);
this.showCurrent(false);
}.bind(this), 500);
}
break;
}
}
},
showCurrent: function showCurrent(aMove) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:ShowCurrent', { move: aMove });
},
announce: function announce(aAnnouncement) {
this._output(Presentation.announce(aAnnouncement),
Utils.CurrentBrowser);
@ -632,8 +634,7 @@ var Input = {
switch (gestureName) {
case 'dwell1':
case 'explore1':
this.moveCursor('moveToPoint', 'SimpleTouch', 'gesture',
aGesture.x, aGesture.y);
this.moveToPoint('SimpleTouch', aGesture.x, aGesture.y);
break;
case 'doubletap1':
this.activateCurrent();
@ -754,12 +755,18 @@ var Input = {
aEvent.stopPropagation();
},
moveCursor: function moveCursor(aAction, aRule, aInputType, aX, aY) {
moveToPoint: function moveToPoint(aRule, aX, aY) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:VirtualCursor',
mm.sendAsyncMessage('AccessFu:MoveToPoint', {rule: aRule,
x: aX, y: aY,
origin: 'top'});
},
moveCursor: function moveCursor(aAction, aRule, aInputType) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:MoveCursor',
{action: aAction, rule: aRule,
x: aX, y: aY, origin: 'top',
inputType: aInputType});
origin: 'top', inputType: aInputType});
},
moveCaret: function moveCaret(aDetails) {

View File

@ -234,7 +234,8 @@ this.Utils = {
inHiddenSubtree: function inHiddenSubtree(aAccessible) {
for (let acc=aAccessible; acc; acc=acc.parent) {
if (JSON.parse(Utils.getAttributes(acc).hidden)) {
let hidden = Utils.getAttributes(acc).hidden;
if (hidden && JSON.parse(hidden)) {
return true;
}
}

View File

@ -26,100 +26,136 @@ Logger.debug('content-script.js');
let eventManager = null;
function virtualCursorControl(aMessage) {
if (Logger.logLevel >= Logger.DEBUG)
Logger.debug(aMessage.name, JSON.stringify(aMessage.json));
function moveCursor(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
try {
let vc = Utils.getVirtualCursor(content.document);
let origin = aMessage.json.origin;
if (origin != 'child') {
if (forwardMessage(vc, aMessage))
return;
}
let vc = Utils.getVirtualCursor(content.document);
let origin = aMessage.json.origin;
let action = aMessage.json.action;
let rule = TraversalRules[aMessage.json.rule];
let details = aMessage.json;
let rule = TraversalRules[details.rule];
let moved = 0;
switch (details.action) {
case 'moveFirst':
case 'moveLast':
moved = vc[details.action](rule);
break;
case 'moveNext':
case 'movePrevious':
try {
if (origin == 'parent' && vc.position == null) {
if (details.action == 'moveNext')
moved = vc.moveFirst(rule);
else
moved = vc.moveLast(rule);
} else {
moved = vc[details.action](rule);
function moveCursorInner() {
try {
if (origin == 'parent' &&
!Utils.isAliveAndVisible(vc.position)) {
// We have a bad position in this frame, move vc to last or first item.
if (action == 'moveNext') {
return vc.moveFirst(rule);
} else if (action == 'movePrevious') {
return vc.moveLast(rule);
}
} catch (x) {
}
return vc[action](rule);
} catch (x) {
if (action == 'moveNext' || action == 'movePrevious') {
// If we are trying to move next/prev put the vc on the focused item.
let acc = Utils.AccRetrieval.
getAccessibleFor(content.document.activeElement);
moved = vc.moveNext(rule, acc, true);
return vc.moveNext(rule, acc, true);
} else {
throw x;
}
break;
case 'moveToPoint':
if (!this._ppcp) {
this._ppcp = Utils.getPixelsPerCSSPixel(content);
}
moved = vc.moveToPoint(rule,
details.x * this._ppcp, details.y * this._ppcp,
true);
break;
case 'whereIsIt':
if (!forwardMessage(vc, aMessage)) {
if (!vc.position && aMessage.json.move)
vc.moveFirst(TraversalRules.Simple);
else {
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE));
}
}
break;
default:
break;
}
if (moved == true) {
forwardMessage(vc, aMessage);
} else if (moved == false && details.action != 'moveToPoint') {
return false;
}
try {
if (origin != 'child' &&
forwardToChild(aMessage, moveCursor, vc.position)) {
// We successfully forwarded the move to the child document.
return;
}
if (moveCursorInner()) {
// If we moved, try forwarding the message to the new position,
// it may be a frame with a vc of its own.
forwardToChild(aMessage, moveCursor, vc.position);
} else {
// If we did not move, we probably reached the end or start of the
// document, go back to parent content and move us out of the iframe.
if (origin == 'parent') {
vc.position = null;
}
aMessage.json.origin = 'child';
sendAsyncMessage('AccessFu:VirtualCursor', aMessage.json);
forwardToParent(aMessage);
}
} catch (x) {
Logger.logException(x, 'Failed to move virtual cursor');
Logger.logException(x, 'Cursor move failed');
}
}
function forwardMessage(aVirtualCursor, aMessage) {
try {
let acc = aVirtualCursor.position;
if (acc && acc.role == ROLE_INTERNAL_FRAME) {
let mm = Utils.getMessageManager(acc.DOMNode);
mm.addMessageListener(aMessage.name, virtualCursorControl);
aMessage.json.origin = 'parent';
if (Utils.isContentProcess) {
// XXX: OOP content's screen offset is 0,
// so we remove the real screen offset here.
aMessage.json.x -= content.mozInnerScreenX;
aMessage.json.y -= content.mozInnerScreenY;
}
mm.sendAsyncMessage(aMessage.name, aMessage.json);
return true;
}
} catch (x) {
// Frame may be hidden, we regard this case as false.
function moveToPoint(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
return false;
let vc = Utils.getVirtualCursor(content.document);
let details = aMessage.json;
let rule = TraversalRules[details.rule];
try {
if (!this._ppcp) {
this._ppcp = Utils.getPixelsPerCSSPixel(content);
}
vc.moveToPoint(rule, details.x * this._ppcp, details.y * this._ppcp, true);
forwardToChild(aMessage, moveToPoint, vc.position);
} catch (x) {
Logger.logException(x, 'Failed move to point');
}
}
function showCurrent(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
let vc = Utils.getVirtualCursor(content.document);
if (!forwardToChild(vc, showCurrent, aMessage)) {
if (!vc.position && aMessage.json.move) {
vc.moveFirst(TraversalRules.Simple);
} else {
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE));
}
}
}
function forwardToParent(aMessage) {
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'child';
sendAsyncMessage(aMessage.name, newJSON);
}
function forwardToChild(aMessage, aListener, aVCPosition) {
let acc = aVCPosition || Utils.getVirtualCursor(content.document).position;
if (!Utils.isAliveAndVisible(acc) || acc.role != ROLE_INTERNAL_FRAME) {
return false;
}
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug('forwardToChild', Logger.accessibleToString(acc),
aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
let mm = Utils.getMessageManager(acc.DOMNode);
mm.addMessageListener(aMessage.name, aListener);
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'parent';
if (Utils.isContentProcess) {
// XXX: OOP content's screen offset is 0,
// so we remove the real screen offset here.
newJSON.x -= content.mozInnerScreenX;
newJSON.y -= content.mozInnerScreenY;
}
mm.sendAsyncMessage(aMessage.name, newJSON);
return true;
}
function activateCurrent(aMessage) {
@ -174,9 +210,10 @@ function activateCurrent(aMessage) {
return;
}
let vc = Utils.getVirtualCursor(content.document);
if (!forwardMessage(vc, aMessage))
activateAccessible(vc.position);
let position = Utils.getVirtualCursor(content.document).position;
if (!forwardToChild(aMessage, activateCurrent, position)) {
activateAccessible(position);
}
}
function activateContextMenu(aMessage) {
@ -188,9 +225,9 @@ function activateContextMenu(aMessage) {
sendAsyncMessage('AccessFu:ActivateContextMenu', {x: x, y: y});
}
let vc = Utils.getVirtualCursor(content.document);
if (!forwardMessage(vc, aMessage))
if (!forwardToChild(aMessage, activateContextMenu, vc.position)) {
sendContextMenuCoordinates(vc.position);
}
}
function moveCaret(aMessage) {
@ -315,15 +352,14 @@ function scroll(aMessage) {
return false;
}
if (aMessage.json.origin != 'child') {
if (forwardMessage(vc, aMessage))
return;
if (aMessage.json.origin != 'child' &&
forwardToChild(aMessage, scroll, vc.position)) {
return;
}
if (!tryToScroll()) {
// Failed to scroll anything in this document. Try in parent document.
aMessage.json.origin = 'child';
sendAsyncMessage('AccessFu:Scroll', aMessage.json);
forwardToParent(aMessage);
}
}
@ -334,7 +370,9 @@ addMessageListener(
if (m.json.buildApp)
Utils.MozBuildApp = m.json.buildApp;
addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
addMessageListener('AccessFu:MoveToPoint', moveToPoint);
addMessageListener('AccessFu:MoveCursor', moveCursor);
addMessageListener('AccessFu:ShowCurrent', showCurrent);
addMessageListener('AccessFu:Activate', activateCurrent);
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
addMessageListener('AccessFu:Scroll', scroll);
@ -351,7 +389,9 @@ addMessageListener(
function(m) {
Logger.debug('AccessFu:Stop');
removeMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
removeMessageListener('AccessFu:MoveToPoint', moveToPoint);
removeMessageListener('AccessFu:MoveCursor', moveCursor);
removeMessageListener('AccessFu:ShowCurrent', showCurrent);
removeMessageListener('AccessFu:Activate', activateCurrent);
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
removeMessageListener('AccessFu:Scroll', scroll);