Bug 1030181 - Share code for dealing with WidgetTouchEvent and MultiTouchInput. r=drs r=smaug
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 25 Jun 2014 20:11:20 -0400
changeset 212757 6303520ccb8ffd4a91b2f0ef295e9c7c7440d869
parent 212756 989fb81974cddd9940590dfe795efea26f03d7b3
child 212758 1477d87d4f3ac01b27acb7ad5ddaf1ba882cfead
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)
reviewersdrs, smaug
bugs1030181
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 1030181 - Share code for dealing with WidgetTouchEvent and MultiTouchInput. r=drs r=smaug
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
widget/InputData.h
widget/xpwidgets/InputData.cpp
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -373,132 +373,18 @@ nsEventStatus
 APZCTreeManager::ReceiveInputEvent(const InputData& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid)
 {
   nsEventStatus result = nsEventStatus_eIgnore;
   gfx3DMatrix transformToApzc;
   gfx3DMatrix transformToGecko;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
-      const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
-      if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
-        // If we are in an overscrolled state and a second finger goes down,
-        // ignore that second touch point completely. The touch-start for it is
-        // dropped completely; subsequent touch events until the touch-end for it
-        // will have this touch point filtered out.
-        if (mApzcForInputBlock && mApzcForInputBlock->IsOverscrolled()) {
-          if (mRetainedTouchIdentifier == -1) {
-            mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
-          }
-          return nsEventStatus_eConsumeNoDefault;
-        }
-        // MULTITOUCH_START input contains all active touches of the current
-        // session thus resetting mTouchCount.
-        mTouchCount = multiTouchInput.mTouches.Length();
-        mInOverscrolledApzc = false;
-        nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint),
-                                                                          &mInOverscrolledApzc);
-        for (size_t i = 1; i < multiTouchInput.mTouches.Length(); i++) {
-          nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[i].mScreenPoint),
-                                                                 &mInOverscrolledApzc);
-          apzc = CommonAncestor(mApzcForInputBlock.get(), apzc2.get());
-          APZCTM_LOG("Using APZC %p as the common ancestor\n", mApzcForInputBlock.get());
-          // For now, we only ever want to do pinching on the root APZC for a given layers id. So
-          // when we find the common ancestor of multiple points, also walk up to the root APZC.
-          apzc = RootAPZCForLayersId(mApzcForInputBlock);
-          APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", mApzcForInputBlock.get());
-        }
-        if (apzc != mApzcForInputBlock) {
-          // If we're moving to a different APZC as our input target, then send a cancel event
-          // to the old one so that it clears its internal state. Otherwise it could get left
-          // in the middle of a panning touch block (for example) and not clean up properly.
-          if (mApzcForInputBlock) {
-            MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
-            mApzcForInputBlock->ReceiveInputEvent(cancel);
-          }
-          mApzcForInputBlock = apzc;
-        }
-
-        // Prepare for possible overscroll handoff.
-        BuildOverscrollHandoffChain(mApzcForInputBlock);
-
-        if (mApzcForInputBlock) {
-          // Cache transformToApzc so it can be used for future events in this block.
-          GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
-          mCachedTransformToApzcForInputBlock = transformToApzc;
-        } else {
-          // Reset the cached apz transform
-          mCachedTransformToApzcForInputBlock = gfx3DMatrix();
-        }
-      } else if (mApzcForInputBlock) {
-        APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
-      }
-
-      // If we receive a touch-cancel, it means all touches are finished, so we
-      // can stop ignoring any that we were ignoring.
-      if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
-        mRetainedTouchIdentifier = -1;
-      }
-
-      if (mApzcForInputBlock) {
-        mApzcForInputBlock->GetGuid(aOutTargetGuid);
-        // Use the cached transform to compute the point to send to the APZC.
-        // This ensures that the sequence of touch points an APZC sees in an
-        // input block are all in the same coordinate space.
-        transformToApzc = mCachedTransformToApzcForInputBlock;
-
-        // Make a copy of the input event that we pass, with some modifications,
-        // to the target APZC.
-        MultiTouchInput inputForApzc(multiTouchInput);
-
-        // If we are currently ignoring any touch points, filter them out from
-        // the set of touch points included in this event.
-        if (mRetainedTouchIdentifier != -1) {
-          for (size_t j = 0; j < inputForApzc.mTouches.Length(); ++j) {
-            if (inputForApzc.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
-              // TODO(botond): Once we get rid of ReceiveInputEvent(WidgetInputEvent),
-              // the signature of this function will change to take the InputData
-              // via non-const reference. We can then remove the touch point from
-              // multiTouchInput rather than the copy (inputForApzc), so that
-              // content doesn't get it either.
-              inputForApzc.mTouches.RemoveElementAt(j);
-              --j;
-            }
-          }
-          if (inputForApzc.mTouches.IsEmpty()) {
-            return nsEventStatus_eConsumeNoDefault;
-          }
-        }
-
-        for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
-          ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
-        }
-        mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
-      }
-      result = mInOverscrolledApzc ? nsEventStatus_eConsumeNoDefault
-             : mApzcForInputBlock ? nsEventStatus_eConsumeDoDefault
-             : nsEventStatus_eIgnore;
-      if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL ||
-          multiTouchInput.mType == MultiTouchInput::MULTITOUCH_END) {
-        if (mTouchCount >= multiTouchInput.mTouches.Length()) {
-          // MULTITOUCH_END input contains only released touches thus decrementing.
-          mTouchCount -= multiTouchInput.mTouches.Length();
-        } else {
-          NS_WARNING("Got an unexpected touchend/touchcancel");
-          mTouchCount = 0;
-        }
-        // If we have an mApzcForInputBlock and it's the end of the touch sequence
-        // then null it out so we don't keep a dangling reference and leak things.
-        if (mTouchCount == 0) {
-          mApzcForInputBlock = nullptr;
-          mInOverscrolledApzc = false;
-          mRetainedTouchIdentifier = -1;
-          ClearOverscrollHandoffChain();
-        }
-      }
+      MultiTouchInput touchInput = aEvent.AsMultiTouchInput();
+      result = ProcessTouchInput(touchInput, aOutTargetGuid);
       break;
     } case PANGESTURE_INPUT: {
       const PanGestureInput& panInput = aEvent.AsPanGestureInput();
       bool inOverscrolledApzc = false;
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
                                                             &inOverscrolledApzc);
       if (apzc) {
         if (panInput.mType == PanGestureInput::PANGESTURE_START ||
@@ -549,63 +435,62 @@ APZCTreeManager::ReceiveInputEvent(const
              : nsEventStatus_eIgnore;
       break;
     }
   }
   return result;
 }
 
 already_AddRefed<AsyncPanZoomController>
