Bug 591718. Part 1: rename some poorly-named methods, rework global-transform methods to avoid computing bounding-boxes more than once when there are are multiple transformed ancestors, make sure nsIFrame::GetTransformMatrix can stop at any desired ancestor. r=mats
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 28 Dec 2011 16:24:18 +1300
changeset 83418 10ea92a6a8503b5f0f04db8951e5ca733839f70e
parent 83417 fa8de405cc873ef1f68fa0e2ded9b21ce544ec8f
child 83419 f793f9cfa72c746a1b35928254e12653141af3fb
push id4400
push userrocallahan@mozilla.com
push dateWed, 28 Dec 2011 03:29:06 +0000
treeherdermozilla-inbound@dc7c7734ec7c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs591718
milestone12.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 591718. Part 1: rename some poorly-named methods, rework global-transform methods to avoid computing bounding-boxes more than once when there are are multiple transformed ancestors, make sure nsIFrame::GetTransformMatrix can stop at any desired ancestor. r=mats
layout/base/nsDisplayList.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
layout/forms/nsComboboxControlFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/svg/base/src/nsSVGForeignObjectFrame.cpp
layout/svg/base/src/nsSVGForeignObjectFrame.h
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -454,17 +454,18 @@ static nsRect GetDisplayPortBounds(nsDis
 {
   nsIFrame* frame = aItem->GetUnderlyingFrame();
   const nsRect* displayport = aBuilder->GetDisplayPort();
 
   if (aIgnoreTransform) {
     return *displayport;
   }
 
-  return nsLayoutUtils::TransformRectToBoundsInAncestor(
+  // XXX this isn't correct, see bug 701190
+  return nsLayoutUtils::TransformAncestorRectToFrame(
            frame,
            nsRect(0, 0, displayport->width, displayport->height),
            aBuilder->ReferenceFrame());
 }
 
 bool
 nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
                                            nsRegion* aVisibleRegion,
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -970,17 +970,17 @@ nsLayoutUtils::GetEventCoordinatesRelati
   PRInt32 rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
   PRInt32 localAPD = aFrame->PresContext()->AppUnitsPerDevPixel();
   widgetToView = widgetToView.ConvertAppUnits(rootAPD, localAPD);
 
   /* 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)
-    return InvertTransformsToRoot(aFrame, widgetToView);
+    return TransformRootPointToFrame(aFrame, widgetToView);
 
   /* Otherwise, all coordinate systems are translations of one another,
    * so we can just subtract out the different.
    */
   return widgetToView - aFrame->GetOffsetToCrossDoc(rootFrame);
 }
 
 nsIFrame*
@@ -1112,93 +1112,106 @@ nsLayoutUtils::MatrixTransformPoint(cons
                                     const gfx3DMatrix &aMatrix, float aFactor)
 {
   gfxPoint image = aMatrix.Transform(gfxPoint(NSAppUnitsToFloatPixels(aPoint.x, aFactor),
                                               NSAppUnitsToFloatPixels(aPoint.y, aFactor)));
   return nsPoint(NSFloatPixelsToAppUnits(float(image.x), aFactor),
                  NSFloatPixelsToAppUnits(float(image.y), aFactor));
 }
 
-static gfxPoint 
-InvertTransformsToAncestor(nsIFrame *aFrame,
-                           const gfxPoint &aPoint,
-                           nsIFrame *aStopAtAncestor = nsnull)
+static gfx3DMatrix
+GetTransformToAncestor(nsIFrame *aFrame, nsIFrame *aAncestor)
 {
-  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
-
-  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
-   * the point.
-   */
-  nsIFrame *parent = nsnull;
-  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
-  gfxPoint result = aPoint;
-  
-  if (parent && parent != aStopAtAncestor) {
-      result = InvertTransformsToAncestor(parent, aPoint, aStopAtAncestor);
-  }
-
-  result = ctm.Inverse().ProjectPoint(result);
-  return result;
+  nsIFrame* parent;
+  gfx3DMatrix ctm = aFrame->GetTransformMatrix(aAncestor, &parent);
+  while (parent && parent != aAncestor) {
+    ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent);
+  }
+  return ctm;
+}
+
+static gfxPoint
+TransformGfxPointFromAncestor(nsIFrame *aFrame,
+                              const gfxPoint &aPoint,
+                              nsIFrame *aAncestor)
+{
+  gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor);
+  return ctm.Inverse().ProjectPoint(aPoint);
 }
 
 static gfxRect
-InvertGfxRectToAncestor(nsIFrame *aFrame,
-                     const gfxRect &aRect,
-                     nsIFrame *aStopAtAncestor = nsnull)
+TransformGfxRectFromAncestor(nsIFrame *aFrame,
+                             const gfxRect &aRect,
+                             nsIFrame *aAncestor)
 {
-  NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?");
-
-  /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform
-   * the point.
-   */
-  nsIFrame *parent = nsnull;
-  gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent);
-  gfxRect result = aRect;
-  
-  if (parent && parent != aStopAtAncestor) {
-      result = InvertGfxRectToAncestor(parent, aRect, aStopAtAncestor);
-  }
-
-  result = ctm.Inverse().ProjectRectBounds(result);
-  return result;
+  gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor);
+  return ctm.Inverse().ProjectRectBounds(aRect);
+}
+
+static gfxRect
+TransformGfxRectToAncestor(nsIFrame *aFrame,
+                           const gfxRect &aRect,
+                           nsIFrame *aAncestor)
+{
+  gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor);
+  return ctm.ProjectRectBounds(aRect);
 }
 
 nsPoint
-nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame,
-                                      const nsPoint &aPoint)
+nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame,
+                                         const nsPoint &aPoint)
 {
     float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
     gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
                     NSAppUnitsToFloatPixels(aPoint.y, factor));
     
-    result = InvertTransformsToAncestor(aFrame, result);
+    result = TransformGfxPointFromAncestor(aFrame, result, nsnull);
    
     return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor),
                    NSFloatPixelsToAppUnits(float(result.y), factor));
 }
 
 nsRect 
-nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame,
-                                               const nsRect &aRect,
-                                               nsIFrame* aStopAtAncestor)
+nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame,
+                                            const nsRect &aRect,
+                                            nsIFrame* aAncestor)
 {
     float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
     gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
                    NSAppUnitsToFloatPixels(aRect.y, factor),
                    NSAppUnitsToFloatPixels(aRect.width, factor),
                    NSAppUnitsToFloatPixels(aRect.height, factor));
 
-    result = InvertGfxRectToAncestor(aFrame, result, aStopAtAncestor);
+    result = TransformGfxRectFromAncestor(aFrame, result, aAncestor);
 
     return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
                   NSFloatPixelsToAppUnits(float(result.y), factor),
                   NSFloatPixelsToAppUnits(float(result.width), factor),
                   NSFloatPixelsToAppUnits(float(result.height), factor));
 }
 
+nsRect
+nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
+                                            const nsRect& aRect,
+                                            nsIFrame* aAncestor)
+{
+  float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
+  gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor),
+                 NSAppUnitsToFloatPixels(aRect.y, factor),
+                 NSAppUnitsToFloatPixels(aRect.width, factor),
+                 NSAppUnitsToFloatPixels(aRect.height, factor));
+
+  result = TransformGfxRectToAncestor(aFrame, result, aAncestor);
+
+  return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor),
+                NSFloatPixelsToAppUnits(float(result.y), factor),
+                NSFloatPixelsToAppUnits(float(result.width), factor),
+                NSFloatPixelsToAppUnits(float(result.height), factor));
+}
+
 static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) {
   nsIntPoint offset(0, 0);
   nsIWidget* parent = aWidget->GetParent();
   while (parent) {
     nsIntRect bounds;
     aWidget->GetBounds(bounds);
     offset += bounds.TopLeft();
     aWidget = parent;
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -510,33 +510,43 @@ public:
    * @param aIgnoreRootScrollFrame whether or not the display list builder
    * should ignore the root scroll frame.
    */
   static nsresult GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
                                    nsTArray<nsIFrame*> &aOutFrames,
                                    bool aShouldIgnoreSuppression = false,
                                    bool aIgnoreRootScrollFrame = false);
 
-  
+  /**
+   * Transform aRect relative to aAncestor down to the coordinate system of
+   * aFrame. Computes the bounding-box of the true quadrilateral.
+   */
+  static nsRect TransformAncestorRectToFrame(nsIFrame* aFrame,
+                                             const nsRect& aRect,
+                                             nsIFrame* aAncestor);
 
-  static nsRect TransformRectToBoundsInAncestor(nsIFrame* aFrame,
-                                                const nsRect& aRect,
-                                                nsIFrame* aStopAtAncestor);
+  /**
+   * Transform aRect relative to aFrame up to the coordinate system of
+   * aAncestor. Computes the bounding-box of the true quadrilateral.
+   */
+  static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame,
+                                             const nsRect& aRect,
+                                             nsIFrame* aAncestor);
 
   /**
    * Given a point in the global coordinate space, returns that point expressed
    * in the coordinate system of aFrame.  This effectively inverts all transforms
    * between this point and the root frame.
    *
    * @param aFrame The frame that acts as the coordinate space container.
    * @param aPoint The point, in the global space, to get in the frame-local space.
    * @return aPoint, expressed in aFrame's canonical coordinate space.
    */
