Bug 841192. Part 2: Move FrameLayerBuilder::Clip to DisplayItemClip. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Mon, 04 Mar 2013 22:55:59 +1300
changeset 127797 698d23ec564bb1cbb8f4b97c2aa09779bd186ad0
parent 127796 1740631994daf663573763f3843b2e33c9850945
child 127798 fb7633e8733e0094329fa668b21738f6464e52cf
push id24512
push userryanvm@gmail.com
push dateFri, 05 Apr 2013 20:13:49 +0000
treeherdermozilla-central@139b6ba547fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs841192
milestone23.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 841192. Part 2: Move FrameLayerBuilder::Clip to DisplayItemClip. r=mattwoodrow
layout/base/DisplayItemClip.cpp
layout/base/DisplayItemClip.h
layout/base/FrameLayerBuilder.cpp
layout/base/FrameLayerBuilder.h
layout/base/Makefile.in
layout/base/MaskLayerImageCache.h
new file mode 100644
--- /dev/null
+++ b/layout/base/DisplayItemClip.cpp
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DisplayItemClip.h"
+
+#include "gfxContext.h"
+#include "nsPresContext.h"
+#include "nsDisplayList.h"
+#include "nsCSSRendering.h"
+#include "nsLayoutUtils.h"
+
+namespace mozilla {
+
+DisplayItemClip::DisplayItemClip(const DisplayItemClip& aOther, nsDisplayItem* aClipItem)
+  : mRoundedClipRects(aOther.mRoundedClipRects),
+    mHaveClipRect(true)
+{
+  nsDisplayItem::Type type = aClipItem->GetType();
+  NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
+                    type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
+                    "unexpected display item type");
+  nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
+  // Always intersect with mClipRect, even if we're going to add a
+  // rounded rect.
+  if (aOther.mHaveClipRect) {
+    mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
+  } else {
+    mClipRect = item->GetClipRect();
+  }
+
+  if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
+    RoundedRect *rr = mRoundedClipRects.AppendElement();
+    if (rr) {
+      rr->mRect = item->GetClipRect();
+      static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
+    }
+  }
+
+  // FIXME: Optimize away excess rounded rectangles due to the new addition.
+}
+
+void
+DisplayItemClip::ApplyTo(gfxContext* aContext,
+                         nsPresContext* aPresContext,
+                         uint32_t aBegin, uint32_t aEnd)
+{
+  int32_t A2D = aPresContext->AppUnitsPerDevPixel();
+  ApplyRectTo(aContext, A2D);
+  ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd);
+}
+
+void
+DisplayItemClip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
+{
+  aContext->NewPath();
+  gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
+  aContext->Rectangle(clip, true);
+  aContext->Clip();
+}
+
+void
+DisplayItemClip::ApplyRoundedRectsTo(gfxContext* aContext,
+                                     int32_t A2D,
+                                     uint32_t aBegin, uint32_t aEnd) const
+{
+  aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
+
+  for (uint32_t i = aBegin; i < aEnd; ++i) {
+    AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]);
+    aContext->Clip();
+  }
+}
+
+void
+DisplayItemClip::DrawRoundedRectsTo(gfxContext* aContext,
+                                    int32_t A2D,
+                                    uint32_t aBegin, uint32_t aEnd) const
+{
+  aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
+
+  if (aEnd - aBegin == 0)
+    return;
+
+  // If there is just one rounded rect we can just fill it, if there are more then we
+  // must clip the rest to get the intersection of clips
+  ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1);
+  AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]);
+  aContext->Fill();
+}
+
+void
+DisplayItemClip::AddRoundedRectPathTo(gfxContext* aContext,
+                                      int32_t A2D,
+                                      const RoundedRect &aRoundRect) const
+{
+  gfxCornerSizes pixelRadii;
+  nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii);
+
+  gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D);
+  clip.Round();
+  clip.Condition();
+
+  aContext->NewPath();
+  aContext->RoundedRectangle(clip, pixelRadii);
+}
+
+nsRect
+DisplayItemClip::ApproximateIntersect(const nsRect& aRect) const
+{
+  nsRect r = aRect;
+  if (mHaveClipRect) {
+    r.IntersectRect(r, mClipRect);
+  }
+  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
+       i < iEnd; ++i) {
+    const RoundedRect &rr = mRoundedClipRects[i];
+    nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
+    r = rgn.GetLargestRectangle();
+  }
+  return r;
+}
+
+// Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
+// and radii aXRadius, aYRadius.
+bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
+                     nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
+{
+  float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
+  float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
+  return scaledX * scaledX + scaledY * scaledY < 1.0f;
+}
+
+bool
+DisplayItemClip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
+{
+  if (mRoundedClipRects.IsEmpty())
+    return false;
+
+  nsRect rect;
+  rect.IntersectRect(aRect, NonRoundedIntersection());
+  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
+       i < iEnd; ++i) {
+    const RoundedRect &rr = mRoundedClipRects[i];
+    // top left
+    if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] &&
+        rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) {
+      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X],
+                           rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X],
+                           rect.x,
+                           rr.mRadii[NS_CORNER_TOP_LEFT_Y],
+                           rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y],
+                           rect.y)) {
+        return true;
+      }
+    }
+    // top right
+    if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] &&
+        rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) {
+      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X],
+                           rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X],
+                           rect.XMost(),
+                           rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
+                           rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
+                           rect.y)) {
+        return true;
+      }
+    }
+    // bottom left
+    if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] &&
+        rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) {
+      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
+                           rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
+                           rect.x,
+                           rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
+                           rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
+                           rect.YMost())) {
+        return true;
+      }
+    }
+    // bottom right
+    if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] &&
+        rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) {
+      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
+                           rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
+                           rect.XMost(),
+                           rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
+                           rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
+                           rect.YMost())) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+nsRect
+DisplayItemClip::NonRoundedIntersection() const
+{
+  NS_ASSERTION(mHaveClipRect, "Must have a clip rect!");
+  nsRect result = mClipRect;
+  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
+       i < iEnd; ++i) {
+    result.IntersectRect(result, mRoundedClipRects[i].mRect);
+  }
+  return result;
+}
+
+nsRect
+DisplayItemClip::ApplyNonRoundedIntersection(const nsRect& aRect) const
+{
+  if (!mHaveClipRect) {
+    return aRect;
+  }
+
+  nsRect result = aRect.Intersect(mClipRect);
+  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
+       i < iEnd; ++i) {
+    result.Intersect(mRoundedClipRects[i].mRect);
+  }
+  return result;
+}
+
+void
+DisplayItemClip::RemoveRoundedCorners()
+{
+  if (mRoundedClipRects.IsEmpty())
+    return;
+
+  mClipRect = NonRoundedIntersection();
+  mRoundedClipRects.Clear();
+}
+
+static void
+AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut)
+{
+  if (aR1.IsEqualInterior(aR2))
+    return;
+  nsRegion r;
+  r.Xor(aR1, aR2);
+  aOut->Or(*aOut, r);
+}
+
+void
+DisplayItemClip::AddOffsetAndComputeDifference(const nsPoint& aOffset,
+                                               const nsRect& aBounds,
+                                               const DisplayItemClip& aOther,
+                                               const nsRect& aOtherBounds,
+                                               nsRegion* aDifference)
+{
+  if (mHaveClipRect != aOther.mHaveClipRect ||
+      mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
+    aDifference->Or(*aDifference, aBounds);
+    aDifference->Or(*aDifference, aOtherBounds);
+    return;
+  }
+  if (mHaveClipRect) {
+    AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds),
+                             aOther.mClipRect.Intersect(aOtherBounds),
+                             aDifference);
+  }
+  for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
+    if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
+      // The corners make it tricky so we'll just add both rects here.
+      aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
+      aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
+    }
+  }
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/layout/base/DisplayItemClip.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DISPLAYITEMCLIP_H_
+#define DISPLAYITEMCLIP_H_
+
+#include "nsRect.h"
+#include "nsTArray.h"
+#include "nsStyleConsts.h"
+
+class gfxContext;
+class nsDisplayItem;
+class nsPresContext;
+class nsRegion;
+
+namespace mozilla {
+
+/**
+ * An DisplayItemClip represents the intersection of an optional rectangle
+ * with a list of rounded rectangles (which is often empty), all in appunits.
+ * It can represent everything CSS clipping can do to an element (except for
+ * SVG clip-path), including no clipping at all.
+ */
+struct DisplayItemClip {
+  struct RoundedRect {
+    nsRect mRect;
+    // Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
+    nscoord mRadii[8];
+
+    RoundedRect operator+(const nsPoint& aOffset) const {
+      RoundedRect r = *this;
+      r.mRect += aOffset;
+      return r;
+    }
+    bool operator==(const RoundedRect& aOther) const {
+      if (!mRect.IsEqualInterior(aOther.mRect)) {
+        return false;
+      }
+
+      NS_FOR_CSS_HALF_CORNERS(corner) {
+        if (mRadii[corner] != aOther.mRadii[corner]) {
+          return false;
+        }
+      }
+      return true;
+    }
+    bool operator!=(const RoundedRect& aOther) const {
+      return !(*this == aOther);
+    }
+  };
+  nsRect mClipRect;
+  nsTArray<RoundedRect> mRoundedClipRects;
+  bool mHaveClipRect;
+
+  // Constructs a DisplayItemClip that does no clipping at all.
+  DisplayItemClip() : mHaveClipRect(false) {}
+
+  // Construct as the intersection of aOther and aClipItem.
+  DisplayItemClip(const DisplayItemClip& aOther, nsDisplayItem* aClipItem);
+
+  // Apply this |DisplayItemClip| to the given gfxContext.  Any saving of state
+  // or clearing of other clips must be done by the caller.
+  // See aBegin/aEnd note on ApplyRoundedRectsTo.
+  void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext,
+               uint32_t aBegin = 0, uint32_t aEnd = UINT32_MAX);
+
+  void ApplyRectTo(gfxContext* aContext, int32_t A2D) const;
+  // Applies the rounded rects in this Clip to aContext
+  // Will only apply rounded rects from aBegin (inclusive) to aEnd
+  // (exclusive) or the number of rounded rects, whichever is smaller.
+  void ApplyRoundedRectsTo(gfxContext* aContext, int32_t A2DPRInt32,
+                           uint32_t aBegin, uint32_t aEnd) const;
+
+  // Draw (fill) the rounded rects in this clip to aContext
+  void DrawRoundedRectsTo(gfxContext* aContext, int32_t A2D,
+                          uint32_t aBegin, uint32_t aEnd) const;
+  // 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
+  void AddRoundedRectPathTo(gfxContext* aContext, int32_t A2D,
+                            const RoundedRect &aRoundRect) const;
+
+  // Return a rectangle contained in the intersection of aRect with this
+  // clip region. Tries to return the largest possible rectangle, but may
+  // not succeed.
+  nsRect ApproximateIntersect(const nsRect& aRect) const;
+
+  // Returns false if aRect is definitely not clipped by a rounded corner in
+  // this clip. Returns true if aRect is clipped by a rounded corner in this
+  // clip or it can not be quickly determined that it is not clipped by a
+  // rounded corner in this clip.
+  bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
+
+  // Intersection of all rects in this clip ignoring any rounded corners.
+  nsRect NonRoundedIntersection() const;
+
+  // Intersect the given rects with all rects in this clip, ignoring any
+  // rounded corners.
+  nsRect ApplyNonRoundedIntersection(const nsRect& aRect) const;
+
+  // Gets rid of any rounded corners in this clip.
+  void RemoveRoundedCorners();
+
+  // Adds the difference between Intersect(*this + aPoint, aBounds) and
+  // Intersect(aOther, aOtherBounds) to aDifference (or a bounding-box thereof).
+  void AddOffsetAndComputeDifference(const nsPoint& aPoint, const nsRect& aBounds,
+                                     const DisplayItemClip& aOther, const nsRect& aOtherBounds,
+                                     nsRegion* aDifference);
+
+  bool operator==(const DisplayItemClip& aOther) const {
+    return mHaveClipRect == aOther.mHaveClipRect &&
+           (!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
+           mRoundedClipRects == aOther.mRoundedClipRects;
+  }
+  bool operator!=(const DisplayItemClip& aOther) const {
+    return !(*this == aOther);
+  }
+};
+
+}
+
+#endif /* DISPLAYITEMCLIP_H_ */
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -270,17 +270,17 @@ public:
 
   /**
    * This is the method that actually walks a display list and builds
    * the child layers. We invoke it recursively to process clipped sublists.
    * @param aClipRect the clip rect to apply to the list items, or null
    * if no clipping is required
    */
   void ProcessDisplayItems(const nsDisplayList& aList,
-                           FrameLayerBuilder::Clip& aClip,
+                           DisplayItemClip& aClip,
                            uint32_t aFlags,
                            const nsIFrame* aForceActiveScrolledRoot = nullptr);
   /**
    * This finalizes all the open ThebesLayers by popping every element off
    * mThebesLayerDataStack, then sets the children of the container layer
    * to be all the layers in mNewChildLayers in that order and removes any
    * layers as children of the container that aren't in mNewChildLayers.
    * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
@@ -359,17 +359,17 @@ protected:
      * opaque rect that we can.
      * @param aSolidColor if non-null, the visible area of the item is
      * a constant color given by *aSolidColor
      */
     void Accumulate(ContainerState* aState,
                     nsDisplayItem* aItem,
                     const nsIntRect& aVisibleRect,
                     const nsIntRect& aDrawRect,
-                    const FrameLayerBuilder::Clip& aClip);
+                    const DisplayItemClip& aClip);
     const nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; }
 
     /**
      * If this represents only a nsDisplayImage, and the image type
      * supports being optimized to an ImageLayer (TYPE_RASTER only) returns
      * an ImageContainer for the image.
      */
     already_AddRefed<ImageContainer> CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder);
