Bug 1016481 - Prevent dispatching a click when the touch-end event is cancelled. r=smaug r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Thu, 17 Jul 2014 22:24:23 -0400
changeset 216744 568b73c5bd31bfe3b597e67ae07feb4fa4c8d1f2
parent 216743 1709c5fdd182b00d8e4044dc9dc6738da261674f
child 216745 a561499de7a4a22429d3e82a0f4a259a4abce734
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, botond
bugs1016481
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1016481 - Prevent dispatching a click when the touch-end event is cancelled. r=smaug r=botond
dom/events/test/test_bug603008.html
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
layout/base/nsPresShell.cpp
--- a/dom/events/test/test_bug603008.html
+++ b/dom/events/test/test_bug603008.html
@@ -422,17 +422,17 @@ function testPreventDefault() {
      { name: "touchend", prevent: true }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false, doPrevent: true },
      { name: "touchend", prevent: false }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false },
-     { name: "touchend", prevent: false, doPrevent: true }]
+     { name: "touchend", prevent: true, doPrevent: true }]
   ];
 
   var dotest = function(aTest) {
     if (aTest.doPrevent) {
       target.addEventListener(aTest.name, preventFunction, false);
     }
 
     if (aTest.name == "touchmove") {
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -724,16 +724,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
+  , mTouchEndIsClick(Unknown)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mUniqueId(0)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
@@ -1799,16 +1800,20 @@ TabChild::RecvHandleDoubleTap(const CSSP
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
+  if (mTouchEndIsClick == IsNotClick) {
+    return true;
+  }
+
   LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
 
   MessageLoop::current()->PostDelayedTask(
     FROM_HERE,
     NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint),
     sActiveDurationMs);
   return true;
 }
@@ -1889,17 +1894,17 @@ TabChild::RecvNotifyAPZStateChange(const
   }
   case APZStateChange::StartPanning:
   {
     mActiveElementManager->HandlePanStart();
     break;
   }
   case APZStateChange::EndTouch:
   {
-    mActiveElementManager->HandleTouchEnd(aArg);
+    mTouchEndIsClick = (aArg ? IsClick : IsNotClick);
     break;
   }
   default:
     // APZStateChange has a 'sentinel' value, and the compiler complains
     // if an enumerator is not handled and there is no 'default' case.
     break;
   }
   return true;
@@ -2108,34 +2113,43 @@ TabChild::RecvRealTouchEvent(const Widge
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
+    mTouchEndIsClick = Unknown;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
       SendContentReceivedTouch(aGuid, isTouchPrevented);
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
     }
     break;
   }
 
-  case NS_TOUCH_MOVE:
   case NS_TOUCH_END:
-  case NS_TOUCH_CANCEL: {
+    if (isTouchPrevented && mTouchEndIsClick == IsClick) {
+      mTouchEndIsClick = IsNotClick;
+    }
+    // fall through
+  case NS_TOUCH_CANCEL:
+    if (mTouchEndIsClick != Unknown) {
+      mActiveElementManager->HandleTouchEnd(mTouchEndIsClick == IsClick);
+    }
+    // fall through
+  case NS_TOUCH_MOVE: {
     if (mPendingTouchPreventedResponse) {
       MOZ_ASSERT(aGuid == mPendingTouchPreventedGuid);
       SendContentReceivedTouch(mPendingTouchPreventedGuid, isTouchPrevented);
       mPendingTouchPreventedResponse = false;
     }
     break;
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -586,16 +586,23 @@ private:
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mPendingTouchPreventedResponse;
     ScrollableLayerGuid mPendingTouchPreventedGuid;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
+    enum ClickState {
+      Unknown,
+      IsClick,
+      IsNotClick
+    };
+    ClickState mTouchEndIsClick;
+
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
     uint64_t mUniqueId;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -7826,19 +7826,21 @@ nsIPresShell::DispatchGotOrLostPointerCa
 
 void
 PresShell::DispatchTouchEvent(WidgetEvent* aEvent,
                               nsEventStatus* aStatus,
                               nsPresShellEventCB* aEventCB,
                               bool aTouchIsNew)
 {
   // calling preventDefault on touchstart or the first touchmove for a
-  // point prevents mouse events
-  bool canPrevent = aEvent->message == NS_TOUCH_START ||
-              (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew);
+  // point prevents mouse events. calling it on the touchend should
+  // prevent click dispatching.
+  bool canPrevent = (aEvent->message == NS_TOUCH_START) ||
+              (aEvent->message == NS_TOUCH_MOVE && aTouchIsNew) ||
+              (aEvent->message == NS_TOUCH_END);
   bool preventDefault = false;
   nsEventStatus tmpStatus = nsEventStatus_eIgnore;
   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
 
   // loop over all touches and dispatch events on any that have changed
   for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) {
     dom::Touch* touch = touchEvent->touches[i];
     if (!touch || !touch->mChanged) {