Gecko work for bug 823619: Dispatch spec-compliant mouse events when touch events are available. r=cjones,jlebar,schien,vingtetun a=blocking-basecamp

This is a rollowup of the following patches
Bug 823619, part 1: Make TabChild dispatch spec-compliant compat mouse events. r=mwu
Bug 823619, part 2: Use touch event for scrolling if available. r=cjones,schien,vingtetun a=blocking-basecamp
This commit is contained in:
Chris Peterson ext:(%2C%20Shih-Chiang%20Chien%20%3Cschien%40mozilla.com%3E%20and%20Chris%20Jones%20%3Cjones.chris.g%40gmail.com%3E) 2013-01-05 15:53:16 +01:00
parent d714fbe57a
commit 56e0349b73
12 changed files with 419 additions and 53 deletions

View File

@ -283,10 +283,14 @@ pref("content.image.allow_locking", true);
pref("image.mem.min_discard_timeout_ms", 10000);
pref("image.mem.max_decoded_image_kb", 5120); /* 5MB */
// XXX this isn't a good check for "are touch events supported", but
// we don't really have a better one at the moment.
#ifdef MOZ_WIDGET_GONK
// enable touch events interfaces
pref("dom.w3c_touch_events.enabled", 1);
pref("dom.w3c_touch_events.safetyX", 0); // escape borders in units of 1/240"
pref("dom.w3c_touch_events.safetyY", 120); // escape borders in units of 1/240"
#endif
#ifdef MOZ_SAFE_BROWSING
// Safe browsing does nothing unless this pref is set

View File

