Bug 1525570 - Propagate preventDefault from first to subsequent touchstarts. r=botond
☠☠ backed out by 48b049ff10f1 ☠ ☠
authorKartikaya Gupta <kgupta@mozilla.com>
Fri, 01 Mar 2019 20:18:00 +0000
changeset 519874 77625f533af6a3628ad4203530dbe1b4526aceb6
parent 519873 cd326f5e4eb883c0daa8dd5022c99fc886dda9bf
child 519875 ee394d0b085d8c44884284246841b98f45c884dc
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1525570
milestone67.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 1525570 - Propagate preventDefault from first to subsequent touchstarts. r=botond Other mobile browsers disallow browser-based pinch zooming when the first touchstart is preventDefaulted, even if the second one is not. We allowed pinch zooming in that scenario. This patch makes it so that if the first touchstart is preventDefaulted, then subsequent touchstart events are also preventDefaulted, which brings our behaviour in line with that of other browsers. Differential Revision: https://phabricator.services.mozilla.com/D21420
gfx/layers/apz/util/APZEventState.cpp
gfx/layers/apz/util/APZEventState.h
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -99,16 +99,17 @@ APZEventState::APZEventState(nsIWidget* 
                              ContentReceivedInputBlockCallback&& aCallback)
     : mWidget(nullptr)  // initialized in constructor body
       ,
       mActiveElementManager(new ActiveElementManager()),
       mContentReceivedInputBlockCallback(std::move(aCallback)),
       mPendingTouchPreventedResponse(false),
       mPendingTouchPreventedBlockId(0),
       mEndTouchIsClick(false),
+      mFirstTouchCancelled(false),
       mTouchEndCancelled(false),
       mLastTouchIdentifier(0) {
   nsresult rv;
   mWidget = do_GetWeakReference(aWidget, &rv);
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "APZEventState constructed with a widget that"
              " does not support weak references. APZ will NOT work!");
 
@@ -332,16 +333,32 @@ void APZEventState::ProcessTouchEvent(co
       sentContentResponse = SendPendingTouchPreventedResponse(false);
       // sentContentResponse can be true here if we get two TOUCH_STARTs in a
       // row and just responded to the first one.
 
       // We're about to send a response back to APZ, but we should only do it
       // for events that went through APZ (which should be all of them).
       MOZ_ASSERT(aEvent.mFlags.mHandledByAPZ);
 
+      // If the first touchstart event was preventDefaulted, ensure that any
+      // subsequent additional touchstart events also get preventDefaulted. This
+      // ensures that e.g. pinch zooming is prevented even if just the first
+      // touchstart was prevented by content.
+      if (mTouchCounter.GetActiveTouchCount() == 0) {
+        mFirstTouchCancelled = isTouchPrevented;
+      } else {
+        if (mFirstTouchCancelled && !isTouchPrevented) {
+          APZES_LOG(
+              "Propagating prevent-default from first-touch for block %" PRIu64
+              "\n",
+              aInputBlockId);
+        }
+        isTouchPrevented |= mFirstTouchCancelled;
+      }
+
       if (isTouchPrevented) {
         mContentReceivedInputBlockCallback(aGuid, aInputBlockId,
                                            isTouchPrevented);
         sentContentResponse = true;
       } else {
         APZES_LOG("Event not prevented; pending response for %" PRIu64 " %s\n",
                   aInputBlockId, Stringify(aGuid).c_str());
         mPendingTouchPreventedResponse = true;
@@ -368,16 +385,21 @@ void APZEventState::ProcessTouchEvent(co
       break;
     }
 
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown touch event type");
       break;
   }
 
+  mTouchCounter.Update(aEvent);
+  if (mTouchCounter.GetActiveTouchCount() == 0) {
+    mFirstTouchCancelled = false;
+  }
+
   if (sentContentResponse && !isTouchPrevented &&
       aApzResponse == nsEventStatus_eConsumeDoDefault &&
       gfxPrefs::PointerEventsEnabled()) {
     WidgetTouchEvent cancelEvent(aEvent);
     cancelEvent.mMessage = eTouchPointerCancel;
     cancelEvent.mFlags.mCancelable = false;  // mMessage != eTouchCancel;
     for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
       if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {
--- a/gfx/layers/apz/util/APZEventState.h
+++ b/gfx/layers/apz/util/APZEventState.h
@@ -8,16 +8,17 @@
 #define mozilla_layers_APZEventState_h
 
 #include <stdint.h>
 
 #include "Units.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/layers/GeckoContentController.h"  // for APZStateChange
 #include "mozilla/layers/ScrollableLayerGuid.h"     // for ScrollableLayerGuid
+#include "mozilla/layers/TouchCounter.h"            // for TouchCounter
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"        // for NS_INLINE_DECL_REFCOUNTING
 #include "nsIWeakReferenceUtils.h"  // for nsWeakPtr
 
 #include <functional>
 
 template <class>
@@ -89,20 +90,22 @@ class APZEventState {
                              const nsCOMPtr<nsIWidget>& aWidget);
   already_AddRefed<nsIWidget> GetWidget() const;
   already_AddRefed<nsIContent> GetTouchRollup() const;
 
  private:
   nsWeakPtr mWidget;
   RefPtr<ActiveElementManager> mActiveElementManager;
   ContentReceivedInputBlockCallback mContentReceivedInputBlockCallback;
+  TouchCounter mTouchCounter;
   bool mPendingTouchPreventedResponse;
   ScrollableLayerGuid mPendingTouchPreventedGuid;
   uint64_t mPendingTouchPreventedBlockId;
   bool mEndTouchIsClick;
+  bool mFirstTouchCancelled;
   bool mTouchEndCancelled;
   int32_t mLastTouchIdentifier;
 
   // Because touch-triggered mouse events (e.g. mouse events from a tap
   // gesture) happen asynchronously from the touch events themselves, we
   // need to stash and replicate some of the state from the touch events
   // to the mouse events. One piece of state is the rollup content, which
   // is the content for which a popup window was recently closed. If we