2012-10-01 13:33:26 -07:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2013-05-21 11:16:49 -07:00
|
|
|
let Ci = Components.interfaces;
|
|
|
|
let Cu = Components.utils;
|
|
|
|
|
|
|
|
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
|
|
|
|
'resource://gre/modules/accessibility/Utils.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'Presentation',
|
|
|
|
'resource://gre/modules/accessibility/Presentation.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'TraversalRules',
|
|
|
|
'resource://gre/modules/accessibility/TraversalRules.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
|
|
|
|
'resource://gre/modules/accessibility/Utils.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'EventManager',
|
|
|
|
'resource://gre/modules/accessibility/EventManager.jsm');
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Logger.debug('content-script.js');
|
|
|
|
|
2013-05-21 11:16:49 -07:00
|
|
|
let eventManager = null;
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
function virtualCursorControl(aMessage) {
|
|
|
|
if (Logger.logLevel >= Logger.DEBUG)
|
|
|
|
Logger.debug(aMessage.name, JSON.stringify(aMessage.json));
|
|
|
|
|
|
|
|
try {
|
|
|
|
let vc = Utils.getVirtualCursor(content.document);
|
|
|
|
let origin = aMessage.json.origin;
|
|
|
|
if (origin != 'child') {
|
|
|
|
if (forwardMessage(vc, aMessage))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
} catch (x) {
|
2012-10-08 00:06:38 -07:00
|
|
|
let acc = Utils.AccRetrieval.
|
|
|
|
getAccessibleFor(content.document.activeElement);
|
|
|
|
moved = vc.moveNext(rule, acc, true);
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'moveToPoint':
|
2013-05-21 11:16:50 -07:00
|
|
|
if (!this._ppcp) {
|
|
|
|
this._ppcp = Utils.getPixelsPerCSSPixel(content);
|
|
|
|
}
|
|
|
|
moved = vc.moveToPoint(rule,
|
|
|
|
details.x * this._ppcp, details.y * this._ppcp,
|
|
|
|
true);
|
2012-10-19 10:06:08 -07:00
|
|
|
break;
|
|
|
|
case 'whereIsIt':
|
|
|
|
if (!forwardMessage(vc, aMessage)) {
|
|
|
|
if (!vc.position && aMessage.json.move)
|
|
|
|
vc.moveFirst(TraversalRules.Simple);
|
2013-05-21 11:16:49 -07:00
|
|
|
else {
|
|
|
|
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
|
|
|
|
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE));
|
|
|
|
}
|
2012-10-19 10:06:08 -07:00
|
|
|
}
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (moved == true) {
|
|
|
|
forwardMessage(vc, aMessage);
|
|
|
|
} else if (moved == false && details.action != 'moveToPoint') {
|
|
|
|
if (origin == 'parent') {
|
|
|
|
vc.position = null;
|
|
|
|
}
|
|
|
|
aMessage.json.origin = 'child';
|
|
|
|
sendAsyncMessage('AccessFu:VirtualCursor', aMessage.json);
|
|
|
|
}
|
|
|
|
} catch (x) {
|
|
|
|
Logger.error(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function forwardMessage(aVirtualCursor, aMessage) {
|
|
|
|
try {
|
|
|
|
let acc = aVirtualCursor.position;
|
|
|
|
if (acc && acc.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
|
|
|
|
let mm = Utils.getMessageManager(acc.DOMNode);
|
|
|
|
mm.addMessageListener(aMessage.name, virtualCursorControl);
|
|
|
|
aMessage.json.origin = 'parent';
|
2013-05-21 11:16:50 -07:00
|
|
|
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;
|
|
|
|
}
|
2012-10-01 13:33:26 -07:00
|
|
|
mm.sendAsyncMessage(aMessage.name, aMessage.json);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch (x) {
|
2012-10-08 00:06:38 -07:00
|
|
|
// Frame may be hidden, we regard this case as false.
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function activateCurrent(aMessage) {
|
|
|
|
Logger.debug('activateCurrent');
|
|
|
|
function activateAccessible(aAccessible) {
|
|
|
|
if (aAccessible.actionCount > 0) {
|
|
|
|
aAccessible.doAction(0);
|
|
|
|
} else {
|
|
|
|
// XXX Some mobile widget sets do not expose actions properly
|
|
|
|
// (via ARIA roles, etc.), so we need to generate a click.
|
|
|
|
// Could possibly be made simpler in the future. Maybe core
|
|
|
|
// engine could expose nsCoreUtiles::DispatchMouseEvent()?
|
|
|
|
let docAcc = Utils.AccRetrieval.getAccessibleFor(content.document);
|
|
|
|
let docX = {}, docY = {}, docW = {}, docH = {};
|
|
|
|
docAcc.getBounds(docX, docY, docW, docH);
|
|
|
|
|
|
|
|
let objX = {}, objY = {}, objW = {}, objH = {};
|
|
|
|
aAccessible.getBounds(objX, objY, objW, objH);
|
|
|
|
|
|
|
|
let x = Math.round((objX.value - docX.value) + objW.value / 2);
|
|
|
|
let y = Math.round((objY.value - docY.value) + objH.value / 2);
|
|
|
|
|
2013-04-25 12:39:16 -07:00
|
|
|
let node = aAccessible.DOMNode || aAccessible.parent.DOMNode;
|
|
|
|
|
|
|
|
function dispatchMouseEvent(aEventType) {
|
2013-05-21 11:16:49 -07:00
|
|
|
let evt = content.document.createEvent('MouseEvents');
|
2013-04-25 12:39:16 -07:00
|
|
|
evt.initMouseEvent(aEventType, true, true, content,
|
|
|
|
x, y, 0, 0, 0, false, false, false, false, 0, null);
|
|
|
|
node.dispatchEvent(evt);
|
|
|
|
}
|
|
|
|
|
2013-05-21 11:16:49 -07:00
|
|
|
dispatchMouseEvent('mousedown');
|
|
|
|
dispatchMouseEvent('mouseup');
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let vc = Utils.getVirtualCursor(content.document);
|
|
|
|
if (!forwardMessage(vc, aMessage))
|
|
|
|
activateAccessible(vc.position);
|
|
|
|
}
|
|
|
|
|
|
|
|
function scroll(aMessage) {
|
|
|
|
let vc = Utils.getVirtualCursor(content.document);
|
|
|
|
|
|
|
|
function tryToScroll() {
|
|
|
|
let horiz = aMessage.json.horizontal;
|
|
|
|
let page = aMessage.json.page;
|
|
|
|
|
|
|
|
// Search up heirarchy for scrollable element.
|
|
|
|
let acc = vc.position;
|
|
|
|
while (acc) {
|
|
|
|
let elem = acc.DOMNode;
|
|
|
|
|
|
|
|
// We will do window scrolling next.
|
|
|
|
if (elem == content.document)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!horiz && elem.clientHeight < elem.scrollHeight) {
|
|
|
|
let s = content.getComputedStyle(elem);
|
|
|
|
if (s.overflowY == 'scroll' || s.overflowY == 'auto') {
|
|
|
|
elem.scrollTop += page * elem.clientHeight;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (horiz) {
|
|
|
|
if (elem.clientWidth < elem.scrollWidth) {
|
|
|
|
let s = content.getComputedStyle(elem);
|
|
|
|
if (s.overflowX == 'scroll' || s.overflowX == 'auto') {
|
|
|
|
elem.scrollLeft += page * elem.clientWidth;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let controllers = acc.
|
|
|
|
getRelationByType(
|
|
|
|
Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
|
|
|
|
for (let i = 0; controllers.targetsCount > i; i++) {
|
|
|
|
let controller = controllers.getTarget(i);
|
|
|
|
// If the section has a controlling slider, it should be considered
|
|
|
|
// the page-turner.
|
|
|
|
if (controller.role == Ci.nsIAccessibleRole.ROLE_SLIDER) {
|
|
|
|
// Sliders are controlled with ctrl+right/left. I just decided :)
|
|
|
|
let evt = content.document.createEvent('KeyboardEvent');
|
|
|
|
evt.initKeyEvent(
|
|
|
|
'keypress', true, true, null,
|
|
|
|
true, false, false, false,
|
|
|
|
(page > 0) ? evt.DOM_VK_RIGHT : evt.DOM_VK_LEFT, 0);
|
|
|
|
controller.DOMNode.dispatchEvent(evt);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
acc = acc.parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scroll window.
|
|
|
|
if (!horiz && content.scrollMaxY &&
|
|
|
|
((page > 0 && content.scrollY < content.scrollMaxY) ||
|
|
|
|
(page < 0 && content.scrollY > 0))) {
|
2012-12-05 12:54:37 -08:00
|
|
|
content.scroll(0, content.innerHeight * page + content.scrollY);
|
2012-10-01 13:33:26 -07:00
|
|
|
return true;
|
|
|
|
} else if (horiz && content.scrollMaxX &&
|
|
|
|
((page > 0 && content.scrollX < content.scrollMaxX) ||
|
|
|
|
(page < 0 && content.scrollX > 0))) {
|
2012-12-05 12:54:37 -08:00
|
|
|
content.scroll(content.innerWidth * page + content.scrollX);
|
2012-10-01 13:33:26 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aMessage.json.origin != 'child') {
|
|
|
|
if (forwardMessage(vc, aMessage))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!tryToScroll()) {
|
|
|
|
// Failed to scroll anything in this document. Try in parent document.
|
|
|
|
aMessage.json.origin = 'child';
|
|
|
|
sendAsyncMessage('AccessFu:Scroll', aMessage.json);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addMessageListener(
|
|
|
|
'AccessFu:Start',
|
|
|
|
function(m) {
|
2013-04-23 10:39:15 -07:00
|
|
|
Logger.debug('AccessFu:Start');
|
2012-10-01 13:33:26 -07:00
|
|
|
if (m.json.buildApp)
|
|
|
|
Utils.MozBuildApp = m.json.buildApp;
|
|
|
|
|
2013-04-23 10:39:15 -07:00
|
|
|
addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
|
|
|
|
addMessageListener('AccessFu:Activate', activateCurrent);
|
|
|
|
addMessageListener('AccessFu:Scroll', scroll);
|
|
|
|
|
2013-05-21 11:16:49 -07:00
|
|
|
if (!eventManager) {
|
|
|
|
eventManager = new EventManager(this);
|
|
|
|
}
|
|
|
|
eventManager.start();
|
2012-10-01 13:33:26 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
addMessageListener(
|
|
|
|
'AccessFu:Stop',
|
|
|
|
function(m) {
|
|
|
|
Logger.debug('AccessFu:Stop');
|
|
|
|
|
2013-04-23 10:39:15 -07:00
|
|
|
removeMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
|
|
|
|
removeMessageListener('AccessFu:Activate', activateCurrent);
|
|
|
|
removeMessageListener('AccessFu:Scroll', scroll);
|
|
|
|
|
2013-05-21 11:16:49 -07:00
|
|
|
eventManager.stop();
|
2012-10-01 13:33:26 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
sendAsyncMessage('AccessFu:Ready');
|