@@ -446,31 +446,31 @@ protected:
     /**
      * Stores the clip that we need to apply to the image or, if there is no
      * image, a clip for SOME item in the layer. There is no guarantee which
      * item's clip will be stored here and mItemClip should not be used to clip
      * the whole layer - only some part of the clip should be used, as determined
      * by ThebesDisplayItemLayerUserData::GetCommonClipCount() - which may even be
      * no part at all.
      */
-    FrameLayerBuilder::Clip mItemClip;
+    DisplayItemClip mItemClip;
     /**
      * The first mCommonClipCount rounded rectangle clips are identical for
      * all items in the layer.
      * -1 if there are no items in the layer; must be >=0 by the time that this
      * data is popped from the stack.
      */
     int32_t mCommonClipCount;
     /*
      * Updates mCommonClipCount by checking for rounded rect clips in common
      * between the clip on a new item (aCurrentClip) and the common clips
      * on items already in the layer (the first mCommonClipCount rounded rects
      * in mItemClip).
      */
-    void UpdateCommonClipCount(const FrameLayerBuilder::Clip& aCurrentClip);
+    void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip);
   };
   friend class ThebesLayerData;
 
   /**
    * Grab the next recyclable ThebesLayer, or create one if there are no
    * more recyclable ThebesLayers. Does any necessary invalidation of
    * a recycled ThebesLayer, and sets up the transform on the ThebesLayer
    * to account for scrolling.
@@ -501,17 +501,17 @@ protected:
   void CollectOldLayers();
   /**
    * If aItem used to belong to a ThebesLayer, invalidates the area of
    * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of
    * aItem in that layer.
    */
   void InvalidateForLayerChange(nsDisplayItem* aItem, 
                                 Layer* aNewLayer,
-                                const FrameLayerBuilder::Clip& aClip,
+                                const DisplayItemClip& aClip,
                                 const nsPoint& aTopLeft,
                                 nsDisplayItemGeometry *aGeometry);
   /**
    * Try to determine whether the ThebesLayer at aThebesLayerIndex
    * has a single opaque color behind it, over the entire bounds of its visible
    * region.
    * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
    */
