Bug 1370034 - Call SetWindowTransform with the right values from -moz-window-transform(-origin). r?dbaron draft
authorMarkus Stange <mstange@themasta.com>
Mon, 19 Jun 2017 00:49:52 -0400
changeset 596348 b18a48b2cc8002a8887a7dad9484d4116b938b45
parent 596347 09e3d93c1bcb133e6d0157a856751c331451500f
child 596816 d11579e749a9d2d3ab7ca2e047c9516cd70f0f47
push id64588
push userbmo:mstange@themasta.com
push dateMon, 19 Jun 2017 05:46:08 +0000
reviewersdbaron
bugs1370034
milestone56.0a1
Bug 1370034 - Call SetWindowTransform with the right values from -moz-window-transform(-origin). r?dbaron MozReview-Commit-ID: 3IVl3kdbpTd
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/painting/nsDisplayList.cpp
layout/style/nsStyleTransformMatrix.cpp
layout/style/nsStyleTransformMatrix.h
layout/xul/nsMenuPopupFrame.cpp
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10474,16 +10474,57 @@ IsFrameScrolledOutOfView(nsIFrame *aFram
 }
 
 bool
 nsIFrame::IsScrolledOutOfView()
 {
   return IsFrameScrolledOutOfView(this);
 }
 
+gfx::Matrix
+nsIFrame::ComputeWidgetTransform()
+{
+  const nsStyleUIReset* uiReset = StyleUIReset();
+  if (!uiReset->mSpecifiedWindowTransform) {
+    return gfx::Matrix();
+  }
+
+  nsStyleTransformMatrix::TransformReferenceBox refBox;
+  refBox.Init(GetSize());
+
+  nsPresContext* presContext = PresContext();
+  int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
+  RuleNodeCacheConditions dummy;
+  bool dummyBool;
+  gfx::Matrix4x4 matrix =
+    nsStyleTransformMatrix::ReadTransforms(uiReset->mSpecifiedWindowTransform->mHead,
+                                           StyleContext(),
+                                           presContext,
+                                           dummy,
+                                           refBox,
+                                           float(appUnitsPerDevPixel),
+                                           &dummyBool);
+
+  // Apply the -moz-window-transform-origin translation to the matrix.
+  Point transformOrigin =
+    nsStyleTransformMatrix::Convert2DPosition(uiReset->mWindowTransformOrigin,
+                                              refBox, appUnitsPerDevPixel);
+  matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
+
+  gfx::Matrix result2d;
+  if (!matrix.CanDraw2D(&result2d)) {
+    // FIXME: It would be preferable to reject non-2D transforms at parse time.
+    NS_WARNING("-moz-window-transform does not describe a 2D transform, "
+               "but only 2d transforms are supported");
+    return gfx::Matrix();
+  }
+
+  return result2d;
+}
+
 static already_AddRefed<nsIWidget>
 GetWindowWidget(nsPresContext* aPresContext)
 {
   // We want to obtain the widget for the window. We can't use any of these
   // methods: nsPresContext::GetRootWidget, nsPresContext::GetNearestWidget,
   // nsIFrame::GetNearestWidget because those deal with child widgets and
   // there is no parent widget connection between child widgets and the
   // window widget that contains them.
@@ -10509,16 +10550,17 @@ nsIFrame::UpdateWidgetProperties()
   nsIFrame* rootFrame =
     presContext->FrameConstructor()->GetRootElementStyleFrame();
   if (this != rootFrame) {
     // Only the window's root style frame is relevant for widget properties.
     return;
   }
   if (nsCOMPtr<nsIWidget> widget = GetWindowWidget(presContext)) {
     widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
+    widget->SetWindowTransform(ComputeWidgetTransform());
   }
 }
 
 void
 nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoStyleSet& aStyleSet,
                                         nsStyleChangeList& aChangeList,
                                         nsChangeHint aHintForThisFrame)
 {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -3930,16 +3930,24 @@ public:
   }
 
   /**
    * Returns true if the frame is scrolled out of view.
    */
   bool IsScrolledOutOfView();
 
   /**
+   * Computes a 2D matrix from the -moz-window-transform and
+   * -moz-window-transform-origin properties on aFrame.
+   * Values that don't result in a 2D matrix will be ignored and an identity
+   * matrix will be returned instead.
+   */
+  Matrix ComputeWidgetTransform();
+
+  /**
    * Applies the values from the -moz-window-* properties to the widget.
    */
   virtual void UpdateWidgetProperties();
 
   /**
    * @return true iff this frame has one or more associated image requests.
    * @see mozilla::css::ImageLoader.
    */
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7068,67 +7068,38 @@ nsDisplayTransform::ComputePerspectiveMa
   }
   nscoord perspective = cbDisplay->mChildPerspective.GetCoordValue();
   if (perspective < std::numeric_limits<Float>::epsilon()) {
     return true;
   }
 
   TransformReferenceBox refBox(cbFrame);
 
