mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
253 lines
8.0 KiB
JavaScript
253 lines
8.0 KiB
JavaScript
/* 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/. */
|
|
|
|
var Cc = Components.classes;
|
|
var Ci = Components.interfaces;
|
|
var Cu = Components.utils;
|
|
var Cr = Components.results;
|
|
|
|
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
|
Cu.import('resource://gre/modules/accessibility/EventManager.jsm');
|
|
Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
|
|
Cu.import('resource://gre/modules/Services.jsm');
|
|
|
|
Logger.debug('content-script.js');
|
|
|
|
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) {
|
|
let acc = Utils.AccRetrieval.
|
|
getAccessibleFor(content.document.activeElement);
|
|
moved = vc.moveNext(rule, acc, true);
|
|
}
|
|
break;
|
|
case 'moveToPoint':
|
|
moved = vc.moveToPoint(rule, details.x, details.y, true);
|
|
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';
|
|
// 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.
|
|
}
|
|
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);
|
|
|
|
let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
getInterface(Ci.nsIDOMWindowUtils);
|
|
cwu.sendMouseEventToWindow('mousedown', x, y, 0, 1, 0, false);
|
|
cwu.sendMouseEventToWindow('mouseup', x, y, 0, 1, 0, false);
|
|
}
|
|
}
|
|
|
|
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))) {
|
|
content.scroll(0, content.innerHeight);
|
|
return true;
|
|
} else if (horiz && content.scrollMaxX &&
|
|
((page > 0 && content.scrollX < content.scrollMaxX) ||
|
|
(page < 0 && content.scrollX > 0))) {
|
|
content.scroll(content.innerWidth, 0);
|
|
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:VirtualCursor', virtualCursorControl);
|
|
addMessageListener('AccessFu:Activate', activateCurrent);
|
|
addMessageListener('AccessFu:Scroll', scroll);
|
|
|
|
addMessageListener(
|
|
'AccessFu:Start',
|
|
function(m) {
|
|
if (m.json.buildApp)
|
|
Utils.MozBuildApp = m.json.buildApp;
|
|
|
|
EventManager.start(
|
|
function sendMessage(aName, aDetails) {
|
|
sendAsyncMessage(aName, aDetails);
|
|
});
|
|
|
|
docShell.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
getInterface(Ci.nsIWebProgress).
|
|
addProgressListener(EventManager,
|
|
(Ci.nsIWebProgress.NOTIFY_STATE_ALL |
|
|
Ci.nsIWebProgress.NOTIFY_LOCATION));
|
|
addEventListener('scroll', EventManager, true);
|
|
addEventListener('resize', EventManager, true);
|
|
// XXX: Ideally this would be an a11y event. Bug #742280.
|
|
addEventListener('DOMActivate', EventManager, true);
|
|
});
|
|
|
|
addMessageListener(
|
|
'AccessFu:Stop',
|
|
function(m) {
|
|
Logger.debug('AccessFu:Stop');
|
|
|
|
EventManager.stop();
|
|
|
|
docShell.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
getInterface(Ci.nsIWebProgress).
|
|
removeProgressListener(EventManager);
|
|
removeEventListener('scroll', EventManager, true);
|
|
removeEventListener('resize', EventManager, true);
|
|
// XXX: Ideally this would be an a11y event. Bug #742280.
|
|
removeEventListener('DOMActivate', EventManager, true);
|
|
});
|
|
|
|
sendAsyncMessage('AccessFu:Ready');
|