@@ -537,17 +537,17 @@ protected:
    * display item
    * @param aOpaqueRect if non-null, a region of the display item that is opaque
    * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect
    * will be painted with aSolidColor by the item
    */
   ThebesLayerData* FindThebesLayerFor(nsDisplayItem* aItem,
                                                    const nsIntRect& aVisibleRect,
                                                    const nsIntRect& aDrawRect,
-                                                   const FrameLayerBuilder::Clip& aClip,
+                                                   const DisplayItemClip& aClip,
                                                    const nsIFrame* aActiveScrolledRoot,
                                                    const nsPoint& aTopLeft);
   ThebesLayerData* GetTopThebesLayerData()
   {
     return mThebesLayerDataStack.IsEmpty() ? nullptr
         : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get();
   }
 
@@ -555,17 +555,17 @@ protected:
    * there is no clipping specified or a mask layer cannot be built.
    * Builds an ImageLayer for the appropriate backend; the mask is relative to
    * aLayer's visible region.
    * aLayer is the layer to be clipped.
    * aRoundedRectClipCount is used when building mask layers for ThebesLayers,
    * SetupMaskLayer will build a mask layer for only the first
    * aRoundedRectClipCount rounded rects in aClip
    */
-  void SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
+  void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
                       uint32_t aRoundedRectClipCount = UINT32_MAX);
 
   bool ChooseActiveScrolledRoot(const nsDisplayList& aList,
                                 const nsIFrame **aActiveScrolledRoot);
 
   nsDisplayListBuilder*            mBuilder;
   LayerManager*                    mManager;
   FrameLayerBuilder*               mLayerBuilder;
@@ -675,17 +675,17 @@ struct MaskLayerUserData : public LayerU
            mScaleX == aOther.mScaleX &&
            mScaleY == aOther.mScaleY &&
            mOffset == aOther.mOffset;
   }
 
   nsRefPtr<const MaskLayerImageCache::MaskLayerImageKey> mImageKey;
   // properties of the mask layer; the mask layer may be re-used if these
   // remain unchanged.