-APZCTreeManager::GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent,
+APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
                                         bool* aOutInOverscrolledApzc)
 {
-  ScreenPoint point = ScreenPoint(aEvent.touches[0]->mRefPoint.x, aEvent.touches[0]->mRefPoint.y);
-  // Ignore events if any touch hits inside an overscrolled apzc.
-  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(point, aOutInOverscrolledApzc);
-  for (size_t i = 1; i < aEvent.touches.Length(); i++) {
-    point = ScreenPoint(aEvent.touches[i]->mRefPoint.x, aEvent.touches[i]->mRefPoint.y);
-    nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(point, aOutInOverscrolledApzc);
+  nsRefPtr<AsyncPanZoomController> apzc;
+  if (aEvent.mTouches.Length() == 0) {
+    return apzc.forget();
+  }
+
+  apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, aOutInOverscrolledApzc);
+  for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
+    nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, aOutInOverscrolledApzc);
     apzc = CommonAncestor(apzc.get(), apzc2.get());
     APZCTM_LOG("Using APZC %p as the common ancestor\n", apzc.get());
     // For now, we only ever want to do pinching on the root APZC for a given layers id. So
     // when we find the common ancestor of multiple points, also walk up to the root APZC.
     apzc = RootAPZCForLayersId(apzc);
     APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
   }
+
   // Prepare for possible overscroll handoff.
   BuildOverscrollHandoffChain(apzc);
+
   return apzc.forget();
 }
 
 nsEventStatus
