Bug 597286, part 6: MouseModule generates tap events instead of using clunky clickers [r=mbrubeck]

This commit is contained in:
Benjamin Stover 2010-09-22 15:08:18 -07:00
Родитель f10170e5ae
Коммит f2075d1692
2 изменённых файлов: 94 добавлений и 112 удалений

Просмотреть файл

@ -283,7 +283,7 @@ InputHandler.prototype = {
* to drag scrollable elements).
*
* The MouseModule grabs event focus of the input handler on mousedown, at which
* point it will attempt to find such custom draggers/clickers by walking up the
* point it will attempt to find such custom draggers by walking up the
* DOM tree from the event target. It ungrabs event focus on mouseup. It
* redispatches the swallowed mousedown, mouseup events back to chrome, so that
* chrome elements still get their events.
@ -312,21 +312,11 @@ InputHandler.prototype = {
* Between mousedown and mouseup, MouseModule incrementally drags and updates
* the dragger accordingly, and also determines whether a [double-]click occured
* (based on whether the input moves have moved outside of a certain drag disk
* centered at the mousedown position). If a [double-]click happened, any
* customClicker will be notified. The customClicker must support the following
* interface:
*
* singleClick(cx, cy, modifiers)
* Signals a single (as opposed to double) click occured at client
* coordinates cx, cy. Specify optional modifiers to include
* shift-keys with click.
*
* doubleClick(cx1, cy1, cx2, cy2)
* Signals a doubleclick occured, with the first click at client coordinates
* cx1, cy1, and second click at client coordinates cx2, cy2.
* centered at the mousedown position). Custom touch events are dispatched
* accordingly.
*
* There is a default dragger in case a scrollable element is dragged --- see
* the defaultDragger prototype property. There is no default clicker.
* the defaultDragger prototype property.
*/
function MouseModule(owner, browserViewContainer) {
this._owner = owner;
@ -334,7 +324,6 @@ function MouseModule(owner, browserViewContainer) {
this._dragData = new DragData(kTapRadius);
this._dragger = null;
this._clicker = null;
this._downUpEvents = [];
this._targetScrollInterface = null;
@ -399,15 +388,7 @@ MouseModule.prototype = {
this._cleanClickBuffer();
},
/**
* Handle a mousedown by stopping any lingering kinetic drag, walking DOM tree
* in search of a scrollable element (and its custom dragger if available) and
* a clicker, and initiating a drag if we have said scrollable element. The
* mousedown event is entirely swallowed but is saved for later redispatching,
* once we know right and proper what the input is trying to do to us.
*
* We grab() in here.
*/
/** Begin possible pan and send tap down event. */
_onMouseDown: function _onMouseDown(aEvent) {
this._owner.allowClicks();
@ -428,46 +409,31 @@ MouseModule.prototype = {
if (this._kinetic.isActive() && this._dragger != dragger)
this._kinetic.end();
let targetClicker = this.getClickerFromElement(aEvent.target);
this._targetScrollInterface = targetScrollInterface;
this._dragger = dragger;
this._clicker = (targetClicker) ? targetClicker.customClicker : null;
this._target = aEvent.target;
if (this._clicker)
this._clicker.mouseDown(aEvent.clientX, aEvent.clientY);
if (this._targetIsContent(aEvent)) {
let event = document.createEvent("Events");
event.initEvent("TapDown", true, false);
event.clientX = aEvent.clientX;
event.clientY = aEvent.clientY;
aEvent.target.dispatchEvent(event);
this._recordEvent(aEvent);
} else {
// cancel all pending content clicks
this._cleanClickBuffer();
}
if (this._dragger) {
let draggable = this._dragger.isDraggable(targetScrollbox, targetScrollInterface);
if (draggable.x || draggable.y)
this._doDragStart(aEvent);
}
if (this._targetIsContent(aEvent)) {
this._recordEvent(aEvent);
}
else {
// cancel all pending content clicks
this._cleanClickBuffer();
if (this._dragger) {
// do not allow axis locking if panning is only possible in one direction
let draggable = this._dragger.isDraggable(targetScrollbox, targetScrollInterface);
dragData.locked = !draggable.x || !draggable.y;
}
}
},
/**
* Handle a mouseup by swallowing the event (just as we did the mousedown) as
* well as the possible DOM click event that follows, making one last drag
* (which, do note, might just be the beginning of a kinetic drag that will
* linger long after we are gone), and recording the mousedown for later
* redispatching.
*
* We ungrab() in here.
*/
/** Send tap up event and any necessary full taps. */
_onMouseUp: function _onMouseUp(aEvent) {
let dragData = this._dragData;
let oldIsPan = dragData.isPan();
@ -478,16 +444,22 @@ MouseModule.prototype = {
}
if (this._targetIsContent(aEvent)) {
if (this._dragger) {
let event = document.createEvent("Events");
event.initEvent("TapUp", true, false);
event.clientX = aEvent.clientX
event.clientY = aEvent.clientY;
aEvent.target.dispatchEvent(event);
}
// User possibly clicked on something in content
this._recordEvent(aEvent);
let commitToClicker = this._clicker && dragData.isClick() && (this._downUpEvents.length > 1);
let commitToClicker = dragData.isClick() && (this._downUpEvents.length > 1);
if (commitToClicker)
// commit this click to the doubleclick timewait buffer
this._commitAnotherClick();
else
// clean the click buffer ourselves, since there was no clicker
// to commit to. when there is one, the path taken through
// _commitAnotherClick takes care of this.
// clean the click buffer ourselves
this._cleanClickBuffer();
}
else if (dragData.isPan()) {
@ -497,14 +469,13 @@ MouseModule.prototype = {
let generatesClick = aEvent.detail;
if (generatesClick)
this._owner.suppressNextClick();
}
let clicker = this._clicker;
if (clicker) {
// Let clicker know when mousemove begins a pan
if (!oldIsPan && dragData.isPan())
clicker.panBegin();
clicker.mouseUp(aEvent.clientX, aEvent.clientY);
// Pan has begun
if (!oldIsPan) {
let event = document.createEvent("Events");
event.initEvent("PanBegin", true, false);
document.dispatchEvent(event);
}
}
this._owner.ungrab(this);
@ -527,10 +498,12 @@ MouseModule.prototype = {
let [sX, sY] = dragData.panPosition();
this._doDragMove();
// Let clicker know when mousemove begins a pan
let clicker = this._clicker;
if (!oldIsPan && clicker)
clicker.panBegin();
// Let everyone know when mousemove begins a pan
if (!oldIsPan && dragData.isPan()) {
let event = document.createEvent("Events");
event.initEvent("PanBegin", true, false);
aEvent.target.dispatchEvent(event);
}
}
}
},
@ -616,9 +589,8 @@ MouseModule.prototype = {
/**
* Commit another click event to our click buffer. The `click buffer' is a
* timeout initiated by the first click. If the timeout is still alive when
* another click is committed, then the click buffer forms a double click, and
* the timeout is cancelled. Otherwise, the timeout issues a single click to
* the clicker.
* another click is committed, then the click buffer forms a double tap, and
* the timeout is cancelled. Otherwise, the timeout issues a single tap.
*/
_commitAnotherClick: function _commitAnotherClick() {
if (this._singleClickTimeout.isPending()) { // we're waiting for a second click for double
@ -629,9 +601,7 @@ MouseModule.prototype = {
}
},
/**
* Endpoint of _commitAnotherClick(). Finalize a single click and tell the clicker.
*/
/** Endpoint of _commitAnotherClick(). Finalize a single tap. */
_doSingleClick: function _doSingleClick() {
let ev = this._downUpEvents[1];
this._cleanClickBuffer();
@ -642,19 +612,29 @@ MouseModule.prototype = {
(ev.ctrlKey ? Ci.nsIDOMNSEvent.CONTROL_MASK : 0) |
(ev.shiftKey ? Ci.nsIDOMNSEvent.SHIFT_MASK : 0) |
(ev.metaKey ? Ci.nsIDOMNSEvent.META_MASK : 0);
this._clicker.singleClick(ev.clientX, ev.clientY, modifiers);
let event = document.createEvent("Events");
event.initEvent("TapSingle", true, false);
event.clientX = ev.clientX;
event.clientY = ev.clientY;
event.modifiers = modifiers;
ev.target.dispatchEvent(event);
},
/**
* Endpoint of _commitAnotherClick(). Finalize a double click and tell the clicker.
*/
/** Endpoint of _commitAnotherClick(). Finalize a double tap. */
_doDoubleClick: function _doDoubleClick() {
let mouseUp1 = this._downUpEvents[1];
// sometimes the second press event is not dispatched at all
let mouseUp2 = this._downUpEvents[Math.min(3, this._downUpEvents.length - 1)];
this._cleanClickBuffer();
this._clicker.doubleClick(mouseUp1.clientX, mouseUp1.clientY,
mouseUp2.clientX, mouseUp2.clientY);
let event = document.createEvent("Events");
event.initEvent("TapDouble", true, false);
event.clientX1 = mouseUp1.clientX;
event.clientY1 = mouseUp1.clientY;
event.clientX2 = mouseUp1.clientX;
event.clientY2 = mouseUp1.clientY;
mouseUp1.target.dispatchEvent(event);
},
/**
@ -759,23 +739,10 @@ MouseModule.prototype = {
return [scrollbox, qinterface, (scrollbox ? (scrollbox.customDragger || this._defaultDragger) : null)];
},
/**
* Walk up (parentward) the DOM tree from elem in search of an element with
* a customClicker. Return the element if found, null elsewise.
*/
getClickerFromElement: function getClickerFromElement(elem) {
for (; elem; elem = elem.parentNode)
if (elem.customClicker)
break;
return (elem) ? elem : null;
},
toString: function toString() {
return '[MouseModule] {'
+ '\n\tdragData=' + this._dragData + ', '
+ 'dragger=' + this._dragger + ', '
+ 'clicker=' + this._clicker + ', '
+ '\n\tdownUpEvents=' + this._downUpEvents + ', '
+ 'length=' + this._downUpEvents.length + ', '
+ '\n\ttargetScroller=' + this._targetScrollInterface + ', '
@ -1128,13 +1095,6 @@ function KeyModule(owner, browserViewContainer) {
}
KeyModule.prototype = {
getClickerFromElement: function getClickerFromElement(elem) {
for (; elem; elem = elem.parentNode)
if (elem.customKeySender)
break;
return (elem) ? elem : null;
},
handleEvent: function handleEvent(aEvent) {
if (aEvent.type == "keydown" || aEvent.type == "keyup" || aEvent.type == "keypress") {
let keyer = this._browserViewContainer.customKeySender;
@ -1289,10 +1249,6 @@ GestureModule.prototype = {
return;
}
// hide element highlight
// XXX ugh, this is awful. None of this code should be in InputHandler.
document.getElementById("inputhandler-overlay").customClicker.panBegin();
// create the AnimatedZoom object for fast arbitrary zooming
this._pinchZoom = AnimatedZoom;
this._pinchZoomRect = AnimatedZoom.getStartRect()

Просмотреть файл

@ -175,10 +175,11 @@ var Browser = {
/* handles dispatching clicks on browser into clicks in content or zooms */
let inputHandlerOverlay = document.getElementById("inputhandler-overlay");
inputHandlerOverlay.customClicker = new ContentCustomClicker();
inputHandlerOverlay.customKeySender = new ContentCustomKeySender();
inputHandlerOverlay.customDragger = new Browser.MainDragger();
ContentTouchHandler.init();
// Warning, total hack ahead. All of the real-browser related scrolling code
// lies in a pretend scrollbox here. Let's not land this as-is. Maybe it's time
// to redo all the dragging code.
@ -1262,10 +1263,35 @@ const BrowserSearch = {
/** Watches for mouse events in chrome and sends them to content. */
function ContentCustomClicker() {
}
const ContentTouchHandler = {
init: function init() {
document.addEventListener("TapDown", this, false);
document.addEventListener("TapUp", this, false);
document.addEventListener("TapSingle", this, false);
document.addEventListener("TapDouble", this, false);
document.addEventListener("PanBegin", this, false);
},
handleEvent: function handleEvent(ev) {
switch (ev.type) {
case "TapDown":
this.tapDown(ev.clientX, ev.clientY);
break;
case "TapUp":
thisTapSingletapUp(ev.clientX, ev.clientY);
break;
case "TapSingle":
this.tapSingle(ev.clientX, ev.clientY, ev.modifiers);
break;
case "TapDouble":
this.tapDouble(ev.clientX1, ev.clientY1, ev.clientX2, ev.clientY2);
break;
case "PanBegin":
this.panBegin();
break;
}
},
ContentCustomClicker.prototype = {
_dispatchMouseEvent: function _dispatchMouseEvent(aName, aX, aY, aModifiers) {
let aX = aX || 0;
let aY = aY || 0;
@ -1275,7 +1301,7 @@ ContentCustomClicker.prototype = {
browser.messageManager.sendAsyncMessage(aName, { x: x, y: y, modifiers: aModifiers });
},
mouseDown: function mouseDown(aX, aY) {
tapDown: function tapDown(aX, aY) {
// Ensure that the content process has gets an activate event
let browser = getBrowser();
let fl = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
@ -1287,7 +1313,7 @@ ContentCustomClicker.prototype = {
this._dispatchMouseEvent("Browser:MouseDown", aX, aY);
},
mouseUp: function mouseUp(aX, aY) {
tapUp: function tapUp(aX, aY) {
TapHighlightHelper.hide(200);
},
@ -1297,7 +1323,7 @@ ContentCustomClicker.prototype = {
this._dispatchMouseEvent("Browser:MouseCancel");
},
singleClick: function singleClick(aX, aY, aModifiers) {
tapSingle: function tapSingle(aX, aY, aModifiers) {
TapHighlightHelper.hide(200);
// Cancel the mouse click if we are showing a context menu
@ -1306,7 +1332,7 @@ ContentCustomClicker.prototype = {
this._dispatchMouseEvent("Browser:MouseCancel");
},
doubleClick: function doubleClick(aX1, aY1, aX2, aY2) {
tapDouble: function tapDouble(aX1, aY1, aX2, aY2) {
TapHighlightHelper.hide();
this._dispatchMouseEvent("Browser:MouseCancel");
@ -1322,7 +1348,7 @@ ContentCustomClicker.prototype = {
},
toString: function toString() {
return "[ContentCustomClicker] { }";
return "[ContentTouchHandler] { }";
}
};