Bug 862054 - Get rid of ElementTouchHelper and our custom elementFromPoint function. r=mbrubeck

This commit is contained in:
Jim Mathies 2013-04-23 08:51:02 -05:00
parent ab9e9a5d91
commit 11e1cd7dec

View File

@ -46,161 +46,6 @@ const kReferenceDpi = 240; // standard "pixel" size used in some preferences
const kStateActive = 0x00000001; // :active pseudoclass for elements
/*
* ElementTouchHelper
*
* Assists users by watching for mouse clicks in content and redirect
* them to the best found target.
*/
const ElementTouchHelper = {
get radius() {
let prefs = Services.prefs;
delete this.radius;
return this.radius = { "top": prefs.getIntPref("ui.touch.radius.topmm"),
"right": prefs.getIntPref("ui.touch.radius.rightmm"),
"bottom": prefs.getIntPref("ui.touch.radius.bottommm"),
"left": prefs.getIntPref("ui.touch.radius.leftmm")
};
},
get weight() {
delete this.weight;
return this.weight = { "visited": Services.prefs.getIntPref("ui.touch.radius.visitedWeight")
};
},
/* Retrieve the closest element to a point by looking at borders position */
getClosest: function getClosest(aWindowUtils, aX, aY) {
if (!this.dpiRatio)
this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi;
let dpiRatio = this.dpiRatio;
let target = aWindowUtils.elementFromPoint(aX, aY,
true, /* ignore root scroll frame*/
false); /* don't flush layout */
// return early if the click is just over a clickable element
if (this._isElementClickable(target))
return target;
let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio,
this.radius.right * dpiRatio,
this.radius.bottom * dpiRatio,
this.radius.left * dpiRatio, true, false);
let threshold = Number.POSITIVE_INFINITY;
for (let i = 0; i < nodes.length; i++) {
let current = nodes[i];
if (!current.mozMatchesSelector || !this._isElementClickable(current))
continue;
let rect = current.getBoundingClientRect();
let distance = this._computeDistanceFromRect(aX, aY, rect);
// increase a little bit the weight for already visited items
if (current && current.mozMatchesSelector("*:visited"))
distance *= (this.weight.visited / 100);
if (distance < threshold) {
target = current;
threshold = distance;
}
}
return target;
},
_isElementClickable: function _isElementClickable(aElement) {
const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label";
for (let elem = aElement; elem; elem = elem.parentNode) {
if (this._hasMouseListener(elem))
return true;
if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector))
return true;
}
return false;
},
_computeDistanceFromRect: function _computeDistanceFromRect(aX, aY, aRect) {
let x = 0, y = 0;
let xmost = aRect.left + aRect.width;
let ymost = aRect.top + aRect.height;
// compute horizontal distance from left/right border depending if X is
// before/inside/after the element's rectangle
if (aRect.left < aX && aX < xmost)
x = Math.min(xmost - aX, aX - aRect.left);
else if (aX < aRect.left)
x = aRect.left - aX;
else if (aX > xmost)
x = aX - xmost;
// compute vertical distance from top/bottom border depending if Y is
// above/inside/below the element's rectangle
if (aRect.top < aY && aY < ymost)
y = Math.min(ymost - aY, aY - aRect.top);
else if (aY < aRect.top)
y = aRect.top - aY;
if (aY > ymost)
y = aY - ymost;
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
},
_els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService),
_clickableEvents: ["mousedown", "mouseup", "click"],
_hasMouseListener: function _hasMouseListener(aElement) {
let els = this._els;
let listeners = els.getListenerInfoFor(aElement, {});
for (let i = 0; i < listeners.length; i++) {
if (this._clickableEvents.indexOf(listeners[i].type) != -1)
return true;
}
return false;
}
};
/*
* Global functions
*/
/*
* elementFromPoint - find the closes element at a point. searches
* sub-frames.
*
* @param aX, aY browser coordinates
* @return
* element - element at the position, or null if no active browser or
* element was found.
* frameX - x position within the subframe element was found. aX if no
* sub-frame was found.
* frameY - y position within the subframe element was found. aY if no
* sub-frame was found.
*/
function elementFromPoint(aX, aY) {
// browser's elementFromPoint expect browser-relative client coordinates.
// subtract browser's scroll values to adjust
let cwu = Util.getWindowUtils(content);
let elem = ElementTouchHelper.getClosest(cwu, aX, aY);
// step through layers of IFRAMEs and FRAMES to find innermost element
while (elem && (elem instanceof HTMLIFrameElement ||
elem instanceof HTMLFrameElement)) {
// adjust client coordinates' origin to be top left of iframe viewport
let rect = elem.getBoundingClientRect();
aX -= rect.left;
aY -= rect.top;
let windowUtils = elem.contentDocument
.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
elem = ElementTouchHelper.getClosest(windowUtils, aX, aY);
}
return { element: elem, frameX: aX, frameY: aY };
}
/*
* getBoundingContentRect
*
@ -349,8 +194,7 @@ let Content = {
break;
case "touchstart":
let touch = aEvent.changedTouches[0];
this._onTouchStart(touch.clientX, touch.clientY);
this._onTouchStart(aEvent);
break;
}
},
@ -403,10 +247,8 @@ let Content = {
* Event handlers
*/
_onTouchStart: function _onTouchStart(x, y) {
let { element } = elementFromPoint(x, y);
if (!element)
return;
_onTouchStart: function _onTouchStart(aEvent) {
let element = aEvent.target;
// There is no need to have a feedback for disabled element
let isDisabled = element instanceof HTMLOptionElement ?
@ -419,11 +261,9 @@ let Content = {
},
_onClickCapture: function _onClickCapture(aEvent) {
ContextMenuHandler.reset();
let element = aEvent.target;
let { element: element } = elementFromPoint(aEvent.clientX, aEvent.clientY);
if (!element)
return;
ContextMenuHandler.reset();
// Only show autocomplete after the item is clicked
if (!this.lastClickElement || this.lastClickElement != element) {