-  nsTArray<FrameLayerBuilder::Clip::RoundedRect> mRoundedClipRects;
+  nsTArray<DisplayItemClip::RoundedRect> mRoundedClipRects;
   // scale from the masked layer which is applied to the mask
   float mScaleX, mScaleY;
   // The ContainerParameters offset which is applied to the mask's transform.
   nsIntPoint mOffset;
 };
 
 /**
  * The address of gThebesDisplayItemLayerUserData is used as the user
@@ -832,17 +832,17 @@ InvalidatePostTransformRegion(ThebesLaye
   nsAutoCString str;
   AppendToString(str, rgn);
   printf("Invalidating layer %p: %s\n", aLayer, str.get());
 #endif
 }
 
 static void
 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsRect& aRect, 
-                              const FrameLayerBuilder::Clip& aClip,
+                              const DisplayItemClip& aClip,
                               const nsIntPoint& aTranslation)
 {
   ThebesDisplayItemLayerUserData* data =
       static_cast<ThebesDisplayItemLayerUserData*>(aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
 
   nsRect rect = aClip.ApplyNonRoundedIntersection(aRect);
 
   nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel);
@@ -1122,17 +1122,17 @@ FrameLayerBuilder::GetOldLayerForFrame(n
     return data;
   }
   return nullptr;
 }
 
 Layer*
 FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem, 
                                   nsDisplayItemGeometry** aOldGeometry, 
-                                  Clip** aOldClip,
+                                  DisplayItemClip** aOldClip,
                                   nsTArray<nsIFrame*>* aChangedFrames,
                                   bool *aIsInvalid)
 {
   uint32_t key = aItem->GetPerFrameKey();
   nsIFrame* frame = aItem->GetUnderlyingFrame();
 
   if (frame) {
     DisplayItemData* oldData = GetOldLayerForFrame(frame, key);
@@ -1515,17 +1515,17 @@ ContainerState::FindOpaqueBackgroundColo
     }
     break;
   }
   return NS_RGBA(0,0,0,0);
 }
 
 void
 ContainerState::ThebesLayerData::UpdateCommonClipCount(
-    const FrameLayerBuilder::Clip& aCurrentClip)
+    const DisplayItemClip& aCurrentClip)
 {
   if (mCommonClipCount >= 0) {
     int32_t end = std::min<int32_t>(aCurrentClip.mRoundedClipRects.Length(),
                                   mCommonClipCount);
     int32_t clipCount = 0;
     for (; clipCount < end; ++clipCount) {
       if (mItemClip.mRoundedClipRects[clipCount] !=
           aCurrentClip.mRoundedClipRects[clipCount]) {
@@ -1735,17 +1735,17 @@ WindowHasTransparency(nsDisplayListBuild
   return windowTransparentRegion && !windowTransparentRegion->IsEmpty();
 }
 
 void
 ContainerState::ThebesLayerData::Accumulate(ContainerState* aState,
                                             nsDisplayItem* aItem,
                                             const nsIntRect& aVisibleRect,
                                             const nsIntRect& aDrawRect,
-                                            const FrameLayerBuilder::Clip& aClip)
+                                            const DisplayItemClip& aClip)
 {
   if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
     mForceTransparentSurface = true;
   }
   if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
     // Disable component alpha.
     // Note that the transform (if any) on the ThebesLayer is always an integer translation so
     // we don't have to factor that in here.
@@ -1862,17 +1862,17 @@ ContainerState::ThebesLayerData::Accumul
     }
   }
 }
 
 ContainerState::ThebesLayerData*
 ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
                                    const nsIntRect& aVisibleRect,
                                    const nsIntRect& aDrawRect,
-                                   const FrameLayerBuilder::Clip& aClip,
+                                   const DisplayItemClip& aClip,
                                    const nsIFrame* aActiveScrolledRoot,
                                    const nsPoint& aTopLeft)
 {
   int32_t i;
   int32_t lowestUsableLayerWithScrolledRoot = -1;
   int32_t topmostLayerWithScrolledRoot = -1;
   for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) {
     ThebesLayerData* data = mThebesLayerDataStack[i];
@@ -2046,17 +2046,17 @@ ContainerState::ChooseActiveScrolledRoot
  * (ThebesLayers don't need a clip rect on the layer, we clip the items
  * individually when we draw them.)
  * We set the visible rect for all layers, although the actual setting
  * of visible rects for some ThebesLayers is deferred until the calling
  * of ContainerState::Finish.
  */
 void
 ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
-                                    FrameLayerBuilder::Clip& aClip,
+                                    DisplayItemClip& aClip,
                                     uint32_t aFlags,
                                     const nsIFrame* aForceActiveScrolledRoot)
 {
   PROFILER_LABEL("ContainerState", "ProcessDisplayItems");
 
   const nsIFrame* lastActiveScrolledRoot = nullptr;
   nsPoint topLeft;
 
@@ -2072,17 +2072,17 @@ ContainerState::ProcessDisplayItems(cons
 
     topLeft = lastActiveScrolledRoot->GetOffsetToCrossDoc(mContainerReferenceFrame);
   }
 
   for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
     nsDisplayItem::Type type = item->GetType();
     if (type == nsDisplayItem::TYPE_CLIP ||
         type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
-      FrameLayerBuilder::Clip childClip(aClip, item);
+      DisplayItemClip childClip(aClip, item);
       ProcessDisplayItems(*item->GetSameCoordinateSystemChildren(), childClip, aFlags, lastActiveScrolledRoot);
       continue;
     }
 
     NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
       "items in a container layer should all have the same app units per dev pixel");
 
     nsIntRect itemVisibleRect =
@@ -2266,18 +2266,18 @@ ContainerState::ProcessDisplayItems(cons
  *
  * @param aClip Current clip
  * @param aOldClip Optional clip from previous paint.
  * @param aShift Offet to apply to aOldClip
  * @param aCombined Outparam - Computed clip region
  * @return True if the clip should be applied, false
  *         otherwise.
  */
-static bool ComputeCombinedClip(const FrameLayerBuilder::Clip& aClip,
-                                FrameLayerBuilder::Clip* aOldClip,
+static bool ComputeCombinedClip(const DisplayItemClip& aClip,
+                                DisplayItemClip* aOldClip,
                                 const nsPoint& aShift,
                                 nsRegion& aCombined)
 {
   if (!aClip.mHaveClipRect ||
       (aOldClip && !aOldClip->mHaveClipRect)) {
     return false;
   }
 
@@ -2289,26 +2289,26 @@ static bool ComputeCombinedClip(const Fr
     aCombined = aClip.NonRoundedIntersection();
   }
   return true;
 }
 
 void
 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, 
                                          Layer* aNewLayer,
-                                         const FrameLayerBuilder::Clip& aClip,
+                                         const DisplayItemClip& aClip,
                                          const nsPoint& aTopLeft,
                                          nsDisplayItemGeometry *aGeometry)
 {
   NS_ASSERTION(aItem->GetUnderlyingFrame(),
                "Display items that render using Thebes must have a frame");
   NS_ASSERTION(aItem->GetPerFrameKey(),
                "Display items that render using Thebes must have a key");
   nsDisplayItemGeometry *oldGeometry = NULL;
-  FrameLayerBuilder::Clip* oldClip = NULL;
+  DisplayItemClip* oldClip = NULL;
   nsAutoTArray<nsIFrame*,4> changedFrames;
   bool isInvalid = false;
   Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip, &changedFrames, &isInvalid);
   if (aNewLayer != oldLayer && oldLayer) {
     // The item has changed layers.
     // Invalidate the old bounds in the old layer and new bounds in the new layer.
     ThebesLayer* t = oldLayer->AsThebesLayer();
     if (t) {
@@ -2400,17 +2400,17 @@ ContainerState::InvalidateForLayerChange
         combined.ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
         GetTranslationForThebesLayer(newThebesLayer));
   }
 }
 
 void
 FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
                                         nsDisplayItem* aItem,
