Bug 1120683 - Properly handle unapplying 3D projective transforms throughout APZ code. r=botond
authorKevin Wern <kevin.m.wern@gmail.com>
Fri, 03 Jul 2015 15:06:26 -0400
changeset 251411 2fd3c5132792f7e2261a024d61ef24fa3e57dfdb
parent 251410 0642a5722c595e9515166252fc7fb17c036b4673
child 251412 aaebaa5cf6d2d3da78789457eef92cdc16214a7b
push id28996
push userphilringnalda@gmail.com
push dateSat, 04 Jul 2015 18:07:47 +0000
treeherdermozilla-central@5fe7988b5632 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1120683
milestone42.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 1120683 - Properly handle unapplying 3D projective transforms throughout APZ code. r=botond Whenever the inverse of a 3D projective transform is applied to a point, only use the result if it has a positive w-coordinate. When transforming by a matrix that we know should be 2D, assert to that effect. Transformations of rectangles (as opposed to points) remain to be audited.
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/HitTestingTreeNode.cpp
layout/base/UnitTransforms.h
widget/InputData.cpp
widget/InputData.h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -600,16 +600,17 @@ APZCTreeManager::ReceiveInputEvent(Input
         // scrolling, which changes the async transform. However, the event we
         // want to pass to gecko should be the pre-scroll event coordinates,
         // transformed into the gecko space. (pre-scroll because the mouse
         // cursor is stationary during wheel scrolling, unlike touchmove
         // events). Since we just flushed the pending repaints the transform to
         // gecko space should only consist of overscroll-cancelling transforms.
         Matrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
                                    * GetApzcToGeckoTransform(apzc);
+        MOZ_ASSERT(transformToGecko.Is2D());
         ScreenPoint untransformedOrigin = TransformTo<ScreenPixel>(
           transformToGecko, wheelInput.mOrigin);
 
         result = mInputQueue->ReceiveInputEvent(
           apzc,
           /* aTargetConfirmed = */ hitResult == HitLayer,
           wheelInput, aOutInputBlockId);
 
@@ -629,16 +630,17 @@ APZCTreeManager::ReceiveInputEvent(Input
             apzc,
             /* aTargetConfirmed = */ hitResult == HitLayer,
             panInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         Matrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
                                    * GetApzcToGeckoTransform(apzc);
+        MOZ_ASSERT(transformToGecko.Is2D());
         panInput.mPanStartPoint = TransformTo<ScreenPixel>(
             transformToGecko, panInput.mPanStartPoint);
         panInput.mPanDisplacement = TransformVector<ScreenPixel>(
             transformToGecko, panInput.mPanDisplacement, panInput.mPanStartPoint);
       }
       break;
     } case PINCHGESTURE_INPUT: {  // note: no one currently sends these
       PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
@@ -651,16 +653,17 @@ APZCTreeManager::ReceiveInputEvent(Input
             apzc,
             /* aTargetConfirmed = */ hitResult == HitLayer,
             pinchInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         Matrix4x4 outTransform = GetScreenToApzcTransform(apzc)
                                * GetApzcToGeckoTransform(apzc);
+        MOZ_ASSERT(outTransform.Is2D());
         pinchInput.mFocusPoint = TransformTo<ScreenPixel>(
             outTransform, pinchInput.mFocusPoint);
       }
       break;
     } case TAPGESTURE_INPUT: {  // note: no one currently sends these
       TapGestureInput& tapInput = aEvent.AsTapGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(tapInput.mPoint,
                                                             &hitResult);
@@ -671,16 +674,17 @@ APZCTreeManager::ReceiveInputEvent(Input
             apzc,
             /* aTargetConfirmed = */ hitResult == HitLayer,
             tapInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         Matrix4x4 outTransform = GetScreenToApzcTransform(apzc)
                                * GetApzcToGeckoTransform(apzc);
+        MOZ_ASSERT(outTransform.Is2D());
         tapInput.mPoint = TransformTo<ScreenPixel>(outTransform, tapInput.mPoint);
       }
       break;
     }
   }
   return result;
 }
 