-  static nsPoint InvertTransformsToRoot(nsIFrame* aFrame,
-                                        const nsPoint &aPt);
+  static nsPoint TransformRootPointToFrame(nsIFrame* aFrame,
+                                           const nsPoint &aPt);
 
   /**
    * Helper function that, given a rectangle and a matrix, returns the smallest
    * rectangle containing the image of the source rectangle.
    *
    * @param aBounds The rectangle to transform.
    * @param aMatrix The matrix to transform it with.
    * @param aFactor The number of app units per graphics unit.
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -545,18 +545,18 @@ nsComboboxControlFrame::ReflowDropdown(n
 
 nsPoint
 nsComboboxControlFrame::GetCSSTransformTranslation()
 {
   nsIFrame* frame = this;
   bool is3DTransform = false;
   gfxMatrix transform;
   while (frame) {
-    nsIFrame* parent = nsnull;
-    gfx3DMatrix ctm = frame->GetTransformMatrix(&parent);
+    nsIFrame* parent;
+    gfx3DMatrix ctm = frame->GetTransformMatrix(nsnull, &parent);
     gfxMatrix matrix;
     if (ctm.Is2D(&matrix)) {
       transform = transform * matrix;
     } else {
       is3DTransform = true;
       break;
     }
     frame = parent;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4536,29 +4536,31 @@ nsIFrame::InvalidateInternal(const nsRec
     InvalidateInternalAfterResize(r, 0, 0, aFlags);
     return;
   }
   
   InvalidateInternalAfterResize(aDamageRect, aX, aY, aFlags);
 }
 
 gfx3DMatrix
-nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
+nsIFrame::GetTransformMatrix(nsIFrame* aStopAtAncestor,
+                             nsIFrame** aOutAncestor)
 {
   NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
 
   /* If we're transformed, we want to hand back the combination
    * transform/translate matrix that will apply our current transform, then
    * shift us to our parent.
    */
   if (IsTransformed()) {
     /* Compute the delta to the parent, which we need because we are converting
      * coordinates to our parent.
      */
-    NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), "Cannot transform the viewport frame!");
+    NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
+	             "Cannot transform the viewport frame!");
     PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel();
 
     gfx3DMatrix result =
       nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
                                                       scaleFactor, nsnull, aOutAncestor);
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     /* Combine the raw transform with a translation to our parent. */
     result *= gfx3DMatrix::Translation
@@ -4577,17 +4579,17 @@ nsIFrame::GetTransformMatrix(nsIFrame **
    * we have to check to see if we have a parent.  If not, we'll set the
    * outparam to null (indicating that there's nothing left) and will hand back
    * the identity matrix.
    */
   if (!*aOutAncestor)
     return gfx3DMatrix();
   
   /* Keep iterating while the frame can't possibly be transformed. */
-  while (!(*aOutAncestor)->IsTransformed()) {
+  while (!(*aOutAncestor)->IsTransformed() && *aOutAncestor != aStopAtAncestor) {
     /* If no parent, stop iterating.  Otherwise, update the ancestor. */
     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
     if (!parent)
       break;
 
     *aOutAncestor = parent;
   }
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1927,27 +1927,30 @@ public:
   /**
    * Get the "type" of the frame. May return a NULL atom pointer
    *
    * @see nsGkAtoms
    */
   virtual nsIAtom* GetType() const = 0;
 
   /**
-   * Returns a transformation matrix that converts points in this frame's coordinate space
-   * to points in some ancestor frame's coordinate space.  The frame decides which ancestor
-   * it will use as a reference point.  If this frame has no ancestor, aOutAncestor will be
-   * set to null.
+   * Returns a transformation matrix that converts points in this frame's
+   * coordinate space to points in some ancestor frame's coordinate space.
+   * The frame decides which ancestor it will use as a reference point.
+   * If this frame has no ancestor, aOutAncestor will be set to null.
    *
-   * @param aOutAncestor [out] The ancestor frame the frame has chosen.  If this frame has no
-   *        ancestor, aOutAncestor will be nsnull.
-   * @return A gfxMatrix that converts points in this frame's coordinate space into
-   *         points in aOutAncestor's coordinate space.
+   * @param aStopAtAncestor don't look further than aStopAtAncestor. If null,
+   *   all ancestors (including across documents) will be traversed.
+   * @param aOutAncestor [out] The ancestor frame the frame has chosen.  If
+   *   this frame has no ancestor, *aOutAncestor will be set to null.
+   * @return A gfxMatrix that converts points in this frame's coordinate space
+   *   into points in aOutAncestor's coordinate space.
    */
-  virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor);
+  virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor,
+                                         nsIFrame **aOutAncestor);
 
   /**
    * Bit-flags to pass to IsFrameOfType()
    */
   enum {
     eMathML =                           1 << 0,
     eSVG =                              1 << 1,
     eSVGForeignObject =                 1 << 2,
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp
@@ -273,17 +273,18 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGR
                                           NS_RGBA(0,0,0,0), flags);
 
   gfx->Restore();
 
   return rv;
 }
 
 gfx3DMatrix
-nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
+nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor,
+                                            nsIFrame **aOutAncestor)
 {
   NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!");
 
   /* Set the ancestor to be the outer frame. */
   *aOutAncestor = nsSVGUtils::GetOuterSVGFrame(this);
   NS_ASSERTION(*aOutAncestor, "How did we end up without an outer frame?");
 
   /* Return the matrix back to the root, factoring in the x and y offsets. */
--- a/layout/svg/base/src/nsSVGForeignObjectFrame.h
+++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h
@@ -88,17 +88,18 @@ public:
   virtual bool IsTransformed() const
   {
     return true;
   }
 
   /**
    * Foreign objects can return a transform matrix.
    */
-  virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor);
+  virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aAncestor,
+                                         nsIFrame **aOutAncestor);
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::svgForeignObjectFrame
    */
   virtual nsIAtom* GetType() const;