Bug 890940 - [AccessFu] Refactor coordinate handling to account for widget scaling. r=eeejay

--HG--
extra : rebase_source : 273f9171405c18e38bc9ec9e0d8d01f3d7d5fae4
This commit is contained in:
Max Li 2013-07-17 16:41:29 -04:00
parent f0b2dcf9d3
commit b9c7f12779
4 changed files with 77 additions and 53 deletions

View File

@ -356,7 +356,56 @@ this.AccessFu = {
// Keep track of message managers tha already have a 'content-script.js'
// injected.
_processedMessageManagers: []
_processedMessageManagers: [],
/**
* Adjusts the given bounds relative to the given browser. Converts from screen
* or device pixels to either device or CSS pixels.
* @param {Rect} aJsonBounds the bounds to adjust
* @param {browser} aBrowser the browser we want the bounds relative to
* @param {bool} aToCSSPixels whether to convert to CSS pixels (as opposed to
* device pixels)
* @param {bool} aFromDevicePixels whether to convert from device pixels (as
* opposed to screen pixels)
*/
adjustContentBounds: function(aJsonBounds, aBrowser, aToCSSPixels, aFromDevicePixels) {
let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top);
let win = Utils.win;
let dpr = win.devicePixelRatio;
let vp = Utils.getViewport(win);
let offset = { left: -win.mozInnerScreenX, top: -win.mozInnerScreenY };
if (!aBrowser.contentWindow) {
// OOP browser, add offset of browser.
// The offset of the browser element in relation to its parent window.
let clientRect = aBrowser.getBoundingClientRect();
let win = aBrowser.ownerDocument.defaultView;
offset.left += clientRect.left + win.mozInnerScreenX;
offset.top += clientRect.top + win.mozInnerScreenY;
}
// Here we scale from screen pixels to layout device pixels by dividing by
// the resolution (caused by pinch-zooming). The resolution is the viewport
// zoom divided by the devicePixelRatio. If there's no viewport, then we're
// on a platform without pinch-zooming and we can just ignore this.
if (!aFromDevicePixels && vp) {
bounds = bounds.scale(vp.zoom / dpr, vp.zoom / dpr);
}
// Add the offset; the offset is in CSS pixels, so multiply the
// devicePixelRatio back in before adding to preserve unit consistency.
bounds = bounds.translate(offset.left * dpr, offset.top * dpr);
// If we want to get to CSS pixels from device pixels, this needs to be
// further divided by the devicePixelRatio due to widget scaling.
if (aToCSSPixels) {
bounds = bounds.scale(1 / dpr, 1 / dpr);
}
return bounds.expandToIntegers();
}
};
var Output = {
@ -472,7 +521,7 @@ var Output = {
}
let padding = aDetails.padding;
let r = this._adjustBounds(aDetails.bounds, aBrowser);
let r = AccessFu.adjustContentBounds(aDetails.bounds, aBrowser, true);
// First hide it to avoid flickering when changing the style.
highlightBox.style.display = 'none';
@ -536,7 +585,7 @@ var Output = {
for each (let androidEvent in aDetails) {
androidEvent.type = 'Accessibility:Event';
if (androidEvent.bounds)
androidEvent.bounds = this._adjustBounds(androidEvent.bounds, aBrowser, true);
androidEvent.bounds = AccessFu.adjustContentBounds(androidEvent.bounds, aBrowser);
switch(androidEvent.eventType) {
case ANDROID_VIEW_TEXT_CHANGED:
@ -559,33 +608,6 @@ var Output = {
Braille: function Braille(aDetails, aBrowser) {
Logger.debug('Braille output: ' + aDetails.text);
},
_adjustBounds: function(aJsonBounds, aBrowser, aIncludeZoom) {
let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top);
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0, offsetY: 0 };
let root = Utils.win;
let offset = { left: -root.mozInnerScreenX, top: -root.mozInnerScreenY };
let scale = 1 / Utils.getPixelsPerCSSPixel(Utils.win);
if (!aBrowser.contentWindow) {
// OOP browser, add offset of browser.
// The offset of the browser element in relation to its parent window.
let clientRect = aBrowser.getBoundingClientRect();
let win = aBrowser.ownerDocument.defaultView;
offset.left += clientRect.left + win.mozInnerScreenX;
offset.top += clientRect.top + win.mozInnerScreenY;
}
let newBounds = bounds.scale(scale, scale).translate(offset.left, offset.top);
if (aIncludeZoom) {
newBounds = newBounds.scale(vp.zoom, vp.zoom);
}
return newBounds.expandToIntegers();
}
};
@ -615,9 +637,6 @@ var Input = {
this._handleKeypress(aEvent);
break;
case 'mozAccessFuGesture':
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0 };
aEvent.detail.x *= vp.zoom;
aEvent.detail.y *= vp.zoom;
this._handleGesture(aEvent.detail);
break;
}
@ -796,10 +815,10 @@ var Input = {
activateContextMenu: function activateContextMenu(aMessage) {
if (Utils.MozBuildApp === 'mobile/android') {
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0 };
let p = AccessFu.adjustContentBounds(aMessage.bounds, Utils.CurrentBrowser,
true, true).center();
Services.obs.notifyObservers(null, 'Gesture:LongPress',
JSON.stringify({x: aMessage.x / vp.zoom,
y: aMessage.y / vp.zoom}));
JSON.stringify({x: p.x, y: p.y}));
}
},