@@ -780,16 +784,18 @@ APZCTreeManager::ProcessTouchInput(Multi
         aInput, aOutInputBlockId);
 
     // For computing the event to pass back to Gecko, use up-to-date transforms
     // (i.e. not anything cached in an input block).
     // This ensures that transformToApzc and transformToGecko are in sync.
     Matrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
     Matrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
     Matrix4x4 outTransform = transformToApzc * transformToGecko;
+    MOZ_ASSERT(outTransform.Is2D());
+    
     for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
       SingleTouchData& touchData = aInput.mTouches[i];
       touchData.mScreenPoint = TransformTo<ScreenPixel>(
           outTransform, touchData.mScreenPoint);
     }
   }
 
   if (aInput.mType == MultiTouchInput::MULTITOUCH_END) {
@@ -820,16 +826,17 @@ APZCTreeManager::TransformCoordinateToGe
                                             LayoutDeviceIntPoint* aOutTransformedPoint)
 {
   MOZ_ASSERT(aOutTransformedPoint);
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aPoint, nullptr);
   if (apzc && aOutTransformedPoint) {
     Matrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
     Matrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
     Matrix4x4 outTransform = transformToApzc * transformToGecko;
+    MOZ_ASSERT(outTransform.Is2D());
     *aOutTransformedPoint = TransformTo<LayoutDevicePixel>(outTransform, aPoint);
   }
 }
 
 void
 APZCTreeManager::UpdateWheelTransaction(WidgetInputEvent& aEvent)
 {
   WheelBlockState* txn = mInputQueue->GetCurrentWheelTransaction();
@@ -887,16 +894,17 @@ APZCTreeManager::ProcessEvent(WidgetInpu
   nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(ScreenPoint(aEvent.refPoint.x, aEvent.refPoint.y),
                                                         &hitResult);
   if (apzc) {
     MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion);
     apzc->GetGuid(aOutTargetGuid);
     Matrix4x4 transformToApzc = GetScreenToApzcTransform(apzc);
     Matrix4x4 transformToGecko = GetApzcToGeckoTransform(apzc);
     Matrix4x4 outTransform = transformToApzc * transformToGecko;
+    MOZ_ASSERT(outTransform.Is2D());
     aEvent.refPoint = TransformTo<LayoutDevicePixel>(outTransform, aEvent.refPoint);
   }
   return result;
 }
 
 nsEventStatus
 APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent,
                                    ScrollableLayerGuid* aOutTargetGuid,
@@ -1183,32 +1191,41 @@ APZCTreeManager::GetRootNode() const
  * Transform a displacement from the ParentLayer coordinates of a source APZC
  * to the ParentLayer coordinates of a target APZC.
  * @param aTreeManager the tree manager for the APZC tree containing |aSource|
  *                     and |aTarget|
  * @param aSource the source APZC
  * @param aTarget the target APZC
  * @param aStartPoint the start point of the displacement
  * @param aEndPoint the end point of the displacement
+ * @return true on success, false if aStartPoint or aEndPoint cannot be transformed into target's coordinate space
  */