@ -6,8 +6,35 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
const ContentPanning = {
// Are we listening to touch or mouse events?
watchedEventsType: '',
// Are mouse events being delivered to this content along with touch
// events, in violation of spec?
hybridEvents: false,
init: function cp_init() {
['mousedown', 'mouseup', 'mousemove'].forEach(function(type) {
var events;
try {
content.document.createEvent('TouchEvent');
events = ['touchstart', 'touchend', 'touchmove'];
this.watchedEventsType = 'touch';
#ifdef MOZ_WIDGET_GONK
// The gonk widget backend does not deliver mouse events per
// spec. Third-party content isn't exposed to this behavior,
// but that behavior creates some extra work for us here.
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
let isParentProcess =
!appInfo || appInfo.getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
this.hybridEvents = isParentProcess;
#endif
} catch(e) {
// Touch events aren't supported, so fall back on mouse.
events = ['mousedown', 'mouseup', 'mousemove'];
this.watchedEventsType = 'mouse';
}
events.forEach(function(type) {
addEventListener(type, ContentPanning, false);
});
@ -16,14 +43,20 @@ const ContentPanning = {
},
handleEvent: function cp_handleEvent(evt) {
if (evt.defaultPrevented)
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':
@ -40,21 +73,68 @@ const ContentPanning = {
position: new Point(0 , 0),
findPrimaryPointer: function cp_findPrimaryPointer(touches) {
if (!('primaryPointerId' in this))
return null;
for (let i = 0; i < touches.length; i++) {
if (touches[i].identifier === this.primaryPointerId) {
return touches[i];
}
}
return null;
},
onTouchStart: function cp_onTouchStart(evt) {
let screenX, screenY;
if (this.watchedEventsType == 'touch') {
if ('primaryPointerId' in this) {
return;
}
let firstTouch = evt.changedTouches[0];
this.primaryPointerId = firstTouch.identifier;
this.pointerDownTarget = firstTouch.target;
screenX = firstTouch.screenX;
screenY = firstTouch.screenY;
} else {
this.pointerDownTarget = 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(this.pointerDownTarget);
// 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
// frame, inform the pan/zoom controller that it should not attempt to
// handle any touch events it gets until the next batch (meaning the next
// time we get a touch end).
if (this.target != null && ContentPanning._asyncPanZoomForViewportFrame) {
if (this.target != null && this._asyncPanZoomForViewportFrame) {
this.detectingScrolling = true;
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 we have a pointer down target and we're not async
// pan/zooming, we may need to fill in for EventStateManager in
// setting the active state on the target element. Set a timer to
// ensure the pointer-down target is active. (If it's already
// active, the timer is a no-op.)
if (this.pointerDownTarget !== null && !this.detectingScrolling) {
// If there's no possibility this is a drag/pan, activate now.
// Otherwise wait a little bit to see if the gesture isn't a
// tap.
if (this.target === null) {
this.notify(this._activationTimer);
} else {
this._activationTimer.initWithCallback(this,
this._activationDelayMs,
Ci.nsITimer.TYPE_ONE_SHOT);
}
}
// If there is a pan animation running (from a previous pan gesture) and
@ -69,19 +149,38 @@ const ContentPanning = {
this.preventNextClick = true;
}
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.dragging)
let touch = null;
if (!this.dragging ||
(this.watchedEventsType == 'touch' &&
!(touch = this.findPrimaryPointer(evt.changedTouches)))) {
return;
this.dragging = false;
}
this.onTouchMove(evt);
// !isPan() and evt.detail should always give the same answer here
// since they use the same heuristics, but use the native gecko
// computation when possible.
//
// NB: when we're using touch events, then !KineticPanning.isPan()
// => this.panning, so we'll never attempt to block the click
// event. That's OK however, because we won't fire a synthetic
// click when we're using touch events and this touch series
// wasn't a "tap" gesture.
let click = (this.watchedEventsType == 'mouse') ?
evt.detail : !KineticPanning.isPan();
// Additionally, if we're seeing non-compliant hybrid events, a
// "real" click will be generated if we started and ended on the
// same element.
if (this.hybridEvents) {
let target =
content.document.elementFromPoint(touch.clientX, touch.clientY);
click |= (target === this.pointerDownTarget);
}
let click = evt.detail;
if (this.target && click && (this.panning || this.preventNextClick)) {
let target = this.target;
let view = target.ownerDocument ? target.ownerDocument.defaultView
@ -89,31 +188,84 @@ const ContentPanning = {
view.addEventListener('click', this, true, true);
}
if (this.panning)
this._resetActive();
this.dragging = false;
this.detectingScrolling = false;
delete this.primaryPointerId;
this._activationTimer.cancel();
if (this.panning) {
KineticPanning.start(this);
}
},
// True when there's an async pan-zoom controll watching the
// outermost scrollable frame, and we're waiting to see whether
// we're going to take over from it and synchronously scroll an
// inner scrollable frame.
detectingScrolling: false,
onTouchMove: function cp_onTouchMove(evt) {
if (!this.dragging || !this.scrollCallback)
if (!this.dragging)
return;
let screenX, screenY;
if (this.watchedEventsType == 'touch') {
let primaryTouch = this.findPrimaryPointer(evt.changedTouches);
if (evt.touches.length > 1 || !primaryTouch)
return;
screenX = primaryTouch.screenX;
screenY = primaryTouch.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);
// There's no possibility of us panning anything.
if (!this.scrollCallback) {
return;
}
let isPan = KineticPanning.isPan();
if (this.detectingScrolling) {
this.detectingScrolling = false;
// Stop async-pan-zooming if the user is panning the subframe.
if (isPan) {
// We're going to drive synchronously scrolling an inner frame.
Services.obs.notifyObservers(docShell, 'cancel-default-pan-zoom', null);
} else {
// Let AsyncPanZoomController handle the scrolling gesture.
this.scrollCallback = null;
return;
}
}
this.scrollCallback(delta.scale(-1));
// If a pan action happens, cancel the active state of the
// current target.
if (!this.panning && KineticPanning.isPan()) {
if (!this.panning && isPan) {
this.panning = true;
this._resetActive();
this._activationTimer.cancel();
}
if (this.panning) {
evt.stopPropagation();
evt.preventDefault();
}
evt.stopPropagation();
evt.preventDefault();
},
// nsITimerCallback
notify: function cp_notify(timer) {
this._setActive(this.pointerDownTarget);
},
onKineticBegin: function cp_onKineticBegin(evt) {
},
@ -249,11 +401,27 @@ const ContentPanning = {
.getService(Ci.inIDOMUtils);
},
_resetActive: function cp_resetActive() {
let root = this.target.ownerDocument || this.target.document;
get _activationTimer() {
delete this._activationTimer;
return this._activationTimer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
},
get _activationDelayMs() {
let delay = Services.prefs.getIntPref('ui.touch_activation.delay_ms');
delete this._activationDelayMs;
return this._activationDelayMs = delay;
},
_resetActive: function cp_resetActive() {
let elt = this.target || this.pointerDownTarget;
let root = elt.ownerDocument || elt.document;
this._setActive(root.documentElement);
},
_setActive: function cp_setActive(elt) {
const kStateActive = 0x00000001;
this._domUtils.setContentState(root.documentElement, kStateActive);
this._domUtils.setContentState(elt, kStateActive);
},
get _asyncPanZoomForViewportFrame() {

View File

@ -97,6 +97,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)
@ -162,6 +163,8 @@ TabChild::TabChild(const TabContext& aContext, uint32_t aChromeFlags)
, mChromeFlags(aChromeFlags)
, mOuterRect(0, 0, 0, 0)
, mInnerSize(0, 0)
, mActivePointerId(-1)
, mTapHoldTimer(nullptr)
, mOldViewportWidth(0.0f)
, mLastBackgroundColor(NS_RGB(255, 255, 255))
, mDidFakeShow(false)
@ -243,6 +246,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;
@ -1349,42 +1358,135 @@ TabChild::RecvMouseWheelEvent(const WheelEvent& event)
}
void
TabChild::DispatchSynthesizedMouseEvent(const nsTouchEvent& aEvent)
TabChild::DispatchSynthesizedMouseEvent(uint32_t aMsg, uint64_t aTime,
const nsIntPoint& aRefPoint)
{
// Synthesize a phony mouse event.
uint32_t msg;
switch (aEvent.message) {
case NS_TOUCH_START:
msg = NS_MOUSE_BUTTON_DOWN;
break;
case NS_TOUCH_MOVE:
msg = NS_MOUSE_MOVE;
break;
case NS_TOUCH_END:
case NS_TOUCH_CANCEL:
msg = NS_MOUSE_BUTTON_UP;
break;
default:
MOZ_NOT_REACHED("Unknown touch event message");
}
MOZ_ASSERT(aMsg == NS_MOUSE_MOVE || aMsg == NS_MOUSE_BUTTON_DOWN ||
aMsg == NS_MOUSE_BUTTON_UP);
nsIntPoint refPoint(0, 0);
if (aEvent.touches.Length()) {
refPoint = aEvent.touches[0]->mRefPoint;
}
nsMouseEvent event(true, msg, NULL,
nsMouseEvent event(true, aMsg, NULL,
nsMouseEvent::eReal, nsMouseEvent::eNormal);
event.refPoint = refPoint;
event.time = aEvent.time;
event.refPoint = aRefPoint;
event.time = aTime;
event.button = nsMouseEvent::eLeftButton;
if (msg != NS_MOUSE_MOVE) {
if (aMsg != NS_MOUSE_MOVE) {
event.clickCount = 1;
}
DispatchWidgetEvent(event);
}
static nsDOMTouch*
GetTouchForIdentifier(const nsTouchEvent& aEvent, int32_t aId)
{
for (uint32_t i = 0; i < aEvent.touches.Length(); ++i) {
nsDOMTouch* touch = static_cast<nsDOMTouch*>(aEvent.touches[i].get());
if (touch->mIdentifier == aId) {
return touch;
}
}
return nullptr;
}
void
TabChild::UpdateTapState(const nsTouchEvent& aEvent, nsEventStatus aStatus)
{
static bool sHavePrefs;
static bool sClickHoldContextMenusEnabled;
static nsIntSize sDragThreshold;
static int32_t sContextMenuDelayMs;
if (!sHavePrefs) {
sHavePrefs = true;
Preferences::AddBoolVarCache(&sClickHoldContextMenusEnabled,
"ui.click_hold_context_menus", true);
Preferences::AddIntVarCache(&sDragThreshold.width,
"ui.dragThresholdX", 25);
Preferences::AddIntVarCache(&sDragThreshold.height,
"ui.dragThresholdY", 25);
Preferences::AddIntVarCache(&sContextMenuDelayMs,
"ui.click_hold_context_menus.delay", 500);
}
bool currentlyTrackingTouch = (mActivePointerId >= 0);
if (aEvent.message == NS_TOUCH_START) {
if (currentlyTrackingTouch || aEvent.touches.Length() > 1) {
// We're tracking a possible tap for another point, or we saw a
// touchstart for a later pointer after we canceled tracking of
// the first point. Ignore this one.
return;
}
if (aStatus == nsEventStatus_eConsumeNoDefault ||
nsIPresShell::gPreventMouseEvents) {
return;
}
nsDOMTouch* touch = static_cast<nsDOMTouch*>(aEvent.touches[0].get());
mGestureDownPoint = touch->mRefPoint;
mActivePointerId = touch->mIdentifier;
if (sClickHoldContextMenusEnabled) {
MOZ_ASSERT(!mTapHoldTimer);
mTapHoldTimer = NewRunnableMethod(this,
&TabChild::FireContextMenuEvent);
MessageLoop::current()->PostDelayedTask(FROM_HERE, mTapHoldTimer,
sContextMenuDelayMs);
}
return;
}
// If we're not tracking a touch or this event doesn't include the
// one we care about, bail.
if (!currentlyTrackingTouch) {
return;
}
nsDOMTouch* trackedTouch = GetTouchForIdentifier(aEvent, mActivePointerId);
if (!trackedTouch) {
return;
}
nsIntPoint currentPoint = trackedTouch->mRefPoint;
int64_t time = aEvent.time;
switch (aEvent.message) {
case NS_TOUCH_MOVE:
if (abs(currentPoint.x - mGestureDownPoint.x) > sDragThreshold.width ||
abs(currentPoint.y - mGestureDownPoint.y) > sDragThreshold.height) {
CancelTapTracking();
}
return;
case NS_TOUCH_END:
if (!nsIPresShell::gPreventMouseEvents) {
DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, currentPoint);
DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, currentPoint);
DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, currentPoint);
}
// fall through
case NS_TOUCH_CANCEL:
CancelTapTracking();
return;
default:
NS_WARNING("Unknown touch event type");
}
}
void
TabChild::FireContextMenuEvent()
{
MOZ_ASSERT(mTapHoldTimer && mActivePointerId >= 0);
RecvHandleLongTap(mGestureDownPoint);
CancelTapTracking();
}
void
TabChild::CancelTapTracking()
{
mActivePointerId = -1;
if (mTapHoldTimer) {
mTapHoldTimer->Cancel();
}
mTapHoldTimer = nullptr;
}
bool
TabChild::RecvRealTouchEvent(const nsTouchEvent& aEvent)
{
@ -1398,8 +1500,8 @@ TabChild::RecvRealTouchEvent(const nsTouchEvent& aEvent)
if (innerWindow && innerWindow->HasTouchEventListeners()) {
SendContentReceivedTouch(nsIPresShell::gPreventMouseEvents);
}
} else if (status != nsEventStatus_eConsumeNoDefault) {
DispatchSynthesizedMouseEvent(aEvent);
} else {
UpdateTapState(aEvent, status);
}
return true;
@ -1680,6 +1782,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();
@ -1815,6 +1918,9 @@ TabChild::InitRenderingState()
observerService->AddObserver(this,
BEFORE_FIRST_PAINT,
false);
observerService->AddObserver(this,
DETECT_SCROLLABLE_SUBFRAME,
false);
}
return true;

View File

@ -384,8 +384,18 @@ private:
void DispatchMessageManagerMessage(const nsAString& aMessageName,
const nsACString& aJSONData);
// Sends a simulated mouse event from a touch event for compatibility.
void DispatchSynthesizedMouseEvent(const nsTouchEvent& aEvent);
void DispatchSynthesizedMouseEvent(uint32_t aMsg, uint64_t aTime,
const nsIntPoint& aRefPoint);
// These methods are used for tracking synthetic mouse events
// dispatched for compatibility. On each touch event, we
// UpdateTapState(). If we've detected that the current gesture
// isn't a tap, then we CancelTapTracking(). In the meantime, we
// may detect a context-menu event, and if so we
// FireContextMenuEvent().
void FireContextMenuEvent();
void CancelTapTracking();
void UpdateTapState(const nsTouchEvent& aEvent, nsEventStatus aStatus);
nsresult
BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
@ -411,6 +421,15 @@ private:
uint32_t mChromeFlags;
nsIntRect mOuterRect;
nsIntSize mInnerSize;
// When we're tracking a possible tap gesture, this is the "down"
// point of the touchstart.
nsIntPoint mGestureDownPoint;
// The touch identifier of the active gesture.
int32_t mActivePointerId;
// A timer task that fires if the tap-hold timeout is exceeded by
// the touch we're tracking. That is, if touchend or a touchmove
// that exceeds the gesture threshold doesn't happen.
CancelableTask* mTapHoldTimer;
float mOldViewportWidth;
nscolor mLastBackgroundColor;
ScrollingBehavior mScrolling;

View File

@ -247,6 +247,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();
@ -1130,6 +1150,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.
@ -1223,6 +1244,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));
@ -1309,7 +1334,7 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
}
void AsyncPanZoomController::ContentReceivedTouch(bool aPreventDefault) {
if (!mFrameMetrics.mMayHaveTouchListeners) {
if (!mFrameMetrics.mMayHaveTouchListeners && !mDelayPanning) {
mTouchQueue.Clear();
return;
}
@ -1321,12 +1346,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]);
}

View File

@ -109,7 +109,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.
*
@ -118,6 +118,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
@ -585,6 +591,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, we delay delivering
// touchmove events to GestureListener until BrowserElementScrolling
// decides whether it wants to handle panning for this touch series.
bool mDelayPanning;
friend class Axis;
};

View File

@ -42,6 +42,7 @@ parent:
async NotifyCompositorTransaction();
async CancelDefaultPanZoom();
async DetectScrollableSubframe();
async __delete__();

View File

@ -38,6 +38,12 @@ RenderFrameChild::CancelDefaultPanZoom()
SendCancelDefaultPanZoom();
}
void
RenderFrameChild::DetectScrollableSubframe()
{
SendDetectScrollableSubframe();
}
PLayersChild*
RenderFrameChild::AllocPLayers()
{

View File

@ -20,6 +20,7 @@ public:
virtual ~RenderFrameChild() {}
void CancelDefaultPanZoom();
void DetectScrollableSubframe();
void Destroy();

View File

@ -829,6 +829,15 @@ RenderFrameParent::RecvCancelDefaultPanZoom()
return true;
}
bool
RenderFrameParent::RecvDetectScrollableSubframe()
{
if (mPanZoomController) {
mPanZoomController->DetectScrollableSubframe();
}
return true;
}
PLayersParent*
RenderFrameParent::AllocPLayers()
{

View File

@ -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;

View File

@ -3927,3 +3927,8 @@ pref("dom.placeholder.show_on_focus", true);
pref("wap.UAProf.url", "");
pref("wap.UAProf.tagname", "x-wap-profile");
// If the user puts a finger down on an element and we think the user
// might be executing a pan gesture, how long do we wait before
// tentatively deciding the gesture is actually a tap and activating
// the target element?
pref("ui.touch_activation.delay_ms", 50);