View File

@ -326,8 +326,8 @@ this.TouchAdapter = {
* of one single touch.
*/
function TouchPoint(aTouch, aTime, aDPI) {
this.startX = this.x = aTouch.screenX;
this.startY = this.y = aTouch.screenY;
this.startX = this.x = aTouch.screenX * this.scaleFactor;
this.startY = this.y = aTouch.screenY * this.scaleFactor;
this.startTime = aTime;
this.distanceTraveled = 0;
this.dpi = aDPI;
@ -338,8 +338,8 @@ TouchPoint.prototype = {
update: function TouchPoint_update(aTouch, aTime) {
let lastX = this.x;
let lastY = this.y;
this.x = aTouch.screenX;
this.y = aTouch.screenY;
this.x = aTouch.screenX * this.scaleFactor;
this.y = aTouch.screenY * this.scaleFactor;
this.time = aTime;
this.distanceTraveled += this.getDistanceToCoord(lastX, lastY);
@ -349,6 +349,20 @@ TouchPoint.prototype = {
return Math.sqrt(Math.pow(this.x - aX, 2) + Math.pow(this.y - aY, 2));
},
get scaleFactor() {
if (!this._scaleFactor) {
// Android events come with the x, y coordinates affected by the widget
// scaling; we restore it to normal here.
if (Utils.MozBuildApp == 'mobile/android') {
this._scaleFactor = Utils.win.devicePixelRatio;
} else {
this._scaleFactor = 1;
}
}
return this._scaleFactor;
},
finish: function TouchPoint_finish() {
this.done = true;
},

View File

@ -221,11 +221,6 @@ this.Utils = {
return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
},
getPixelsPerCSSPixel: function getPixelsPerCSSPixel(aWindow) {
return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).screenPixelsPerCSSPixel;
},
getBounds: function getBounds(aAccessible) {
let objX = {}, objY = {}, objW = {}, objH = {};
aAccessible.getBounds(objX, objY, objW, objH);
@ -684,4 +679,4 @@ PrefCache.prototype = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference])
};
};

View File

@ -97,10 +97,8 @@ function moveToPoint(aMessage) {
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);
let dpr = content.devicePixelRatio;
vc.moveToPoint(rule, details.x * dpr, details.y * dpr, true);
forwardToChild(aMessage, moveToPoint, vc.position);
} catch (x) {
Logger.logException(x, 'Failed move to point');
@ -219,9 +217,7 @@ function activateCurrent(aMessage) {
function activateContextMenu(aMessage) {
function sendContextMenuCoordinates(aAccessible) {
let bounds = Utils.getBounds(aAccessible);
sendAsyncMessage('AccessFu:ActivateContextMenu',
{ x: bounds.left + bounds.width / 2,
y: bounds.top + bounds.height / 2 });
sendAsyncMessage('AccessFu:ActivateContextMenu', {bounds: bounds});
}
let position = Utils.getVirtualCursor(content.document).position;