-static void
+static bool
 TransformDisplacement(APZCTreeManager* aTreeManager,
                       AsyncPanZoomController* aSource,
                       AsyncPanZoomController* aTarget,
                       ParentLayerPoint& aStartPoint,
                       ParentLayerPoint& aEndPoint) {
   // Convert start and end points to Screen coordinates.
   Matrix4x4 untransformToApzc = aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
   ScreenPoint screenStart = TransformTo<ScreenPixel>(untransformToApzc, aStartPoint);
   ScreenPoint screenEnd = TransformTo<ScreenPixel>(untransformToApzc, aEndPoint);
 
+
   // Convert start and end points to aTarget's ParentLayer coordinates.
   Matrix4x4 transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
-  aStartPoint = TransformTo<ParentLayerPixel>(transformToApzc, screenStart);
-  aEndPoint = TransformTo<ParentLayerPixel>(transformToApzc, screenEnd);
+  Maybe<ParentLayerPoint> startPoint = UntransformTo<ParentLayerPixel>(transformToApzc, screenStart);
+  Maybe<ParentLayerPoint> endPoint = UntransformTo<ParentLayerPixel>(transformToApzc, screenEnd);
+  if (!startPoint || !endPoint) {
+    return false;
+  }
+  aEndPoint = *endPoint;
+  aStartPoint = *startPoint;
+
+  return true;
 }
 
 bool
 APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
                                 ParentLayerPoint aStartPoint,
                                 ParentLayerPoint aEndPoint,
                                 OverscrollHandoffState& aOverscrollHandoffState)
 {
@@ -1229,17 +1246,19 @@ APZCTreeManager::DispatchScroll(AsyncPan
   }
 
   // Convert the start and end points from |aPrev|'s coordinate space to
   // |next|'s coordinate space. Since |aPrev| may be the same as |next|
   // (if |aPrev| is the APZC that is initiating the scroll and there is no
   // scroll grabbing to grab the scroll from it), don't bother doing the
   // transformations in that case.
   if (next != aPrev) {
-    TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint);
+    if (!TransformDisplacement(this, aPrev, next, aStartPoint, aEndPoint)) {
+      return false;
+    }
   }
 
   // Scroll |next|. If this causes overscroll, it will call DispatchScroll()
   // again with an incremented index.
   return next->AttemptScroll(aStartPoint, aEndPoint, aOverscrollHandoffState);
 }
 
 bool
