Bug 1224015 - Part 1: nsLayoutUtils functions do not account for nsPresShell resolution r=tnikkel
authorRandall Barker <rbarker@mozilla.com>
Thu, 26 Nov 2015 20:51:13 -0600
changeset 310397 22c0d6e4df2c32cbecc6a1be70fbedafc6826d20
parent 310396 8631caf756eb6abf821a15a57a095a98d6f23af5
child 310398 0aa2e79b925254d421b27ee1c2efefe7f2cb1c85
push id1040
push userraliiev@mozilla.com
push dateMon, 29 Feb 2016 17:11:22 +0000
treeherdermozilla-release@8c3167321162 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1224015
milestone45.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 1224015 - Part 1: nsLayoutUtils functions do not account for nsPresShell resolution r=tnikkel
dom/base/nsContentUtils.cpp
dom/events/Event.cpp
gfx/layers/apz/util/APZCCallbackHelper.cpp
gfx/layers/apz/util/ChromeProcessController.cpp
gfx/src/nsPoint.h
layout/base/nsIPresShell.h
layout/base/nsLayoutUtils.cpp
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7654,17 +7654,17 @@ nsContentUtils::GetButtonsFlagForButton(
 }
 
 LayoutDeviceIntPoint
 nsContentUtils::ToWidgetPoint(const CSSPoint& aPoint,
                               const nsPoint& aOffset,
                               nsPresContext* aPresContext)
 {
   return LayoutDeviceIntPoint::FromAppUnitsRounded(
-    CSSPoint::ToAppUnits(aPoint) + aOffset,
+    (CSSPoint::ToAppUnits(aPoint) + aOffset).ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution()),
     aPresContext->AppUnitsPerDevPixel());
 }
 
 nsView*
 nsContentUtils::GetViewToDispatchEvent(nsPresContext* presContext,
                                        nsIPresShell** presShell)
 {
   if (presContext && presShell) {
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -930,16 +930,20 @@ Event::GetScreenCoords(nsPresContext* aP
   WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent();
   if (!aPresContext || !(guiEvent && guiEvent->widget)) {
     return CSSIntPoint(aPoint.x, aPoint.y);
   }
 
   nsPoint pt =
     LayoutDevicePixel::ToAppUnits(aPoint, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
 
+  if (aPresContext->PresShell()) {
+    pt = pt.RemoveResolution(aPresContext->PresShell()->GetCumulativeScaleResolution());
+  }
+
   pt += LayoutDevicePixel::ToAppUnits(guiEvent->widget->WidgetToScreenOffset(),
                                       aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
 
   return CSSPixel::FromAppUnitsRounded(pt);
 }
 
 // static
 CSSIntPoint
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -417,44 +417,40 @@ APZCCallbackHelper::GetRootContentDocume
 CSSPoint
 APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
                                            const ScrollableLayerGuid& aGuid)
 {
     CSSPoint input = aInput;
     if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) {
         return input;
     }
-    nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
-    if (!content) {
-        return input;
-    }
-
-    // First, scale inversely by the root content document's pres shell
-    // resolution to cancel the scale-to-resolution transform that the
-    // compositor adds to the layer with the pres shell resolution. The points
-    // sent to Gecko by APZ don't have this transform unapplied (unlike other
-    // compositor-side transforms) because APZ doesn't know about it.
-    if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
-        input = input / shell->GetResolution();
-    }
-
-    // Now apply the callback-transform.
+    // Apply the callback-transform.
     // XXX: technically we need to walk all the way up the layer tree from the layer
     // represented by |aGuid.mScrollId| up to the root of the layer tree and apply
     // the input transforms at each level in turn. However, it is quite difficult
     // to do this given that the structure of the layer tree may be different from
     // the structure of the content tree. Also it may be impossible to do correctly
     // at this point because there are other CSS transforms and such interleaved in
     // between so applying the inputTransforms all in a row at the end may leave
     // some things transformed improperly. In practice we should rarely hit scenarios
     // where any of this matters, so I'm skipping it for now and just doing the single
     // transform for the layer that the input hit.
-    void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
-    if (property) {
-        CSSPoint delta = (*static_cast<CSSPoint*>(property));
+    nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
+    if (content) {
+        void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
+        CSSPoint delta = property ? (*static_cast<CSSPoint*>(property)) : CSSPoint(0.0f, 0.0f);
+        // The delta is in root content document coordinate space while the
+        // aInput point is in root document coordinate space so convert the
+        // delta to root document space before adding it to the aInput point.
+        float resolution = 1.0f;
+        if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
+            resolution = shell->GetResolution();
+        }
+        delta.x = delta.x * resolution;
+        delta.y = delta.y * resolution;
         input += delta;
     }
     return input;
 }
 
 LayoutDeviceIntPoint
 APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
                                            const ScrollableLayerGuid& aGuid,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -142,16 +142,24 @@ ChromeProcessController::HandleDoubleTap
   }
 
   nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
   if (!document.get()) {
     return;
   }
 
   CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
