mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-31 19:10:36 +00:00
Bug 814252 - use touch event for scrolling if available. r=cjones.
This commit is contained in:
parent
ad934e8b01
commit
551b1dede5
@ -4,7 +4,7 @@
|
||||
|
||||
const ContentPanning = {
|
||||
init: function cp_init() {
|
||||
['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
|
||||
['mousedown', 'mouseup', 'mousemove', 'touchstart', 'touchend', 'touchmove'].forEach(function(type) {
|
||||
addEventListener(type, ContentPanning, false);
|
||||
});
|
||||
|
||||
@ -12,15 +12,41 @@ const ContentPanning = {
|
||||
addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
|
||||
},
|
||||
|
||||
evtFilter: '',
|
||||
_filterEvent: function cp_filterEvent(evt) {
|
||||
switch (this.evtFilter) {
|
||||
case 'mouse':
|
||||
if (evt.type == 'touchstart' || evt.type == 'touchend' || evt.type == 'touchmove') {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'touch':
|
||||
if (evt.type == 'mousedown' || evt.type == 'mouseup' || evt.type == 'mousemove') {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
handleEvent: function cp_handleEvent(evt) {
|
||||
// determine scrolling detection is based on touch or mouse event at runtime
|
||||
if (!this.evtFilter) {
|
||||
if (evt.type == 'touchstart') this.evtFilter = 'touch';
|
||||
else if (evt.type == 'mousedown') this.evtFilter = 'mouse';
|
||||
}
|
||||
if (evt.defaultPrevented || !this._filterEvent(evt)) return;
|
||||
|
||||
switch (evt.type) {
|
||||
case 'mousedown':
|
||||
case 'touchstart':
|
||||
this.onTouchStart(evt);
|
||||
break;
|
||||
case 'mousemove':
|
||||
case 'touchmove':
|
||||
this.onTouchMove(evt);
|
||||
break;
|
||||
case 'mouseup':
|
||||
case 'touchend':
|
||||
this.onTouchEnd(evt);
|
||||
break;
|
||||
case 'click':
|
||||
@ -37,12 +63,39 @@ const ContentPanning = {
|
||||
|
||||
position: new Point(0 , 0),
|
||||
|
||||
findFirstTouch: function cp_findFirstTouch(touches) {
|
||||
if (!('trackingId' in this)) return undefined;
|
||||
|
||||
for (let i = 0; i < touches.length; i++) {
|
||||
if (touches[i].identifier === this.trackingId)
|
||||
return touches[i];
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
onTouchStart: function cp_onTouchStart(evt) {
|
||||
let target, screenX, screenY;
|
||||
if (this.evtFilter == 'touch') {
|
||||
if ('trackingId' in this) {
|
||||
return;
|
||||
}
|
||||
|
||||
let firstTouch = evt.changedTouches[0];
|
||||
this.trackingId = firstTouch.identifier;
|
||||
target = firstTouch.target;
|
||||
screenX = firstTouch.screenX;
|
||||
screenY = firstTouch.screenY;
|
||||
} else {
|
||||
target = evt.target;
|
||||
screenX = evt.screenX;
|
||||
screenY = evt.screenY;
|
||||
}
|
||||
|
||||
this.dragging = true;
|
||||
this.panning = false;
|
||||
|
||||
let oldTarget = this.target;
|
||||
[this.target, this.scrollCallback] = this.getPannable(evt.target);
|
||||
[this.target, this.scrollCallback] = this.getPannable(target);
|
||||
|
||||
// If we found a target, that means we have found a scrollable subframe. In
|
||||
// this case, and if we are using async panning and zooming on the parent
|
||||
@ -51,7 +104,7 @@ const ContentPanning = {
|
||||
// time we get a touch end).
|
||||
if (this.target != null && ContentPanning._asyncPanZoomForViewportFrame) {
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(docShell, 'cancel-default-pan-zoom', null);
|
||||
os.notifyObservers(docShell, 'detect-scrollable-subframe', null);
|
||||
}
|
||||
|
||||
// If there is a pan animation running (from a previous pan gesture) and
|
||||
@ -67,18 +120,24 @@ const ContentPanning = {
|
||||
}
|
||||
|
||||
|
||||
this.position.set(evt.screenX, evt.screenY);
|
||||
this.position.set(screenX, screenY);
|
||||
KineticPanning.record(new Point(0, 0), evt.timeStamp);
|
||||
},
|
||||
|
||||
onTouchEnd: function cp_onTouchEnd(evt) {
|
||||
if (this.evtFilter == 'touch' && !this.findFirstTouch(evt.changedTouches))
|
||||
return;
|
||||
|
||||
if (!this.dragging)
|
||||
return;
|
||||
this.dragging = false;
|
||||
this.isScrolling = false;
|
||||
|
||||
this.onTouchMove(evt);
|
||||
|
||||
let click = evt.detail;
|
||||
delete this.trackingId;
|
||||
|
||||
let click = (this.evtFilter == 'touch') ? true : evt.detail;
|
||||
if (this.target && click && (this.panning || this.preventNextClick)) {
|
||||
let target = this.target;
|
||||
let view = target.ownerDocument ? target.ownerDocument.defaultView
|
||||
@ -90,23 +149,54 @@ const ContentPanning = {
|
||||
KineticPanning.start(this);
|
||||
},
|
||||
|
||||
isScrolling: false, // Scrolling gesture is executed in BrowserElementScrolling
|
||||
onTouchMove: function cp_onTouchMove(evt) {
|
||||
if (!this.dragging || !this.scrollCallback)
|
||||
return;
|
||||
|
||||
let screenX, screenY;
|
||||
if (this.evtFilter == 'touch') {
|
||||
let firstTouch = this.findFirstTouch(evt.changedTouches);
|
||||
if (evt.touches.length > 1 || !firstTouch)
|
||||
return;
|
||||
screenX = firstTouch.screenX;
|
||||
screenY = firstTouch.screenY;
|
||||
} else {
|
||||
screenX = evt.screenX;
|
||||
screenY = evt.screenY;
|
||||
}
|
||||
|
||||
let current = this.position;
|
||||
let delta = new Point(evt.screenX - current.x, evt.screenY - current.y);
|
||||
current.set(evt.screenX, evt.screenY);
|
||||
let delta = new Point(screenX - current.x, screenY - current.y);
|
||||
current.set(screenX, screenY);
|
||||
|
||||
KineticPanning.record(delta, evt.timeStamp);
|
||||
this.scrollCallback(delta.scale(-1));
|
||||
let success = this.scrollCallback(delta.scale(-1));
|
||||
|
||||
// Stop async-pan-zooming if the subframe is really scrolled.
|
||||
if (!this.isScrolling && ContentPanning._asyncPanZoomForViewportFrame) {
|
||||
if (success) {
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.notifyObservers(docShell, 'cancel-default-pan-zoom', null);
|
||||
} else {
|
||||
// Let AsyncPanZoomController handle the scrolling gesture.
|
||||
delete this.trackingId;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Successfully scroll the inner scrollable region.
|
||||
if (success && !this.isScrolling) {
|
||||
this.isScrolling = true;
|
||||
}
|
||||
|
||||
// If a pan action happens, cancel the active state of the
|
||||
// current target.
|
||||
if (!this.panning && KineticPanning.isPan()) {
|
||||
this.panning = true;
|
||||
this._resetActive();
|
||||
}
|
||||
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
},
|
||||
|
@ -96,6 +96,7 @@ static const nsIntSize kDefaultViewportSize(980, 480);
|
||||
static const char CANCEL_DEFAULT_PAN_ZOOM[] = "cancel-default-pan-zoom";
|
||||
static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
|
||||
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
|
||||
static const char DETECT_SCROLLABLE_SUBFRAME[] = "detect-scrollable-subframe";
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContentListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
@ -241,6 +242,12 @@ TabChild::Observe(nsISupports *aSubject,
|
||||
HandlePossibleViewportChange();
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(aTopic, DETECT_SCROLLABLE_SUBFRAME)) {
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
|
||||
nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
|
||||
if (tabChild == this) {
|
||||
mRemoteFrame->DetectScrollableSubframe();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1676,6 +1683,7 @@ TabChild::RecvDestroy()
|
||||
observerService->RemoveObserver(this, CANCEL_DEFAULT_PAN_ZOOM);
|
||||
observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT);
|
||||
observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
|
||||
observerService->RemoveObserver(this, DETECT_SCROLLABLE_SUBFRAME);
|
||||
|
||||
const InfallibleTArray<PIndexedDBChild*>& idbActors =
|
||||
ManagedPIndexedDBChild();
|
||||
@ -1803,6 +1811,9 @@ TabChild::InitRenderingState()
|
||||
observerService->AddObserver(this,
|
||||
BEFORE_FIRST_PAINT,
|
||||
false);
|
||||
observerService->AddObserver(this,
|
||||
DETECT_SCROLLABLE_SUBFRAME,
|
||||
false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -237,6 +237,26 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent)
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (mDelayPanning && aEvent.mInputType == MULTITOUCH_INPUT) {
|
||||
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
|
||||
if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_MOVE) {
|
||||
// Let BrowserElementScrolling perform panning gesture first.
|
||||
SetState(WAITING_LISTENERS);
|
||||
mTouchQueue.AppendElement(multiTouchInput);
|
||||
|
||||
if (!mTouchListenerTimeoutTask) {
|
||||
mTouchListenerTimeoutTask =
|
||||
NewRunnableMethod(this, &AsyncPanZoomController::TimeoutTouchListeners);
|
||||
|
||||
MessageLoop::current()->PostDelayedTask(
|
||||
FROM_HERE,
|
||||
mTouchListenerTimeoutTask,
|
||||
TOUCH_LISTENER_TIMEOUT);
|
||||
}
|
||||
return nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aEvent.mInputType) {
|
||||
case MULTITOUCH_INPUT: {
|
||||
const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
|
||||
@ -1089,6 +1109,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
|
||||
|
||||
mLastContentPaintMetrics = aViewportFrame;
|
||||
|
||||
mFrameMetrics.mMayHaveTouchListeners = aViewportFrame.mMayHaveTouchListeners;
|
||||
if (mWaitingForContentToPaint) {
|
||||
// Remove the oldest sample we have if adding a new sample takes us over our
|
||||
// desired number of samples.
|
||||
@ -1182,6 +1203,10 @@ void AsyncPanZoomController::CancelDefaultPanZoom() {
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::DetectScrollableSubframe() {
|
||||
mDelayPanning = true;
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
|
||||
gfx::Rect zoomToRect(gfx::Rect(aRect.x, aRect.y, aRect.width, aRect.height));
|
||||
|
||||
@ -1268,7 +1293,7 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
|
||||
}
|
||||
|
||||
void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
|
||||
if (!mFrameMetrics.mMayHaveTouchListeners) {
|
||||
if (!mFrameMetrics.mMayHaveTouchListeners && !mDelayPanning) {
|
||||
mTouchQueue.Clear();
|
||||
return;
|
||||
}
|
||||
@ -1280,12 +1305,21 @@ void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
|
||||
|
||||
if (mState == WAITING_LISTENERS) {
|
||||
if (!aPreventDefault) {
|
||||
SetState(NOTHING);
|
||||
// Delayed scrolling gesture is pending at TOUCHING state.
|
||||
if (mDelayPanning) {
|
||||
SetState(TOUCHING);
|
||||
} else {
|
||||
SetState(NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
mHandlingTouchQueue = true;
|
||||
|
||||
while (!mTouchQueue.IsEmpty()) {
|
||||
// we need to reset mDelayPanning before handling scrolling gesture.
|
||||
if (mTouchQueue[0].mType == MultiTouchInput::MULTITOUCH_MOVE) {
|
||||
mDelayPanning = false;
|
||||
}
|
||||
if (!aPreventDefault) {
|
||||
HandleInputEvent(mTouchQueue[0]);
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public:
|
||||
void UpdateCompositionBounds(const nsIntRect& aCompositionBounds);
|
||||
|
||||
/**
|
||||
* We have found a scrollable subframe, so disable our machinery until we hit
|
||||
* We are scrolling a subframe, so disable our machinery until we hit
|
||||
* a touch end or a new touch start. This prevents us from accidentally
|
||||
* panning both the subframe and the parent frame.
|
||||
*
|
||||
@ -117,6 +117,12 @@ public:
|
||||
*/
|
||||
void CancelDefaultPanZoom();
|
||||
|
||||
/**
|
||||
* We have found a scrollable subframe, so we need to delay the scrolling
|
||||
* gesture executed and let subframe do the scrolling first.
|
||||
*/
|
||||
void DetectScrollableSubframe();
|
||||
|
||||
/**
|
||||
* Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
|
||||
* in. The actual animation is done on the compositor thread after being set
|
||||
@ -549,6 +555,12 @@ private:
|
||||
// and we don't want to queue the events back up again.
|
||||
bool mHandlingTouchQueue;
|
||||
|
||||
// Flag used to determine whether or not we should try scrolling by
|
||||
// BrowserElementScrolling first. If set, this means we should pend touch move
|
||||
// event, which not be cosumed by GestureListener. This flag will be reset
|
||||
// after touch move event has been handled by content process.
|
||||
bool mDelayPanning;
|
||||
|
||||
friend class Axis;
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,7 @@ parent:
|
||||
async NotifyCompositorTransaction();
|
||||
|
||||
async CancelDefaultPanZoom();
|
||||
async DetectScrollableSubframe();
|
||||
|
||||
async __delete__();
|
||||
|
||||
|
@ -38,6 +38,12 @@ RenderFrameChild::CancelDefaultPanZoom()
|
||||
SendCancelDefaultPanZoom();
|
||||
}
|
||||
|
||||
void
|
||||
RenderFrameChild::DetectScrollableSubframe()
|
||||
{
|
||||
SendDetectScrollableSubframe();
|
||||
}
|
||||
|
||||
PLayersChild*
|
||||
RenderFrameChild::AllocPLayers()
|
||||
{
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
virtual ~RenderFrameChild() {}
|
||||
|
||||
void CancelDefaultPanZoom();
|
||||
void DetectScrollableSubframe();
|
||||
|
||||
void Destroy();
|
||||
|
||||
|
@ -810,6 +810,15 @@ RenderFrameParent::RecvCancelDefaultPanZoom()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
RenderFrameParent::RecvDetectScrollableSubframe()
|
||||
{
|
||||
if (mPanZoomController) {
|
||||
mPanZoomController->DetectScrollableSubframe();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PLayersParent*
|
||||
RenderFrameParent::AllocPLayers()
|
||||
{
|
||||
|
@ -109,6 +109,7 @@ protected:
|
||||
virtual bool RecvNotifyCompositorTransaction() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvCancelDefaultPanZoom() MOZ_OVERRIDE;
|
||||
virtual bool RecvDetectScrollableSubframe() MOZ_OVERRIDE;
|
||||
|
||||
virtual PLayersParent* AllocPLayers() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPLayers(PLayersParent* aLayers) MOZ_OVERRIDE;
|
||||
|
Loading…
x
Reference in New Issue
Block a user