@@ -1282,21 +1301,23 @@ APZCTreeManager::DispatchFling(AsyncPanZ
     if (current == nullptr || current->IsDestroyed()) {
       return false;
     }
 
     endPoint = startPoint + transformedVelocity;
 
     // Only transform when current apcz can be transformed with previous
     if (startIndex > 0) {
-      TransformDisplacement(this,
+      if (!TransformDisplacement(this,
                             aOverscrollHandoffChain->GetApzcAtIndex(startIndex - 1),
                             current,
                             startPoint,
-                            endPoint);
+                            endPoint)) {
+          return false;
+      }
     }
 
     transformedVelocity = endPoint - startPoint;
 
     if (current->AttemptFling(transformedVelocity,
                               aOverscrollHandoffChain,
                               aHandoff)) {
       return true;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -946,17 +946,19 @@ nsEventStatus AsyncPanZoomController::Ha
                                                        const Matrix4x4& aTransformToApzc) {
   APZThreadUtils::AssertOnControllerThread();
 
   nsEventStatus rv = nsEventStatus_eIgnore;
 
   switch (aEvent.mInputType) {
   case MULTITOUCH_INPUT: {
     MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput();
-    multiTouchInput.TransformToLocal(aTransformToApzc);
+    if (!multiTouchInput.TransformToLocal(aTransformToApzc)) { 
+      return rv;
+    }
 
     nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
     if (listener) {
       rv = listener->HandleInputEvent(multiTouchInput);
       if (rv == nsEventStatus_eConsumeNoDefault) {
         return rv;
       }
     }
@@ -967,48 +969,56 @@ nsEventStatus AsyncPanZoomController::Ha
       case MultiTouchInput::MULTITOUCH_END: rv = OnTouchEnd(multiTouchInput); break;
       case MultiTouchInput::MULTITOUCH_CANCEL: rv = OnTouchCancel(multiTouchInput); break;
       default: NS_WARNING("Unhandled multitouch"); break;
     }
     break;
   }
   case PANGESTURE_INPUT: {
     PanGestureInput panGestureInput = aEvent.AsPanGestureInput();
-    panGestureInput.TransformToLocal(aTransformToApzc);
+    if (!panGestureInput.TransformToLocal(aTransformToApzc)) {
+      return rv;
+    }
 
     switch (panGestureInput.mType) {
       case PanGestureInput::PANGESTURE_MAYSTART: rv = OnPanMayBegin(panGestureInput); break;
       case PanGestureInput::PANGESTURE_CANCELLED: rv = OnPanCancelled(panGestureInput); break;
       case PanGestureInput::PANGESTURE_START: rv = OnPanBegin(panGestureInput); break;
       case PanGestureInput::PANGESTURE_PAN: rv = OnPan(panGestureInput, ScrollSource::Touch, true); break;
       case PanGestureInput::PANGESTURE_END: rv = OnPanEnd(panGestureInput); break;
       case PanGestureInput::PANGESTURE_MOMENTUMSTART: rv = OnPanMomentumStart(panGestureInput); break;
       case PanGestureInput::PANGESTURE_MOMENTUMPAN: rv = OnPan(panGestureInput, ScrollSource::Touch, false); break;
       case PanGestureInput::PANGESTURE_MOMENTUMEND: rv = OnPanMomentumEnd(panGestureInput); break;
       default: NS_WARNING("Unhandled pan gesture"); break;
     }
     break;
   }
   case SCROLLWHEEL_INPUT: {
     ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput();
-    scrollInput.TransformToLocal(aTransformToApzc);
+    if (!scrollInput.TransformToLocal(aTransformToApzc)) { 
+      return rv;
+    }
 
     rv = OnScrollWheel(scrollInput);
     break;
   }
   case PINCHGESTURE_INPUT: {
     PinchGestureInput pinchInput = aEvent.AsPinchGestureInput();
-    pinchInput.TransformToLocal(aTransformToApzc);
+    if (!pinchInput.TransformToLocal(aTransformToApzc)) { 
+      return rv;
+    }
 
     rv = HandleGestureEvent(pinchInput);
     break;
   }
   case TAPGESTURE_INPUT: {
     TapGestureInput tapInput = aEvent.AsTapGestureInput();
-    tapInput.TransformToLocal(aTransformToApzc);
+    if (!tapInput.TransformToLocal(aTransformToApzc)) { 
+      return rv;
+    }
 
     rv = HandleGestureEvent(tapInput);
     break;
   }
   default: NS_WARNING("Unhandled input event type"); break;
   }
 
   return rv;
@@ -1391,22 +1401,24 @@ nsEventStatus AsyncPanZoomController::On
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 bool
 AsyncPanZoomController::ConvertToGecko(const ScreenIntPoint& aPoint, CSSPoint* aOut)
 {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
-    Matrix4x4 transformToApzc = treeManagerLocal->GetScreenToApzcTransform(this);
-    Matrix4x4 transformToGecko = treeManagerLocal->GetApzcToGeckoTransform(this);
+    Matrix4x4 transformScreenToGecko = treeManagerLocal->GetScreenToApzcTransform(this) 
+                                     * treeManagerLocal->GetApzcToGeckoTransform(this);
+    
     // NOTE: This isn't *quite* LayoutDevicePoint, we just don't have a name
     // for this coordinate space and it maps the closest to LayoutDevicePoint.
+    MOZ_ASSERT(transformScreenToGecko.Is2D());
     LayoutDevicePoint layoutPoint = TransformTo<LayoutDevicePixel>(
-        transformToApzc * transformToGecko, aPoint);
+        transformScreenToGecko, aPoint);
 
     { // scoped lock to access mFrameMetrics
       ReentrantMonitorAutoEnter lock(mMonitor);
       *aOut = layoutPoint / mFrameMetrics.GetDevPixelsPerCSSPixel();
     }
     return true;
   }
   return false;
@@ -1832,32 +1844,36 @@ Matrix4x4 AsyncPanZoomController::GetTra
   return Matrix4x4();
 }
 
 ScreenPoint AsyncPanZoomController::ToScreenCoordinates(const ParentLayerPoint& aVector,
                                                         const ParentLayerPoint& aAnchor) const {
   return TransformVector<ScreenPixel>(GetTransformToThis().Inverse(), aVector, aAnchor);
 }
 