+  // CalculateRectToZoomTo performs a hit test on the frame associated with the
+  // Root Content Document. Unfortunately that frame does not know about the
+  // resolution of the document and so we must remove it before calculating
+  // the zoomToRect.
+  nsIPresShell* presShell = document->GetShell();
+  const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f;
+  point.x = point.x / resolution;
+  point.y = point.y / resolution;
   CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
 
   uint32_t presShellId;
   FrameMetrics::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
       document->GetDocumentElement(), &presShellId, &viewId)) {
     mAPZCTreeManager->ZoomToRect(
       ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
--- a/gfx/src/nsPoint.h
+++ b/gfx/src/nsPoint.h
@@ -32,16 +32,21 @@ struct nsPoint : public mozilla::gfx::Ba
 
   /**
    * Return this point scaled to a different appunits per pixel (APP) ratio.
    * @param aFromAPP the APP to scale from
    * @param aToAPP the APP to scale to
    */
   MOZ_WARN_UNUSED_RESULT inline nsPoint
     ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const;
+
+  MOZ_WARN_UNUSED_RESULT inline nsPoint
+    RemoveResolution(const float resolution) const;
+  MOZ_WARN_UNUSED_RESULT inline nsPoint
+    ApplyResolution(const float resolution) const;
 };
 
 inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel);
 
 inline nsIntPoint
 nsPoint::ScaleToNearestPixels(float aXScale, float aYScale,
                               nscoord aAppUnitsPerPixel) const
 {
@@ -63,16 +68,38 @@ nsPoint::ScaleToOtherAppUnits(int32_t aF
     nsPoint point;
     point.x = NSToCoordRound(NSCoordScale(x, aFromAPP, aToAPP));
     point.y = NSToCoordRound(NSCoordScale(y, aFromAPP, aToAPP));
     return point;
   }
   return *this;
 }
 
+inline nsPoint
+nsPoint::RemoveResolution(const float resolution) const {
+  if (resolution != 1.0f) {
+    nsPoint point;
+    point.x = NSToCoordRound(NSCoordToFloat(x) / resolution);
+    point.y = NSToCoordRound(NSCoordToFloat(y) / resolution);
+    return point;
+  }
+  return *this;
+}
+
+inline nsPoint
+nsPoint::ApplyResolution(const float resolution) const {
+  if (resolution != 1.0f) {
+    nsPoint point;
+    point.x = NSToCoordRound(NSCoordToFloat(x) * resolution);
+    point.y = NSToCoordRound(NSCoordToFloat(y) * resolution);
+    return point;
+  }
+  return *this;
+}
+
 // app units are integer multiples of pixels, so no rounding needed
 inline nsPoint
 ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel)
 {
   return nsPoint(NSIntPixelsToAppUnits(aPoint.x, aAppUnitsPerPixel),
                  NSIntPixelsToAppUnits(aPoint.y, aAppUnitsPerPixel));
 }
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -135,20 +135,20 @@ typedef struct CapturingContentInfo {
   // capture should only be allowed during a mousedown event
   bool mAllowed;
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   mozilla::StaticRefPtr<nsIContent> mContent;
 } CapturingContentInfo;
 
-// 327d78a0-0680-4709-b209-1cf9578720e6
+// 5023beaa-0e54-4fc7-b9dc-0344dc4fb8be
 #define NS_IPRESSHELL_IID \
-{ 0x327d78a0, 0x0680, 0x4709, \
-  { 0xb2, 0x09, 0x1c, 0xf9, 0x57, 0x87, 0x20, 0xe6 } }
+{ 0x5023beaa, 0x0e54, 0x4fc7, \
+  { 0xb9, 0xdc, 0x03, 0x44, 0xdc, 0x4f, 0xb8, 0xbe } }
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -1411,16 +1411,17 @@ public:
    * resolution bounds are sane, and the resolution of this was
    * actually updated.
    *
    * The resolution defaults to 1.0.
    */
   virtual nsresult SetResolution(float aResolution) = 0;
   float GetResolution() { return mResolution.valueOr(1.0); }
   virtual float GetCumulativeResolution() = 0;
+  virtual float GetCumulativeScaleResolution() = 0;
 
   /**
    * Was the current resolution set by the user or just default initialized?
    */
   bool IsResolutionSet() { return mResolution.isSome(); }
 
   /**
    * Similar to SetResolution() but also increases the scale of the content
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2106,17 +2106,19 @@ nsLayoutUtils::GetEventCoordinatesRelati
     nsIWidget* frameWidget = view->GetWidget();
     if (frameWidget && frameWidget == aWidget) {
       // Special case this cause it happens a lot.
       // This also fixes bug 664707, events in the extra-special case of select
       // dropdown popups that are transformed.
       nsPresContext* presContext = aFrame->PresContext();
       nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x),
                  presContext->DevPixelsToAppUnits(aPoint.y));
-      return pt - view->ViewToWidgetOffset();
+      pt = pt - view->ViewToWidgetOffset();
+      pt = pt.RemoveResolution(presContext->PresShell()->GetCumulativeScaleResolution());
+      return pt;
     }
   }
 
   /* If we walk up the frame tree and discover that any of the frames are
    * transformed, we need to do extra work to convert from the global
    * space to the local space.
    */
   nsIFrame* rootFrame = aFrame;
@@ -2141,16 +2143,20 @@ nsLayoutUtils::GetEventCoordinatesRelati
     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   }
 
   // Convert from root document app units to app units of the document aFrame
   // is in.
   int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
   int32_t localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
   widgetToView = widgetToView.ScaleToOtherAppUnits(rootAPD, localAPD);
+  nsIPresShell* shell = aFrame->PresContext()->PresShell();
+
+  // XXX Bug 1224748 - Update nsLayoutUtils functions to correctly handle nsPresShell resolution
+  widgetToView = widgetToView.RemoveResolution(shell->GetCumulativeScaleResolution());
 
   /* If we encountered a transform, we can't do simple arithmetic to figure
    * out how to convert back to aFrame's coordinates and must use the CTM.
    */
   if (transformFound || aFrame->IsSVGText()) {
     return TransformRootPointToFrame(aFrame, widgetToView);
   }
 
@@ -2888,18 +2894,19 @@ nsLayoutUtils::TranslateViewToWidget(nsP
                                      nsIWidget* aWidget)
 {
   nsPoint viewOffset;
   nsIWidget* viewWidget = aView->GetNearestWidget(&viewOffset);
   if (!viewWidget) {
     return LayoutDeviceIntPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   }
 
-  LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(aPt.x + viewOffset.x),
-                                            aPresContext->AppUnitsToDevPixels(aPt.y + viewOffset.y));
+  nsPoint pt = (aPt + viewOffset).ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution());
+  LayoutDeviceIntPoint relativeToViewWidget(aPresContext->AppUnitsToDevPixels(pt.x),
+                                            aPresContext->AppUnitsToDevPixels(pt.y));
   return relativeToViewWidget + WidgetToWidgetOffset(viewWidget, aWidget);
 }
 
 // Combine aNewBreakType with aOrigBreakType, but limit the break types
 // to NS_STYLE_CLEAR_LEFT, RIGHT, BOTH.
 uint8_t
 nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType,
                                 uint8_t aNewBreakType)
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5346,16 +5346,32 @@ float PresShell::GetCumulativeResolution
   float resolution = GetResolution();
   nsPresContext* parentCtx = GetPresContext()->GetParentPresContext();
   if (parentCtx) {
     resolution *= parentCtx->PresShell()->GetCumulativeResolution();
   }
   return resolution;
 }
 
+float PresShell::GetCumulativeScaleResolution()
+{
+  float resolution = 1.0;
+  nsIPresShell* currentShell = this;
+  while (currentShell) {
+    resolution *=  currentShell->ScaleToResolution() ? currentShell->GetResolution() : 1.0f;
+    nsPresContext* parentCtx = currentShell->GetPresContext()->GetParentPresContext();
+    if (parentCtx) {
+      currentShell = parentCtx->PresShell();
+    } else {
+      currentShell = nullptr;
+    }
+  }
+  return resolution;
+}
+
 void PresShell::SetRenderingState(const RenderingState& aState)
 {
   if (mRenderFlags != aState.mRenderFlags) {
     // Rendering state changed in a way that forces us to flush any
     // retained layers we might already have.
     LayerManager* manager = GetLayerManager();
     if (manager) {
       FrameLayerBuilder::InvalidateAllLayers(manager);
@@ -7092,18 +7108,19 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       retargetEventDoc = window->GetExtantDoc();
       if (!retargetEventDoc)
         return NS_OK;
     } else if (capturingContent) {
       // if the mouse is being captured then retarget the mouse event at the
       // document that is being captured.
       retargetEventDoc = capturingContent->GetCrossShadowCurrentDoc();
 #ifdef ANDROID
-    } else if (aEvent->mClass == eTouchEventClass ||
-              (aEvent->AsMouseEvent() && aEvent->AsMouseEvent()->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH)) {
+    } else if ((aEvent->mClass == eTouchEventClass) ||
+               (aEvent->mClass == eMouseEventClass) ||
+               (aEvent->mClass == eWheelEventClass)) {
       retargetEventDoc = GetTouchEventTargetDocument();
 #endif
     }
 
     if (retargetEventDoc) {
       nsCOMPtr<nsIPresShell> presShell = retargetEventDoc->GetShell();
       if (!presShell)
         return NS_OK;
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -213,16 +213,17 @@ public:
   virtual nsresult SetResolution(float aResolution) override {
     return SetResolutionImpl(aResolution, /* aScaleToResolution = */ false);
   }
   virtual nsresult SetResolutionAndScaleTo(float aResolution) override {
     return SetResolutionImpl(aResolution, /* aScaleToResolution = */ true);
   }
   virtual bool ScaleToResolution() const override;
   virtual float GetCumulativeResolution() override;
+  virtual float GetCumulativeScaleResolution() override;
 
   //nsIViewObserver interface
 
   virtual void Paint(nsView* aViewToPaint, const nsRegion& aDirtyRegion,
                      uint32_t aFlags) override;
   virtual nsresult HandleEvent(nsIFrame* aFrame,
                                mozilla::WidgetGUIEvent* aEvent,
                                bool aDontRetargetEvents,