-  /* Allows us to access named variables by index. */
-  Point3D perspectiveOrigin;
-  gfx::Float* coords[2] = {&perspectiveOrigin.x, &perspectiveOrigin.y};
-  TransformReferenceBox::DimensionGetter dimensionGetter[] =
-    { &TransformReferenceBox::Width, &TransformReferenceBox::Height };
-
-  /* For both of the coordinates, if the value of perspective-origin is a
-   * percentage, it's relative to the size of the frame.  Otherwise, if it's
-   * a distance, it's already computed for us!
-   */
-  for (uint8_t index = 0; index < 2; ++index) {
-    /* If the -transform-origin specifies a percentage, take the percentage
-     * of the size of the box.
-     */
-    const nsStyleCoord &coord = cbDisplay->mPerspectiveOrigin[index];
-    if (coord.GetUnit() == eStyleUnit_Calc) {
-      const nsStyleCoord::Calc *calc = coord.GetCalcValue();
-      *coords[index] =
-        NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
-          calc->mPercent +
-        NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerPixel);
-    } else if (coord.GetUnit() == eStyleUnit_Percent) {
-      *coords[index] =
-        NSAppUnitsToFloatPixels((refBox.*dimensionGetter[index])(), aAppUnitsPerPixel) *
-        coord.GetPercentValue();
-    } else {
-      MOZ_ASSERT(coord.GetUnit() == eStyleUnit_Coord, "unexpected unit");
-      *coords[index] =
-        NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
-    }
-  }
+  Point perspectiveOrigin =
+    nsStyleTransformMatrix::Convert2DPosition(cbDisplay->mPerspectiveOrigin,
+                                              refBox, aAppUnitsPerPixel);
 
   /* GetOffsetTo computes the offset required to move from 0,0 in cbFrame to 0,0
    * in aFrame. Although we actually want the inverse of this, it's faster to
    * compute this way.
    */
   nsPoint frameToCbOffset = -aFrame->GetOffsetTo(cbFrame);
-  Point3D frameToCbGfxOffset(
+  Point frameToCbGfxOffset(
             NSAppUnitsToFloatPixels(frameToCbOffset.x, aAppUnitsPerPixel),
-            NSAppUnitsToFloatPixels(frameToCbOffset.y, aAppUnitsPerPixel),
-            0.0f);
+            NSAppUnitsToFloatPixels(frameToCbOffset.y, aAppUnitsPerPixel));
 
   /* Move the perspective origin to be relative to aFrame, instead of relative
    * to the containing block which is how it was specified in the style system.
    */
   perspectiveOrigin += frameToCbGfxOffset;
 
   aOutMatrix._34 =
     -1.0 / NSAppUnitsToFloatPixels(perspective, aAppUnitsPerPixel);
 
-  aOutMatrix.ChangeBasis(perspectiveOrigin);
+  aOutMatrix.ChangeBasis(Point3D(perspectiveOrigin.x, perspectiveOrigin.y, 0));
   return true;
 }
 
 nsDisplayTransform::FrameTransformProperties::FrameTransformProperties(const nsIFrame* aFrame,
                                                                        float aAppUnitsPerPixel,
                                                                        const nsRect* aBoundsOverride)
   : mFrame(aFrame)
   , mTransformList(aFrame->StyleDisplay()->mSpecifiedTransform)