-APZCTreeManager::ProcessTouchEvent(WidgetTouchEvent& aEvent,
+APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
                                    ScrollableLayerGuid* aOutTargetGuid)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!aEvent.touches.Length()) {
-    return nsEventStatus_eIgnore;
-  }
-  if (aEvent.message == NS_TOUCH_START) {
+  if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
     // If we are in an overscrolled state and a second finger goes down,
     // ignore that second touch point completely. The touch-start for it is
     // dropped completely; subsequent touch events until the touch-end for it
     // will have this touch point filtered out.
     if (mApzcForInputBlock && mApzcForInputBlock->IsOverscrolled()) {
       if (mRetainedTouchIdentifier == -1) {
         mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
       }
       return nsEventStatus_eConsumeNoDefault;
     }
+
     // NS_TOUCH_START event contains all active touches of the current
     // session thus resetting mTouchCount.
-    mTouchCount = aEvent.touches.Length();
+    mTouchCount = aInput.mTouches.Length();
     mInOverscrolledApzc = false;
-
-    nsRefPtr<AsyncPanZoomController> apzc = GetTouchInputBlockAPZC(aEvent, &mInOverscrolledApzc);
+    nsRefPtr<AsyncPanZoomController> apzc = GetTouchInputBlockAPZC(aInput, &mInOverscrolledApzc);
     if (apzc != mApzcForInputBlock) {
       // If we're moving to a different APZC as our input target, then send a cancel event
       // to the old one so that it clears its internal state. Otherwise it could get left
       // in the middle of a panning touch block (for example) and not clean up properly.
       if (mApzcForInputBlock) {
         MultiTouchInput cancel(MultiTouchInput::MULTITOUCH_CANCEL, 0, TimeStamp::Now(), 0);
         mApzcForInputBlock->ReceiveInputEvent(cancel);
       }
@@ -615,83 +500,90 @@ APZCTreeManager::ProcessTouchEvent(Widge
     if (mApzcForInputBlock) {
       // Cache apz transform so it can be used for future events in this block.
       gfx3DMatrix transformToGecko;
       GetInputTransforms(mApzcForInputBlock, mCachedTransformToApzcForInputBlock, transformToGecko);
     } else {
       // Reset the cached apz transform
       mCachedTransformToApzcForInputBlock = gfx3DMatrix();
     }
+  } else if (mApzcForInputBlock) {
+    APZCTM_LOG("Re-using APZC %p as continuation of event block\n", mApzcForInputBlock.get());
   }
 
   // If we receive a touch-cancel, it means all touches are finished, so we
   // can stop ignoring any that we were ignoring.
-  if (aEvent.message == NS_TOUCH_CANCEL) {
+  if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
     mRetainedTouchIdentifier = -1;
   }
 
   // If we are currently ignoring any touch points, filter them out from the
-  // set of touch points included in this event. Note that we modify aEvent
+  // set of touch points included in this event. Note that we modify aInput
   // itself, so that the touch points are also filtered out when the caller
   // passes the event on to content.
   if (mRetainedTouchIdentifier != -1) {
-    for (size_t j = 0; j < aEvent.touches.Length(); ++j) {
-      if (aEvent.touches[j]->Identifier() != mRetainedTouchIdentifier) {
-        aEvent.touches.RemoveElementAt(j);
+    for (size_t j = 0; j < aInput.mTouches.Length(); ++j) {
+      if (aInput.mTouches[j].mIdentifier != mRetainedTouchIdentifier) {
+        aInput.mTouches.RemoveElementAt(j);
         --j;
       }
     }
-    if (aEvent.touches.IsEmpty()) {
+    if (aInput.mTouches.IsEmpty()) {
       return nsEventStatus_eConsumeNoDefault;
     }
   }
 
   if (mApzcForInputBlock) {
     mApzcForInputBlock->GetGuid(aOutTargetGuid);
     // For computing the input for the APZC, used the cached transform.
     // This ensures that the sequence of touch points an APZC sees in an
     // input block are all in the same coordinate space.
     gfx3DMatrix transformToApzc = mCachedTransformToApzcForInputBlock;
-    MultiTouchInput inputForApzc(aEvent);
+    MultiTouchInput inputForApzc(aInput);
     for (size_t i = 0; i < inputForApzc.mTouches.Length(); i++) {
       ApplyTransform(&(inputForApzc.mTouches[i].mScreenPoint), transformToApzc);
     }
     mApzcForInputBlock->ReceiveInputEvent(inputForApzc);
 
     // For computing the event to pass back to Gecko, use the up-to-date transforms.
     // This ensures that transformToApzc and transformToGecko are in sync
     // (note that transformToGecko isn't cached).
     gfx3DMatrix transformToGecko;
     GetInputTransforms(mApzcForInputBlock, transformToApzc, transformToGecko);
     gfx3DMatrix outTransform = transformToApzc * transformToGecko;
-    for (size_t i = 0; i < aEvent.touches.Length(); i++) {
-      ApplyTransform(&(aEvent.touches[i]->mRefPoint), outTransform);
+    for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
+      ApplyTransform(&(aInput.mTouches[i].mScreenPoint), outTransform);
     }
   }
+
   nsEventStatus result = mInOverscrolledApzc ? nsEventStatus_eConsumeNoDefault
                        : mApzcForInputBlock ? nsEventStatus_eConsumeDoDefault
                        : nsEventStatus_eIgnore;
-  // If we have an mApzcForInputBlock and it's the end of the touch sequence
-  // then null it out so we don't keep a dangling reference and leak things.
-  if (aEvent.message == NS_TOUCH_CANCEL ||
-      aEvent.message == NS_TOUCH_END) {
-    if (mTouchCount >= aEvent.touches.Length()) {
+
+  if (aInput.mType == MultiTouchInput::MULTITOUCH_END) {
+    if (mTouchCount >= aInput.mTouches.Length()) {
       // NS_TOUCH_END event contains only released touches thus decrementing.
-      mTouchCount -= aEvent.touches.Length();
+      mTouchCount -= aInput.mTouches.Length();
     } else {
       NS_WARNING("Got an unexpected touchend/touchcancel");
       mTouchCount = 0;
     }
-    if (mTouchCount == 0) {
-      mApzcForInputBlock = nullptr;
-      mInOverscrolledApzc = false;
-      mRetainedTouchIdentifier = -1;
-      ClearOverscrollHandoffChain();
-    }
+  } else if (aInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
+    mTouchCount = 0;
   }
+
+  // If it's the end of the touch sequence then clear out variables so we
+  // keep dangling references and leak things.
+  if (mTouchCount == 0) {
+    mApzcForInputBlock = nullptr;
+    mInOverscrolledApzc = false;
+    mRetainedTouchIdentifier = -1;
+    ClearOverscrollHandoffChain();
+  }
+
   return result;
 }
 
 void
 APZCTreeManager::TransformCoordinateToGecko(const ScreenIntPoint& aPoint,
                                             LayoutDeviceIntPoint* aOutTransformedPoint)
 {
   MOZ_ASSERT(aOutTransformedPoint);
@@ -730,22 +622,37 @@ APZCTreeManager::ProcessEvent(WidgetInpu
        : apzc ? nsEventStatus_eConsumeDoDefault
        : nsEventStatus_eIgnore;
 }
 
 nsEventStatus
 APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid)
 {
+  // This function will be removed as part of bug 930939.
+  // In general it is preferable to use the version of ReceiveInputEvent
+  // that takes an InputData, as that is usable from off-main-thread.
+
   MOZ_ASSERT(NS_IsMainThread());
 
   switch (aEvent.eventStructType) {
     case NS_TOUCH_EVENT: {
       WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent();
-      return ProcessTouchEvent(touchEvent, aOutTargetGuid);
+      MultiTouchInput touchInput(touchEvent);
+      nsEventStatus result = ProcessTouchInput(touchInput, aOutTargetGuid);
+      // touchInput was modified in-place to possibly remove some
+      // touch points (if we are overscrolled), and the coordinates were
+      // modified using the APZ untransform. We need to copy these changes
+      // back into the WidgetInputEvent.
+      touchEvent.touches.Clear();
+      touchEvent.touches.SetCapacity(touchInput.mTouches.Length());
+      for (size_t i = 0; i < touchInput.mTouches.Length(); i++) {
+        *touchEvent.touches.AppendElement() = touchInput.mTouches[i].ToNewDOMTouch();
+      }
+      return result;
     }
     default: {
       return ProcessEvent(aEvent, aOutTargetGuid);
     }
   }
 }
 
 void
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -20,16 +20,17 @@
 #include "mozilla/Vector.h"             // for mozilla::Vector
 #include "nsTArrayForwardDeclare.h"     // for nsTArray, nsTArray_Impl, etc
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 
 class gfx3DMatrix;
 
 namespace mozilla {
 class InputData;
+class MultiTouchInput;
 
 namespace layers {
 
 enum AllowedTouchBehavior {
   NONE =               0,
   VERTICAL_PAN =       1 << 0,
   HORIZONTAL_PAN =     1 << 1,
   PINCH_ZOOM =         1 << 2,
@@ -324,19 +325,19 @@ private:
   /* Helpers */
   AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, FrameMetrics::ViewID aScrollId);
   AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
   AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc,
                                          const gfxPoint& aHitTestPoint,
                                          bool* aOutInOverscrolledApzc);
   already_AddRefed<AsyncPanZoomController> CommonAncestor(AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2);
   already_AddRefed<AsyncPanZoomController> RootAPZCForLayersId(AsyncPanZoomController* aApzc);
-  already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const WidgetTouchEvent& aEvent,
+  already_AddRefed<AsyncPanZoomController> GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
                                                                   bool* aOutInOverscrolledApzc);
-  nsEventStatus ProcessTouchEvent(WidgetTouchEvent& touchEvent,
+  nsEventStatus ProcessTouchInput(MultiTouchInput& aInput,
                                   ScrollableLayerGuid* aOutTargetGuid);
   nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent,
                              ScrollableLayerGuid* aOutTargetGuid);
   void UpdateZoomConstraintsRecursively(AsyncPanZoomController* aApzc,
                                         const ZoomConstraints& aConstraints);
 
   /**
    * Recursive helper function to build the APZC tree. The tree of APZC instances has
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -8,18 +8,23 @@
 
 #include "nsDebug.h"
 #include "nsPoint.h"
 #include "nsTArray.h"
 #include "Units.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/TimeStamp.h"
 
+template<class E> struct already_AddRefed;
+
 namespace mozilla {
 
+namespace dom {
+class Touch;
+}
 
 enum InputType
 {
   MULTITOUCH_INPUT,
   PANGESTURE_INPUT,
   PINCHGESTURE_INPUT,
   TAPGESTURE_INPUT
 };
@@ -103,24 +108,24 @@ public:
                   float aRotationAngle,
                   float aForce)
     : mIdentifier(aIdentifier),
       mScreenPoint(aScreenPoint),
       mRadius(aRadius),
       mRotationAngle(aRotationAngle),
       mForce(aForce)
   {
-
-
   }
 
   SingleTouchData()
   {
   }
 
+  already_AddRefed<dom::Touch> ToNewDOMTouch();
+
   // A unique number assigned to each SingleTouchData within a MultiTouchInput so
   // that they can be easily distinguished when handling a touch start/move/end.
   int32_t mIdentifier;
 
   // Point on the screen that the touch hit, in device pixels. They are
   // coordinates on the screen.
   ScreenIntPoint mScreenPoint;
 
@@ -160,24 +165,30 @@ public:
     MULTITOUCH_CANCEL
   };
 
   MultiTouchInput(MultiTouchType aType, uint32_t aTime, TimeStamp aTimeStamp,
                   Modifiers aModifiers)
     : InputData(MULTITOUCH_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType)
   {
-
-
   }
 
   MultiTouchInput()
   {
   }
 
+  MultiTouchInput(const MultiTouchInput& aOther)
+    : InputData(MULTITOUCH_INPUT, aOther.mTime,
+                aOther.mTimeStamp, aOther.modifiers)
+    , mType(aOther.mType)
+  {
+    mTouches.AppendElements(aOther.mTouches);
+  }
+
   MultiTouchInput(const WidgetTouchEvent& aTouchEvent);
 
   // This conversion from WidgetMouseEvent to MultiTouchInput is needed because
   // on the B2G emulator we can only receive mouse events, but we need to be
   // able to pan correctly. To do this, we convert the events into a format that
   // the panning code can handle. This code is very limited and only supports
   // SingleTouchData. It also sends garbage for the identifier, radius, force
   // and rotation angle.
--- a/widget/xpwidgets/InputData.cpp
+++ b/widget/xpwidgets/InputData.cpp
@@ -10,16 +10,28 @@
 #include "nsThreadUtils.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TouchEvents.h"
 
 namespace mozilla {
 
 using namespace dom;
 
+already_AddRefed<Touch> SingleTouchData::ToNewDOMTouch()
+{
+  NS_ABORT_IF_FALSE(NS_IsMainThread(),
+                    "Can only create dom::Touch instances on main thread");
+  nsRefPtr<Touch> touch = new Touch(mIdentifier,
+                                    nsIntPoint(mScreenPoint.x, mScreenPoint.y),
+                                    nsIntPoint(mRadius.width, mRadius.height),
+                                    mRotationAngle,
+                                    mForce);
+  return touch.forget();
+}
+
 MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent)
   : InputData(MULTITOUCH_INPUT, aTouchEvent.time, aTouchEvent.timeStamp,
               aTouchEvent.modifiers)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(),
                     "Can only copy from WidgetTouchEvent on main thread");
 
   switch (aTouchEvent.message) {