-                                        const Clip& aClip,
+                                        const DisplayItemClip& aClip,
                                         nsIFrame* aContainerLayerFrame,
                                         LayerState aLayerState,
                                         const nsPoint& aTopLeft,
                                         nsAutoPtr<nsDisplayItemGeometry> aGeometry)
 {
   ThebesDisplayItemLayerUserData* thebesData =
     static_cast<ThebesDisplayItemLayerUserData*>(aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
   nsRefPtr<LayerManager> tempManager;
@@ -2422,17 +2422,17 @@ FrameLayerBuilder::AddThebesDisplayItem(
       tempManager = data->mInactiveManager;
     }
     if (!tempManager) {
       tempManager = new BasicLayerManager();
     }
 
     // We need to grab these before calling AddLayerDisplayItem because it will overwrite them.
     nsRegion clip;
-    FrameLayerBuilder::Clip* oldClip = nullptr;
+    DisplayItemClip* oldClip = nullptr;
     GetOldLayerFor(aItem, nullptr, &oldClip);
     hasClip = ComputeCombinedClip(aClip, oldClip, 
                                   aTopLeft - thebesData->mLastActiveScrolledRootOrigin,
                                   clip);
 
     if (hasClip) {
       intClip = clip.GetBounds().ScaleToOutsidePixels(thebesData->mXScale, 
                                                       thebesData->mYScale, 
@@ -2580,17 +2580,17 @@ FrameLayerBuilder::ClippedDisplayItem::~
     }
     basic->SetUserData(&gLayerManagerLayerBuilder, nullptr);
   }
 }
 
 void
 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
                                        nsDisplayItem* aItem,
-                                       const Clip& aClip,
+                                       const DisplayItemClip& aClip,
                                        LayerState aLayerState,
                                        const nsPoint& aTopLeft,
                                        LayerManager* aManager,
                                        nsAutoPtr<nsDisplayItemGeometry> aGeometry)
 {
   if (aLayer->Manager() != mRetainingManager)
     return;
 
@@ -2957,17 +2957,17 @@ FrameLayerBuilder::BuildContainerLayerFo
     stateFlags = ContainerState::NO_COMPONENT_ALPHA;
   }
   uint32_t flags;
   while (true) {
     ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
                          aContainerFrame, aContainerItem,
                          containerLayer, scaleParameters);
     
-    Clip clip;
+    DisplayItemClip clip;
     state.ProcessDisplayItems(aChildren, clip, stateFlags);
 
     // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
     // This is suboptimal ... a child could have text that's over transparent
     // pixels in its own layer, but over opaque parts of previous siblings.
     state.Finish(&flags, data);
     bounds = state.GetChildrenBounds();
     pixBounds = state.ScaleToOutsidePixels(bounds, false);
@@ -3315,17 +3315,17 @@ FrameLayerBuilder::DrawThebesLayer(Thebe
     if (!cdi->mClip.IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
       cdi->mClip.RemoveRoundedCorners();
     }
   }
 
   nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
   rc->Init(presContext->DeviceContext(), aContext);
 
-  Clip currentClip;
+  DisplayItemClip currentClip;
   bool setClipRect = false;
 
   for (i = 0; i < items.Length(); ++i) {
     ClippedDisplayItem* cdi = &items[i];
 
     if (cdi->mItem->GetVisibleRect().IsEmpty())
       continue;
 
@@ -3409,274 +3409,18 @@ FrameLayerBuilder::CheckDOMModified()
 #ifdef MOZ_DUMP_PAINTING
 /* static */ void
 FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, FILE* aFile, bool aDumpHtml)
 {
   aManager->Dump(aFile, "", aDumpHtml);
 }
 #endif
 