--- a/layout/style/nsStyleTransformMatrix.cpp
+++ b/layout/style/nsStyleTransformMatrix.cpp
@@ -1002,16 +1002,49 @@ ReadTransforms(const nsCSSValueList* aLi
 
   float scale = float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
   result.PreScale(1/scale, 1/scale, 1/scale);
   result.PostScale(scale, scale, scale);
 
   return result;
 }
 
+Point
+Convert2DPosition(nsStyleCoord const (&aValue)[2],
+                  TransformReferenceBox& aRefBox,
+                  int32_t aAppUnitsPerDevPixel)
+{
+  float position[2];
+  nsStyleTransformMatrix::TransformReferenceBox::DimensionGetter dimensionGetter[] =
+    { &nsStyleTransformMatrix::TransformReferenceBox::Width,
+      &nsStyleTransformMatrix::TransformReferenceBox::Height };
+  for (uint8_t index = 0; index < 2; ++index) {
+    const nsStyleCoord& value  = aValue[index];
+    if (value.GetUnit() == eStyleUnit_Calc) {
+      const nsStyleCoord::Calc *calc = value.GetCalcValue();
+      position[index] =
+        NSAppUnitsToFloatPixels((aRefBox.*dimensionGetter[index])(), aAppUnitsPerDevPixel) *
+          calc->mPercent +
+        NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerDevPixel);
+    } else if (value.GetUnit() == eStyleUnit_Percent) {
+      position[index] =
+        NSAppUnitsToFloatPixels((aRefBox.*dimensionGetter[index])(), aAppUnitsPerDevPixel) *
+        value.GetPercentValue();
+    } else {
+      MOZ_ASSERT(value.GetUnit() == eStyleUnit_Coord,
+                "unexpected unit");
+      position[index] =
+        NSAppUnitsToFloatPixels(value.GetCoordValue(),
+                                aAppUnitsPerDevPixel);
+    }
+  }
+
+  return Point(position[0], position[1]);
+}
+
 /*
  * The relevant section of the transitions specification:
  * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
  * defers all of the details to the 2-D and 3-D transforms specifications.
  * For the 2-D transforms specification (all that's relevant for us, right
  * now), the relevant section is:
  * http://dev.w3.org/csswg/css3-2d-transforms/#animation
  * This, in turn, refers to the unmatrix program in Graphics Gems,
--- a/layout/style/nsStyleTransformMatrix.h
+++ b/layout/style/nsStyleTransformMatrix.h
@@ -200,16 +200,24 @@ namespace nsStyleTransformMatrix {
   mozilla::gfx::Matrix4x4 ReadTransforms(const nsCSSValueList* aList,
                                          nsStyleContext* aContext,
                                          nsPresContext* aPresContext,
                                          mozilla::RuleNodeCacheConditions& aConditions,
                                          TransformReferenceBox& aBounds,
                                          float aAppUnitsPerMatrixUnit,
                                          bool* aContains3dTransform);
 
+  /**
+   * Given two nsStyleCoord values, compute the 2d position with respect to the
+   * given TransformReferenceBox that these values describe, in device pixels.
+   */
+  mozilla::gfx::Point Convert2DPosition(nsStyleCoord const (&aValue)[2],
+                                        TransformReferenceBox& aRefBox,
+                                        int32_t aAppUnitsPerDevPixel);
+
   // Shear type for decomposition.
   enum class ShearType {
     XYSHEAR,
     XZSHEAR,
     YZSHEAR,
     Count
   };
   using ShearArray =
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -456,16 +456,17 @@ nsMenuPopupFrame::IsLeafDynamic() const
           !parentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup));
 }
 
 void
 nsMenuPopupFrame::UpdateWidgetProperties()
 {
   if (nsIWidget* widget = GetWidget()) {
     widget->SetWindowOpacity(StyleUIReset()->mWindowOpacity);
+    widget->SetWindowTransform(ComputeWidgetTransform());
   }
 }
 
 void
 nsMenuPopupFrame::LayoutPopup(nsBoxLayoutState& aState, nsIFrame* aParentMenu,
                               nsIFrame* aAnchor, bool aSizedToPopup)
 {
   if (!mGeneratedChildren)