+// TODO: figure out a good way to check the w-coordinate is positive and return the result
 ParentLayerPoint AsyncPanZoomController::ToParentLayerCoordinates(const ScreenPoint& aVector,
                                                                   const ScreenPoint& aAnchor) const {
   return TransformVector<ParentLayerPixel>(GetTransformToThis(), aVector, aAnchor);
 }
 
 bool AsyncPanZoomController::Contains(const ScreenIntPoint& aPoint) const
 {
   Matrix4x4 transformToThis = GetTransformToThis();
-  ParentLayerIntPoint point = TransformTo<ParentLayerPixel>(transformToThis, aPoint);
+  Maybe<ParentLayerIntPoint> point = UntransformTo<ParentLayerPixel>(transformToThis, aPoint);
+  if (!point) {
+    return false;
+  }
 
   ParentLayerIntRect cb;
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
     GetFrameMetrics().GetCompositionBounds().ToIntRect(&cb);
   }
-  return cb.Contains(point);
+  return cb.Contains(*point);
 }
 
 ScreenCoord AsyncPanZoomController::PanDistance() const {
   ParentLayerPoint panVector;
   ParentLayerPoint panStart;
   {
     ReentrantMonitorAutoEnter lock(mMonitor);
     panVector = ParentLayerPoint(mX.PanDistance(), mY.PanDistance());
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -202,20 +202,17 @@ HitTestingTreeNode::IsOutsideClip(const 
 Maybe<LayerPoint>
 HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const
 {
   // convert into Layer coordinate space
   gfx::Matrix4x4 localTransform = mTransform;
   if (mApzc) {
     localTransform = localTransform * mApzc->GetCurrentAsyncTransformWithOverscroll();
   }
-  gfx::Point4D point = localTransform.Inverse().ProjectPoint(aPoint.ToUnknownPoint());
-  return point.HasPositiveWCoord()
-        ? Some(ViewAs<LayerPixel>(point.As2DPoint()))
-        : Nothing();
+  return UntransformTo<LayerPixel>(localTransform.Inverse(), aPoint);
 }
 
 HitTestResult
 HitTestingTreeNode::HitTest(const ParentLayerPoint& aPoint) const
 {
   // This should only ever get called if the point is inside the clip region
   // for this node.
   MOZ_ASSERT(!IsOutsideClip(aPoint));
--- a/layout/base/UnitTransforms.h
+++ b/layout/base/UnitTransforms.h
@@ -140,11 +140,50 @@ template <typename TargetUnits, typename
 static gfx::PointTyped<TargetUnits> TransformVector(const gfx::Matrix4x4& aTransform,
                                                     const gfx::PointTyped<SourceUnits>& aVector,
                                                     const gfx::PointTyped<SourceUnits>& aAnchor) {
   gfx::PointTyped<TargetUnits> transformedStart = TransformTo<TargetUnits>(aTransform, aAnchor);
   gfx::PointTyped<TargetUnits> transformedEnd = TransformTo<TargetUnits>(aTransform, aAnchor + aVector);
   return transformedEnd - transformedStart;
 }
 
+// UntransformTo() and UntransformVector() are like TransformTo() and 
+// TransformVector(), respectively, but are intended for cases where
+// the transformation matrix is the inverse of a 3D projection. When
+// using such transforms, the resulting Point4D is only meaningful
+// if it has a positive w-coordinate. To handle this, these functions
+// return a Maybe object which contains a value if and only if the
+// result is meaningful
+template <typename TargetUnits, typename SourceUnits>
+static Maybe<gfx::PointTyped<TargetUnits>> UntransformTo(const gfx::Matrix4x4& aTransform,
+                                                const gfx::PointTyped<SourceUnits>& aPoint)
+{
+  gfx::Point4D point = aTransform.ProjectPoint(aPoint.ToUnknownPoint());
+  if (!point.HasPositiveWCoord()) {
+    return Nothing();
+  }
+  return Some(ViewAs<TargetUnits>(point.As2DPoint()));
+}
+template <typename TargetUnits, typename SourceUnits>
+static Maybe<gfx::IntPointTyped<TargetUnits>> UntransformTo(const gfx::Matrix4x4& aTransform,
+                                                const gfx::IntPointTyped<SourceUnits>& aPoint)
+{
+  gfx::Point4D point = aTransform.ProjectPoint(aPoint.ToUnknownPoint());
+  if (!point.HasPositiveWCoord()) {
+    return Nothing();
+  }
+  return Some(RoundedToInt(ViewAs<TargetUnits>(point.As2DPoint())));
+}
+template <typename TargetUnits, typename SourceUnits>
+static Maybe<gfx::PointTyped<TargetUnits>> UntransformVector(const gfx::Matrix4x4& aTransform,
+                                                    const gfx::PointTyped<SourceUnits>& aVector,
+                                                    const gfx::PointTyped<SourceUnits>& aAnchor) {
+  gfx::Point4D point = aTransform.ProjectPoint(aAnchor.ToUnknownPoint() + aVector.ToUnknownPoint()) 
+    - aTransform.ProjectPoint(aAnchor.ToUnknownPoint());
+  if (!point.HasPositiveWCoord()){
+    return Nothing();
+  }
+  return Some(ViewAs<TargetUnits>(point.As2DPoint()));
+}
+
 }
 
 #endif
--- a/widget/InputData.cpp
+++ b/widget/InputData.cpp
@@ -207,42 +207,72 @@ MultiTouchInput::MultiTouchInput(const W
                                          ScreenIntPoint::FromUnknownPoint(
                                            gfx::IntPoint(aMouseEvent.refPoint.x,
                                                          aMouseEvent.refPoint.y)),
                                          ScreenSize(1, 1),
                                          180.0f,
                                          1.0f));
 }
 
-void
+bool
 MultiTouchInput::TransformToLocal(const gfx::Matrix4x4& aTransform)
 {
   for (size_t i = 0; i < mTouches.Length(); i++) {
-    mTouches[i].mLocalScreenPoint = TransformTo<ParentLayerPixel>(aTransform, ScreenPoint(mTouches[i].mScreenPoint));
+    Maybe<ParentLayerIntPoint> point = UntransformTo<ParentLayerPixel>(aTransform, mTouches[i].mScreenPoint);
+    if (!point) { 
+      return false;
+    }
+    mTouches[i].mLocalScreenPoint = *point;
   }
+  return true;
 }
 
-void
+bool
 PanGestureInput::TransformToLocal(const gfx::Matrix4x4& aTransform)
-{
-  mLocalPanStartPoint = TransformTo<ParentLayerPixel>(aTransform, mPanStartPoint);
-  mLocalPanDisplacement = TransformVector<ParentLayerPixel>(aTransform, mPanDisplacement, mPanStartPoint);
+{ 
+  Maybe<ParentLayerPoint> panStartPoint = UntransformTo<ParentLayerPixel>(aTransform, mPanStartPoint);
+  if (!panStartPoint) {
+    return false;
+  }
+  mLocalPanStartPoint = *panStartPoint;
+  
+  Maybe<ParentLayerPoint> panDisplacement = UntransformVector<ParentLayerPixel>(aTransform, mPanDisplacement, mPanStartPoint);
+  if (!panDisplacement) {
+    return false;
+  }
+  mLocalPanDisplacement = *panDisplacement;
+  return true;
 }
 
-void
+bool
 PinchGestureInput::TransformToLocal(const gfx::Matrix4x4& aTransform)
-{
-  mLocalFocusPoint = TransformTo<ParentLayerPixel>(aTransform, mFocusPoint);
+{ 
+  Maybe<ParentLayerPoint> point = UntransformTo<ParentLayerPixel>(aTransform, mFocusPoint);
+  if (!point) {
+    return false;
+  }
+  mLocalFocusPoint = *point;
+  return true;
 }
 
-void
+bool
 TapGestureInput::TransformToLocal(const gfx::Matrix4x4& aTransform)
 {
-  mLocalPoint = TransformTo<ParentLayerPixel>(aTransform, mPoint);
+  Maybe<ParentLayerIntPoint> point = UntransformTo<ParentLayerPixel>(aTransform, mPoint);
+  if (!point) {
+    return false;
+  }
+  mLocalPoint = *point;
+  return true;
 }
 
-void
+bool
 ScrollWheelInput::TransformToLocal(const gfx::Matrix4x4& aTransform)
 {
-  mLocalOrigin = TransformTo<ParentLayerPixel>(aTransform, mOrigin);
+  Maybe<ParentLayerPoint> point = UntransformTo<ParentLayerPixel>(aTransform, mOrigin);
+  if (!point) {
+    return false;
+  }
+  mLocalOrigin = *point;
+  return true;
 }
 
 } // namespace mozilla
--- a/widget/InputData.h
+++ b/widget/InputData.h
@@ -234,17 +234,17 @@ public:
   // 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.
   explicit MultiTouchInput(const WidgetMouseEvent& aMouseEvent);
 
-  void TransformToLocal(const gfx::Matrix4x4& aTransform);
+  bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
   MultiTouchType mType;
   nsTArray<SingleTouchData> mTouches;
 };
 
 /**
  * Encapsulation class for pan events, can be used off-main-thread.
  * These events are currently only used for scrolling on desktop.
@@ -306,17 +306,17 @@ public:
                   Modifiers aModifiers)
     : InputData(PANGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType),
       mPanStartPoint(aPanStartPoint),
       mPanDisplacement(aPanDisplacement)
   {
   }
 
-  void TransformToLocal(const gfx::Matrix4x4& aTransform);
+  bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
   PanGestureType mType;
   ScreenPoint mPanStartPoint;
 
   // Only non-zero if mType is PANGESTURE_PAN or PANGESTURE_MOMENTUMPAN.
   ScreenPoint mPanDisplacement;
 
   // Versions of |mPanStartPoint| and |mPanDisplacement| in the local
@@ -369,17 +369,17 @@ public:
     : InputData(PINCHGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType),
       mLocalFocusPoint(aLocalFocusPoint),
       mCurrentSpan(aCurrentSpan),
       mPreviousSpan(aPreviousSpan)
   {
   }
 
-  void TransformToLocal(const gfx::Matrix4x4& aTransform);
+  bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
   PinchGestureType mType;
 
   // Center point of the pinch gesture. That is, if there are two fingers on the
   // screen, it is their midpoint. In the case of more than two fingers, the
   // point is implementation-specific, but can for example be the midpoint
   // between the very first and very last touch. This is in device pixels and
   // are the coordinates on the screen of this midpoint.
@@ -439,17 +439,17 @@ public:
                   const ParentLayerPoint& aLocalPoint,
                   Modifiers aModifiers)
     : InputData(TAPGESTURE_INPUT, aTime, aTimeStamp, aModifiers),
       mType(aType),
       mLocalPoint(aLocalPoint)
   {
   }
 
-  void TransformToLocal(const gfx::Matrix4x4& aTransform);
+  bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
   TapGestureType mType;
 
   // The location of the tap in screen pixels.
   ScreenIntPoint mPoint;
 
   // The location of the tap in the local coordinates of the APZC receiving it.
   // This is set and used by APZ.
@@ -501,17 +501,17 @@ public:
    : InputData(SCROLLWHEEL_INPUT, aTime, aTimeStamp, aModifiers),
      mDeltaType(aDeltaType),
      mScrollMode(aScrollMode),
      mOrigin(aOrigin),
      mDeltaX(aDeltaX),
      mDeltaY(aDeltaY)
   {}
 
-  void TransformToLocal(const gfx::Matrix4x4& aTransform);
+  bool TransformToLocal(const gfx::Matrix4x4& aTransform);
 
   ScrollDeltaType mDeltaType;
   ScrollMode mScrollMode;
   ScreenPoint mOrigin;
 
   // Deltas are in units corresponding to the delta type. For line deltas, they
   // are the number of line units to scroll. The number of device pixels for a
   // horizontal and vertical line unit are in FrameMetrics::mLineScrollAmount.