-FrameLayerBuilder::Clip::Clip(const Clip& aOther, nsDisplayItem* aClipItem)
-  : mRoundedClipRects(aOther.mRoundedClipRects),
-    mHaveClipRect(true)
-{
-  nsDisplayItem::Type type = aClipItem->GetType();
-  NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
-                    type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
-                    "unexpected display item type");
-  nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
-  // Always intersect with mClipRect, even if we're going to add a
-  // rounded rect.
-  if (aOther.mHaveClipRect) {
-    mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
-  } else {
-    mClipRect = item->GetClipRect();
-  }
-
-  if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
-    RoundedRect *rr = mRoundedClipRects.AppendElement();
-    if (rr) {
-      rr->mRect = item->GetClipRect();
-      static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
-    }
-  }
-
-  // FIXME: Optimize away excess rounded rectangles due to the new addition.
-}
-
-void
-FrameLayerBuilder::Clip::ApplyTo(gfxContext* aContext,
-                                 nsPresContext* aPresContext,
-                                 uint32_t aBegin, uint32_t aEnd)
-{
-  int32_t A2D = aPresContext->AppUnitsPerDevPixel();
-  ApplyRectTo(aContext, A2D);
-  ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd);
-}
-
-void
-FrameLayerBuilder::Clip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
-{
-  aContext->NewPath();
-  gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
-  aContext->Rectangle(clip, true);
-  aContext->Clip();
-}
-
-void
-FrameLayerBuilder::Clip::ApplyRoundedRectsTo(gfxContext* aContext,
-                                             int32_t A2D,
-                                             uint32_t aBegin, uint32_t aEnd) const
-{
-  aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
-
-  for (uint32_t i = aBegin; i < aEnd; ++i) {
-    AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]);
-    aContext->Clip();
-  }
-}
-
-void
-FrameLayerBuilder::Clip::DrawRoundedRectsTo(gfxContext* aContext,
-                                            int32_t A2D,
-                                            uint32_t aBegin, uint32_t aEnd) const
-{
-  aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
-
-  if (aEnd - aBegin == 0)
-    return;
-
-  // If there is just one rounded rect we can just fill it, if there are more then we
-  // must clip the rest to get the intersection of clips
-  ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1);
-  AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]);
-  aContext->Fill();
-}
-
-void
-FrameLayerBuilder::Clip::AddRoundedRectPathTo(gfxContext* aContext,
-                                              int32_t A2D,
-                                              const RoundedRect &aRoundRect) const
-{
-  gfxCornerSizes pixelRadii;
-  nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii);
-
-  gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D);
-  clip.Round();
-  clip.Condition();
-
-  aContext->NewPath();
-  aContext->RoundedRectangle(clip, pixelRadii);
-}
-
-nsRect
-FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const
-{
-  nsRect r = aRect;
-  if (mHaveClipRect) {
-    r.IntersectRect(r, mClipRect);
-  }
-  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
-       i < iEnd; ++i) {
-    const Clip::RoundedRect &rr = mRoundedClipRects[i];
-    nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
-    r = rgn.GetLargestRectangle();
-  }
-  return r;
-}
-
-// Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
-// and radii aXRadius, aYRadius.
-bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
-                     nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
-{
-  float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
-  float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
-  return scaledX * scaledX + scaledY * scaledY < 1.0f;
-}
-
-bool
-FrameLayerBuilder::Clip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
-{
-  if (mRoundedClipRects.IsEmpty())
-    return false;
-
-  nsRect rect;
-  rect.IntersectRect(aRect, NonRoundedIntersection());
-  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
-       i < iEnd; ++i) {
-    const Clip::RoundedRect &rr = mRoundedClipRects[i];
-    // top left
-    if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] &&
-        rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) {
-      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X],
-                           rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X],
-                           rect.x,
-                           rr.mRadii[NS_CORNER_TOP_LEFT_Y],
-                           rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y],
-                           rect.y)) {
-        return true;
-      }
-    }
-    // top right
-    if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] &&
-        rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) {
-      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X],
-                           rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X],
-                           rect.XMost(),
-                           rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
-                           rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
-                           rect.y)) {
-        return true;
-      }
-    }
-    // bottom left
-    if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] &&
-        rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) {
-      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
-                           rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
-                           rect.x,
-                           rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
-                           rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
-                           rect.YMost())) {
-        return true;
-      }
-    }
-    // bottom right
-    if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] &&
-        rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) {
-      if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
-                           rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
-                           rect.XMost(),
-                           rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
-                           rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
-                           rect.YMost())) {
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-nsRect
-FrameLayerBuilder::Clip::NonRoundedIntersection() const
-{
-  NS_ASSERTION(mHaveClipRect, "Must have a clip rect!");
-  nsRect result = mClipRect;
-  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
-       i < iEnd; ++i) {
-    result.IntersectRect(result, mRoundedClipRects[i].mRect);
-  }
-  return result;
-}
-
-nsRect
-FrameLayerBuilder::Clip::ApplyNonRoundedIntersection(const nsRect& aRect) const
-{
-  if (!mHaveClipRect) {
-    return aRect;
-  }
-
-  nsRect result = aRect.Intersect(mClipRect);
-  for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
-       i < iEnd; ++i) {
-    result.Intersect(mRoundedClipRects[i].mRect);
-  }
-  return result;
-}
-
-void
-FrameLayerBuilder::Clip::RemoveRoundedCorners()
-{
-  if (mRoundedClipRects.IsEmpty())
-    return;
-
-  mClipRect = NonRoundedIntersection();
-  mRoundedClipRects.Clear();
-}
-
-static void
-AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut)
-{
-  if (aR1.IsEqualInterior(aR2))
-    return;
-  nsRegion r;
-  r.Xor(aR1, aR2);
-  aOut->Or(*aOut, r);
-}
-
-void
-FrameLayerBuilder::Clip::AddOffsetAndComputeDifference(const nsPoint& aOffset,
-                                                       const nsRect& aBounds,
-                                                       const Clip& aOther,
-                                                       const nsRect& aOtherBounds,
-                                                       nsRegion* aDifference)
-{
-  if (mHaveClipRect != aOther.mHaveClipRect ||
-      mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
-    aDifference->Or(*aDifference, aBounds);
-    aDifference->Or(*aDifference, aOtherBounds);
-    return;
-  }
-  if (mHaveClipRect) {
-    AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds),
-                             aOther.mClipRect.Intersect(aOtherBounds),
-                             aDifference);
-  }
-  for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
-    if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
-      // The corners make it tricky so we'll just add both rects here.
-      aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
-      aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
-    }
-  }
-}
-
 gfxRect
-CalculateBounds(const nsTArray<FrameLayerBuilder::Clip::RoundedRect>& aRects, int32_t A2D)
+CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t A2D)
 {
   nsRect bounds = aRects[0].mRect;
   for (uint32_t i = 1; i < aRects.Length(); ++i) {
     bounds.UnionRect(bounds, aRects[i].mRect);
    }
  
   return nsLayoutUtils::RectToGfxRect(bounds, A2D);
 }
@@ -3686,17 +3430,17 @@ SetClipCount(ThebesDisplayItemLayerUserD
              uint32_t aClipCount)
 {
   if (aThebesData) {
     aThebesData->mMaskClipCount = aClipCount;
   }
 }
 
 void
-ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
+ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip,
                                uint32_t aRoundedRectClipCount)
 {
   // if the number of clips we are going to mask has decreased, then aLayer might have
   // cached graphics which assume the existence of a soon-to-be non-existent mask layer
   // in that case, invalidate the whole layer.
   ThebesDisplayItemLayerUserData* thebesData = GetThebesDisplayItemLayerUserData(aLayer);
   if (thebesData &&
       aRoundedRectClipCount < thebesData->mMaskClipCount) {
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -9,16 +9,17 @@
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "nsTArray.h"
 #include "nsRegion.h"
 #include "nsIFrame.h"
 #include "nsDisplayListInvalidation.h"
 #include "LayerTreeInvalidation.h"
 #include "ImageLayers.h"
+#include "DisplayItemClip.h"
 
 class nsDisplayListBuilder;
 class nsDisplayList;
 class nsDisplayItem;
 class gfxContext;
 class nsRootPresContext;
 
 namespace mozilla {
@@ -265,35 +266,34 @@ public:
    *
    * @param aLayer Layer that the display item will be rendered into
    * @param aItem Display item to be drawn.
    * @param aLayerState What LayerState the item is using.
    * @param aTopLeft offset from active scrolled root to reference frame
    * @param aManager If the layer is in the LAYER_INACTIVE state,
    * then this is the temporary layer manager to draw with.
    */
-  struct Clip;
   void AddLayerDisplayItem(Layer* aLayer,
                            nsDisplayItem* aItem,
-                           const Clip& aClip,
+                           const DisplayItemClip& aClip,
                            LayerState aLayerState,
                            const nsPoint& aTopLeft,
                            LayerManager* aManager,
                            nsAutoPtr<nsDisplayItemGeometry> aGeometry);
 
   /**
    * Record aItem as a display item that is rendered by the ThebesLayer
    * aLayer, with aClipRect, where aContainerLayerFrame is the frame
    * for the container layer this ThebesItem belongs to.
    * aItem must have an underlying frame.
    * @param aTopLeft offset from active scrolled root to reference frame
    */
   void AddThebesDisplayItem(ThebesLayer* aLayer,
                             nsDisplayItem* aItem,
-                            const Clip& aClip,
+                            const DisplayItemClip& aClip,
                             nsIFrame* aContainerLayerFrame,
                             LayerState aLayerState,
                             const nsPoint& aTopLeft,
                             nsAutoPtr<nsDisplayItemGeometry> aGeometry);
 
   /**
    * Gets the frame property descriptor for the given manager, or for the current
    * widget layer manager if nullptr is passed.
@@ -302,17 +302,17 @@ public:
 
   /**
    * Calls GetOldLayerForFrame on the underlying frame of the display item,
    * and each subsequent merged frame if no layer is found for the underlying
    * frame.
    */
   Layer* GetOldLayerFor(nsDisplayItem* aItem, 
                         nsDisplayItemGeometry** aOldGeometry = nullptr, 
-                        Clip** aOldClip = nullptr,
+                        DisplayItemClip** aOldClip = nullptr,
                         nsTArray<nsIFrame*>* aChangedFrames = nullptr,
                         bool *aIsInvalid = nullptr);
 
   static Layer* GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey);
 
   /**
    * Destroy any stored LayerManagerDataProperty and the associated data for
    * aFrame.
@@ -356,113 +356,16 @@ public:
 
   /**
    * Stores a Layer as the dedicated layer in the DisplayItemData for a given frame/key pair.
    *
    * Used when we optimize a ThebesLayer into an ImageLayer and want to retroactively update the 
    * DisplayItemData so we can retrieve the layer from within layout.
    */
   void StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer);
-
-  /**
-   * Clip represents the intersection of an optional rectangle with a
-   * list of rounded rectangles.
-   */
-  struct Clip {
-    struct RoundedRect {
-      nsRect mRect;
-      // Indices into mRadii are the NS_CORNER_* constants in nsStyleConsts.h
-      nscoord mRadii[8];
-
-      RoundedRect operator+(const nsPoint& aOffset) const {
-        RoundedRect r = *this;
-        r.mRect += aOffset;
-        return r;
-      }
-      bool operator==(const RoundedRect& aOther) const {
-        if (!mRect.IsEqualInterior(aOther.mRect)) {
-          return false;
-        }
-
-        NS_FOR_CSS_HALF_CORNERS(corner) {
-          if (mRadii[corner] != aOther.mRadii[corner]) {
-            return false;
-          }
-        }
-        return true;
-      }
-      bool operator!=(const RoundedRect& aOther) const {
-        return !(*this == aOther);
-      }
-    };
-    nsRect mClipRect;
-    nsTArray<RoundedRect> mRoundedClipRects;
-    bool mHaveClipRect;
-
-    Clip() : mHaveClipRect(false) {}
-
-    // Construct as the intersection of aOther and aClipItem.
-    Clip(const Clip& aOther, nsDisplayItem* aClipItem);
-
-    // Apply this |Clip| to the given gfxContext.  Any saving of state
-    // or clearing of other clips must be done by the caller.
-    // See aBegin/aEnd note on ApplyRoundedRectsTo.
-    void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext,
-                 uint32_t aBegin = 0, uint32_t aEnd = UINT32_MAX);
-
-    void ApplyRectTo(gfxContext* aContext, int32_t A2D) const;
-    // Applies the rounded rects in this Clip to aContext
-    // Will only apply rounded rects from aBegin (inclusive) to aEnd
-    // (exclusive) or the number of rounded rects, whichever is smaller.
-    void ApplyRoundedRectsTo(gfxContext* aContext, int32_t A2DPRInt32,
-                             uint32_t aBegin, uint32_t aEnd) const;
-
-    // Draw (fill) the rounded rects in this clip to aContext
-    void DrawRoundedRectsTo(gfxContext* aContext, int32_t A2D,
-                            uint32_t aBegin, uint32_t aEnd) const;
-    // 'Draw' (create as a path, does not stroke or fill) aRoundRect to aContext
-    void AddRoundedRectPathTo(gfxContext* aContext, int32_t A2D,
-                              const RoundedRect &aRoundRect) const;
-
-    // Return a rectangle contained in the intersection of aRect with this
-    // clip region. Tries to return the largest possible rectangle, but may
-    // not succeed.
-    nsRect ApproximateIntersect(const nsRect& aRect) const;
-
-    // Returns false if aRect is definitely not clipped by a rounded corner in
-    // this clip. Returns true if aRect is clipped by a rounded corner in this
-    // clip or it can not be quickly determined that it is not clipped by a
-    // rounded corner in this clip.
-    bool IsRectClippedByRoundedCorner(const nsRect& aRect) const;
-
-    // Intersection of all rects in this clip ignoring any rounded corners.
-    nsRect NonRoundedIntersection() const;
-
-    // Intersect the given rects with all rects in this clip, ignoring any
-    // rounded corners.
-    nsRect ApplyNonRoundedIntersection(const nsRect& aRect) const;
-
-    // Gets rid of any rounded corners in this clip.
-    void RemoveRoundedCorners();
-
-    // Adds the difference between Intersect(*this + aPoint, aBounds) and
-    // Intersect(aOther, aOtherBounds) to aDifference.
-    void AddOffsetAndComputeDifference(const nsPoint& aPoint, const nsRect& aBounds,
-                                       const Clip& aOther, const nsRect& aOtherBounds,
-                                       nsRegion* aDifference);
-
-    bool operator==(const Clip& aOther) const {
-      return mHaveClipRect == aOther.mHaveClipRect &&
-             (!mHaveClipRect || mClipRect.IsEqualInterior(aOther.mClipRect)) &&
-             mRoundedClipRects == aOther.mRoundedClipRects;
-    }
-    bool operator!=(const Clip& aOther) const {
-      return !(*this == aOther);
-    }
-  };
   
   NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(LayerManagerDataProperty,
                                                RemoveFrameFromLayerManager)
 
   /**
    * Retained data storage:
    *
    * Each layer manager (widget, and inactive) stores a LayerManagerData object
@@ -518,17 +421,17 @@ public:
                         uint32_t aContainerLayerGeneration, nsDisplayItem* aItem = nullptr);
 
     LayerManagerData* mParent;
     nsRefPtr<Layer> mLayer;
     nsRefPtr<Layer> mOptLayer;
     nsRefPtr<LayerManager> mInactiveManager;
     nsAutoTArray<nsIFrame*, 1> mFrameList;
     nsAutoPtr<nsDisplayItemGeometry> mGeometry;
-    Clip            mClip;
+    DisplayItemClip mClip;
     uint32_t        mDisplayItemKey;
     uint32_t        mContainerLayerGeneration;
     LayerState      mLayerState;
 
     /**
      * Used to track if data currently stored in mFramesWithLayers (from an existing
      * paint) has been updated in the current paint.
      */
@@ -595,33 +498,33 @@ protected:
    * of ClippedDisplayItems. (ThebesLayerItemsEntry is the hash entry
    * for that hashtable.)
    * These are only stored during the paint process, so that the
    * DrawThebesLayer callback can figure out which items to draw for the
    * ThebesLayer.
    * mItem always has an underlying frame.
    */
   struct ClippedDisplayItem {
-    ClippedDisplayItem(nsDisplayItem* aItem, const Clip& aClip, uint32_t aGeneration)
+    ClippedDisplayItem(nsDisplayItem* aItem, const DisplayItemClip& aClip, uint32_t aGeneration)
       : mItem(aItem), mClip(aClip), mContainerLayerGeneration(aGeneration)
     {
     }
 
     ~ClippedDisplayItem();
 
     nsDisplayItem* mItem;
 
     /**
      * If the display item is being rendered as an inactive
      * layer, then this stores the layer manager being
      * used for the inactive transaction.
      */
     nsRefPtr<LayerManager> mInactiveLayerManager;
 
-    Clip mClip;
+    DisplayItemClip mClip;
     uint32_t mContainerLayerGeneration;
   };
 
   /**
    * We accumulate ClippedDisplayItem elements in a hashtable during
    * the paint process. This is the hashentry for that hashtable.
    */
 public:
--- a/layout/base/Makefile.in
+++ b/layout/base/Makefile.in
@@ -53,20 +53,20 @@ EXPORTS		= \
 		StackArena.h \
 		$(NULL)
 
 EXPORTS_mozilla = \
   PaintTracker.h \
   $(NULL)
 
 CPPSRCS		= \
+		DisplayItemClip.cpp \
 		FrameLayerBuilder.cpp \
+		FramePropertyTable.cpp \
 		MaskLayerImageCache.cpp \
-		FramePropertyTable.cpp \
-		RestyleTracker.cpp \
 		nsCSSColorUtils.cpp \
 		nsCSSFrameConstructor.cpp \
 		nsCSSRendering.cpp \
 		nsCSSRenderingBorders.cpp \
 		nsCaret.cpp \
 		nsChildIterator.cpp \
 		nsCounterManager.cpp \
 		nsDisplayList.cpp \
@@ -82,16 +82,17 @@ CPPSRCS		= \
 		nsPresContext.cpp \
 		nsPresShell.cpp \
 		nsQuoteList.cpp \
 		nsRefreshDriver.cpp \
 		nsStyleChangeList.cpp \
 		nsStyleSheetService.cpp \
 		PaintTracker.cpp \
  		PositionedEventTargeting.cpp \
+		RestyleTracker.cpp \
 		StackArena.cpp \
 		$(NULL)
 
 ifndef MOZ_XUL
 EXPORTS    += \
 		nsPIBoxObject.h \
 		$(NULL)
 CPPSRCS    += \
--- a/layout/base/MaskLayerImageCache.h
+++ b/layout/base/MaskLayerImageCache.h
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MASKLAYERIMAGECACHE_H_
 #define MASKLAYERIMAGECACHE_H_
 
 #include "FrameLayerBuilder.h"
+#include "DisplayItemClip.h"
 #include "nsPresContext.h"
 
 namespace mozilla {
 
 namespace layers {
 class ImageContainer;
 }
 
@@ -32,23 +33,23 @@ class MaskLayerImageCache
 {
   typedef mozilla::layers::ImageContainer ImageContainer;
 public:
   MaskLayerImageCache();
   ~MaskLayerImageCache();
 
   /**
    * Representation of a rounded rectangle in device pixel coordinates, in
-   * contrast to FrameLayerBuilder::Clip::RoundedRect, which uses app units.
+   * contrast to DisplayItemClip::RoundedRect, which uses app units.
    * In particular, our internal representation uses a gfxRect, rather than
    * an nsRect, so this class is easier to use with transforms.
    */
   struct PixelRoundedRect
   {
-    PixelRoundedRect(const FrameLayerBuilder::Clip::RoundedRect& aRRect,
+    PixelRoundedRect(const DisplayItemClip::RoundedRect& aRRect,
                      nsPresContext* aPresContext)
       : mRect(aPresContext->AppUnitsToGfxUnits(aRRect.mRect.x),
               aPresContext->AppUnitsToGfxUnits(aRRect.mRect.y),
               aPresContext->AppUnitsToGfxUnits(aRRect.mRect.width),
               aPresContext->AppUnitsToGfxUnits(aRRect.mRect.height))
     {
       MOZ_COUNT_CTOR(PixelRoundedRect);
       NS_FOR_CSS_HALF_CORNERS(corner) {