зеркало из https://github.com/mozilla/gecko-dev.git
Bug 544614 - Touch Events [r=mfinkle, bstover]
This commit is contained in:
Родитель
df4a6c7a1b
Коммит
1c4760b2d3
|
@ -584,6 +584,9 @@ pref("image.mem.decodeondraw", true);
|
|||
pref("content.image.allow_locking", false);
|
||||
pref("image.mem.min_discard_timeout_ms", 20000);
|
||||
|
||||
// enable touch events interfaces
|
||||
pref("dom.w3c_touch_events.enabled", true);
|
||||
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
// Safe browsing does nothing unless this pref is set
|
||||
pref("browser.safebrowsing.enabled", true);
|
||||
|
@ -634,3 +637,4 @@ pref("urlclassifier.updatecachemax", 4194304);
|
|||
// URL for checking the reason for a malware warning.
|
||||
pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1197,6 +1197,10 @@ Browser.MainDragger = function MainDragger() {
|
|||
|
||||
Elements.browsers.addEventListener("PanBegin", this, false);
|
||||
Elements.browsers.addEventListener("PanFinished", this, false);
|
||||
|
||||
// allow pages to to override panning, but should
|
||||
// still allow the sidebars to be panned out of view
|
||||
this.contentMouseCapture = false;
|
||||
};
|
||||
|
||||
Browser.MainDragger.prototype = {
|
||||
|
@ -1227,48 +1231,42 @@ Browser.MainDragger.prototype = {
|
|||
dragMove: function dragMove(dx, dy, scroller, aIsKinetic) {
|
||||
let doffset = new Point(dx, dy);
|
||||
|
||||
// First calculate any panning to take sidebars out of view
|
||||
let panOffset = this._panControlsAwayOffset(doffset);
|
||||
// If the sidebars are showing, we pan them out of the way before panning the content.
|
||||
// The panning distance that should be used for the sidebars in is stored in sidebarOffset,
|
||||
// and subtracted from doffset
|
||||
let sidebarOffset = this._getSidebarOffset(doffset);
|
||||
|
||||
// If we started at one sidebar, stop when we get to the other.
|
||||
if (panOffset.x != 0 && !this._stopAtSidebar) {
|
||||
this._stopAtSidebar = panOffset.x; // negative: stop at left; positive: stop at right
|
||||
// If we started with one sidebar open, stop when we get to the other.
|
||||
if (sidebarOffset.x != 0)
|
||||
this._blockSidebars(sidebarOffset);
|
||||
|
||||
if (!this.contentMouseCapture)
|
||||
this._panContent(doffset);
|
||||
|
||||
if (this._hitSidebar && aIsKinetic)
|
||||
return false; // No kinetic panning after we've stopped at the sidebar.
|
||||
|
||||
// allow panning the sidebars if the page hasn't prevented it, or if any of the sidebars are showing
|
||||
// (i.e. we always allow panning sidebars off screen but not necessarily panning them back on)
|
||||
if (!this.contentMouseCapture || sidebarOffset.x != 0 || sidebarOffset.y > 0)
|
||||
this._panChrome(doffset, sidebarOffset);
|
||||
|
||||
this._updateScrollbars();
|
||||
|
||||
return !doffset.equals(dx, dy);
|
||||
},
|
||||
|
||||
_blockSidebars: function md_blockSidebars(aSidebarOffset) {
|
||||
// only call this code once
|
||||
if (!this._stopAtSidebar) {
|
||||
this._stopAtSidebar = aSidebarOffset.x; // negative: stop at left; positive: stop at right
|
||||
|
||||
// after a timeout, we allow showing the sidebar, to give the appearance of some "friction" at the edge
|
||||
this._sidebarTimeout = setTimeout(function(self) {
|
||||
self._stopAtSidebar = 0;
|
||||
self._sidebarTimeout = null;
|
||||
}, 350, this);
|
||||
}
|
||||
|
||||
if (this._contentView && !this._contentView.isRoot()) {
|
||||
this._panContentView(this._contentView, doffset);
|
||||
// XXX we may need to have "escape borders" for iframe panning
|
||||
// XXX does not deal with scrollables within scrollables
|
||||
}
|
||||
|
||||
// Do content panning
|
||||
this._panContentView(getBrowser().getRootView(), doffset);
|
||||
|
||||
if (this._hitSidebar && aIsKinetic)
|
||||
return; // No kinetic panning after we've stopped at the sidebar.
|
||||
|
||||
// Any leftover panning in doffset would bring controls into view. Add to sidebar
|
||||
// away panning for the total scroll offset.
|
||||
let offsetX = doffset.x;
|
||||
if ((this._stopAtSidebar > 0 && offsetX > 0) ||
|
||||
(this._stopAtSidebar < 0 && offsetX < 0)) {
|
||||
if (offsetX != panOffset.x)
|
||||
this._hitSidebar = true;
|
||||
doffset.x = panOffset.x;
|
||||
} else {
|
||||
doffset.add(panOffset);
|
||||
}
|
||||
|
||||
Browser.tryFloatToolbar(doffset.x, 0);
|
||||
this._panScroller(Browser.controlsScrollboxScroller, doffset);
|
||||
this._panScroller(Browser.pageScrollboxScroller, doffset);
|
||||
this._updateScrollbars();
|
||||
|
||||
return !doffset.equals(dx, dy);
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
|
@ -1299,8 +1297,38 @@ Browser.MainDragger.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_panContent: function md_panContent(aOffset) {
|
||||
if (this._contentView && !this._contentView.isRoot()) {
|
||||
this._panContentView(this._contentView, aOffset);
|
||||
// XXX we may need to have "escape borders" for iframe panning
|
||||
// XXX does not deal with scrollables within scrollables
|
||||
}
|
||||
// Do content panning
|
||||
this._panContentView(getBrowser().getRootView(), aOffset);
|
||||
},
|
||||
|
||||
_panChrome: function md_panSidebars(aOffset, aSidebarOffset) {
|
||||
// Any panning aOffset would bring controls into view. Add to aSidebarOffset
|
||||
let offsetX = aOffset.x;
|
||||
if ((this._stopAtSidebar > 0 && offsetX > 0) ||
|
||||
(this._stopAtSidebar < 0 && offsetX < 0)) {
|
||||
if (offsetX != aSidebarOffset.x)
|
||||
this._hitSidebar = true;
|
||||
aOffset.x = aSidebarOffset.x;
|
||||
} else {
|
||||
aOffset.add(aSidebarOffset);
|
||||
}
|
||||
|
||||
Browser.tryFloatToolbar(aOffset.x, 0);
|
||||
|
||||
// pan the sidebars
|
||||
this._panScroller(Browser.controlsScrollboxScroller, aOffset);
|
||||
// pan the urlbar
|
||||
this._panScroller(Browser.pageScrollboxScroller, aOffset);
|
||||
},
|
||||
|
||||
/** Return offset that pans controls away from screen. Updates doffset with leftovers. */
|
||||
_panControlsAwayOffset: function(doffset) {
|
||||
_getSidebarOffset: function(doffset) {
|
||||
let x = 0, y = 0, rect;
|
||||
|
||||
rect = Rect.fromRect(Browser.pageScrollbox.getBoundingClientRect()).map(Math.round);
|
||||
|
@ -1603,6 +1631,7 @@ const ContentTouchHandler = {
|
|||
document.addEventListener("TapSingle", this, false);
|
||||
document.addEventListener("TapDouble", this, false);
|
||||
document.addEventListener("TapLong", this, false);
|
||||
document.addEventListener("TapMove", this, false);
|
||||
|
||||
document.addEventListener("PanBegin", this, false);
|
||||
document.addEventListener("PopupChanged", this, false);
|
||||
|
@ -1619,8 +1648,8 @@ const ContentTouchHandler = {
|
|||
// a long tap, without waiting for child process.
|
||||
//
|
||||
messageManager.addMessageListener("Browser:ContextMenu", this);
|
||||
|
||||
messageManager.addMessageListener("Browser:Highlight", this);
|
||||
messageManager.addMessageListener("Browser:CaptureEvents", this);
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
|
@ -1661,12 +1690,12 @@ const ContentTouchHandler = {
|
|||
this.tapSingle(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
} else {
|
||||
this.tapUp(aEvent.clientX, aEvent.clientY);
|
||||
}
|
||||
this._dispatchMouseEvent("Browser:MouseUp", aEvent.clientX, aEvent.clientY);
|
||||
break;
|
||||
case "TapSingle":
|
||||
this.tapSingle(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
|
||||
this._dispatchMouseEvent("Browser:MouseUp", aEvent.clientX, aEvent.clientY);
|
||||
break;
|
||||
case "TapDouble":
|
||||
this.tapDouble(aEvent.clientX, aEvent.clientY, aEvent.modifiers);
|
||||
|
@ -1674,6 +1703,9 @@ const ContentTouchHandler = {
|
|||
case "TapLong":
|
||||
this.tapLong(aEvent.clientX, aEvent.clientY);
|
||||
break;
|
||||
case "TapMove":
|
||||
this.tapMove(aEvent.clientX, aEvent.clientY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1694,6 +1726,9 @@ const ContentTouchHandler = {
|
|||
document.dispatchEvent(event);
|
||||
}
|
||||
break;
|
||||
case "Browser:CaptureEvents":
|
||||
Elements.browsers.customDragger.contentMouseCapture = aMessage.json.panning;
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1735,6 +1770,7 @@ const ContentTouchHandler = {
|
|||
try {
|
||||
fl.activateRemoteFrame();
|
||||
} catch (e) {}
|
||||
Elements.browsers.customDragger.contentMouseCapture = false;
|
||||
this._dispatchMouseEvent("Browser:MouseDown", aX, aY);
|
||||
},
|
||||
|
||||
|
@ -1750,7 +1786,11 @@ const ContentTouchHandler = {
|
|||
tapSingle: function tapSingle(aX, aY, aModifiers) {
|
||||
// Cancel the mouse click if we are showing a context menu
|
||||
if (!ContextHelper.popupState)
|
||||
this._dispatchMouseEvent("Browser:MouseUp", aX, aY, { modifiers: aModifiers });
|
||||
this._dispatchMouseEvent("Browser:MouseClick", aX, aY, { modifiers: aModifiers });
|
||||
},
|
||||
|
||||
tapMove: function tapMove(aX, aY) {
|
||||
this._dispatchMouseEvent("Browser:MouseMove", aX, aY);
|
||||
},
|
||||
|
||||
tapDouble: function tapDouble(aX, aY, aModifiers) {
|
||||
|
|
|
@ -262,7 +262,7 @@ let Content = {
|
|||
addMessageListener("Browser:MouseOver", this);
|
||||
addMessageListener("Browser:MouseLong", this);
|
||||
addMessageListener("Browser:MouseDown", this);
|
||||
addMessageListener("Browser:MouseUp", this);
|
||||
addMessageListener("Browser:MouseClick", this);
|
||||
addMessageListener("Browser:MouseCancel", this);
|
||||
addMessageListener("Browser:SaveAs", this);
|
||||
addMessageListener("Browser:ZoomToPoint", this);
|
||||
|
@ -474,15 +474,17 @@ let Content = {
|
|||
|
||||
ContextHandler.messageId = json.messageId;
|
||||
|
||||
let event = content.document.createEvent("PopupEvents");
|
||||
event.initEvent("contextmenu", true, true);
|
||||
let event = content.document.createEvent("MouseEvent");
|
||||
event.initMouseEvent("contextmenu", true, true, content,
|
||||
0, x, y, x, y, false, false, false, false,
|
||||
0, null);
|
||||
event.x = x;
|
||||
event.y = y;
|
||||
element.dispatchEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Browser:MouseUp": {
|
||||
case "Browser:MouseClick": {
|
||||
this._formAssistant.focusSync = true;
|
||||
let element = elementFromPoint(x, y);
|
||||
if (modifiers == Ci.nsIDOMNSEvent.CONTROL_MASK) {
|
||||
|
@ -1188,3 +1190,63 @@ var ConsoleAPIObserver = {
|
|||
};
|
||||
|
||||
ConsoleAPIObserver.init();
|
||||
|
||||
var TouchEventHandler = {
|
||||
element: null,
|
||||
init: function() {
|
||||
addMessageListener("Browser:MouseUp", this);
|
||||
addMessageListener("Browser:MouseDown", this);
|
||||
addMessageListener("Browser:MouseMove", this);
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
if (Util.isParentProcess())
|
||||
return;
|
||||
|
||||
let json = aMessage.json;
|
||||
let cancelled = false;
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "Browser:MouseDown":
|
||||
let cwu = Util.getWindowUtils(content);
|
||||
this.element = cwu.elementFromPoint(json.x, json.y, false, false);
|
||||
cancelled = !this.sendEvent("touchstart", json, this.element);
|
||||
break;
|
||||
|
||||
case "Browser:MouseUp":
|
||||
if (this.element)
|
||||
this.sendEvent("touchend", json, this.element);
|
||||
this.element = null;
|
||||
break;
|
||||
|
||||
case "Browser:MouseMove":
|
||||
if (this.element)
|
||||
cancelled = !this.sendEvent("touchmove", json, this.element);
|
||||
break;
|
||||
}
|
||||
|
||||
if (cancelled)
|
||||
sendAsyncMessage("Browser:CaptureEvents", { messageId: json.messageId,
|
||||
panning: true });
|
||||
},
|
||||
|
||||
sendEvent: function(aName, aData, aElement) {
|
||||
if (!Services.prefs.getBoolPref("dom.w3c_touch_events.enabled"))
|
||||
return true;
|
||||
|
||||
let evt = content.document.createEvent("touchevent");
|
||||
let point = content.document.createTouch(content, aElement, 0,
|
||||
aData.x, aData.y, aData.x, aData.y, aData.x, aData.y,
|
||||
1, 1, 0, 0);
|
||||
let touches = content.document.createTouchList(point);
|
||||
if (aName == "touchend") {
|
||||
let empty = content.document.createTouchList();
|
||||
evt.initTouchEvent(aName, true, true, content, 0, true, true, true, true, empty, empty, touches);
|
||||
} else {
|
||||
evt.initTouchEvent(aName, true, true, content, 0, true, true, true, true, touches, touches, touches);
|
||||
}
|
||||
return aElement.dispatchEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
TouchEventHandler.init();
|
||||
|
|
|
@ -335,6 +335,7 @@ MouseModule.prototype = {
|
|||
this.dY += dragData.prevPanY - sY;
|
||||
|
||||
if (dragData.isPan()) {
|
||||
this.sendMove(aEvent.clientX, aEvent.clientY, aEvent.target);
|
||||
// Only pan when mouse event isn't part of a click. Prevent jittering on tap.
|
||||
this._kinetic.addData(sX - dragData.prevPanX, sY - dragData.prevPanY);
|
||||
this._dragBy(this.dX, this.dY);
|
||||
|
@ -360,6 +361,14 @@ MouseModule.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
sendMove: function(aX, aY, aTarget) {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TapMove", true, true);
|
||||
event.clientX = aX;
|
||||
event.clientY = aY;
|
||||
aTarget.dispatchEvent(event);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inform our dragger of a dragStart.
|
||||
*/
|
||||
|
@ -1246,4 +1255,3 @@ GestureModule.prototype = {
|
|||
return r0.translate(offsetX, offsetY);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче