Bug 841192. Part 15: Move DisplayListClipState clipping methods to AutoSaveRestore and AutoClipMultiple helper classes for safer usage. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Fri, 05 Apr 2013 00:36:45 +1300
changeset 138757 3396e302a1e44e964f3dfeea457f26498acbdff4
parent 138756 5f7f4e26d4a9b1e17772e32caf1aa9502b008a21
child 138758 1e958f7224a737ac34be464a9e11e445c8d1b0df
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [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 15: Move DisplayListClipState clipping methods to AutoSaveRestore and AutoClipMultiple helper classes for safer usage. r=mattwoodrow
layout/base/DisplayListClipState.cpp
layout/base/DisplayListClipState.h
layout/forms/nsFileControlFrame.cpp
layout/forms/nsHTMLButtonControlFrame.cpp
layout/generic/TextOverflow.cpp
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsPageFrame.cpp
layout/generic/nsSubDocumentFrame.cpp
layout/ipc/RenderFrameParent.cpp
--- a/layout/base/DisplayListClipState.cpp
+++ b/layout/base/DisplayListClipState.cpp
@@ -57,25 +57,16 @@ DisplayListClipState::ClipContentDescend
   aClipOnStack.SetTo(aRect);
   if (mClipContentDescendants) {
     aClipOnStack.IntersectWith(*mClipContentDescendants);
   }
   mClipContentDescendants = &aClipOnStack;
   mCurrentCombinedClip = nullptr;
 }
 
-DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox::
-AutoClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
-                                               nsIFrame* aFrame,
-                                               uint32_t aFlags)
-  : AutoSaveRestore(aBuilder->ClipState())
-{
-  mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags);
-}
-
 void
 DisplayListClipState::ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
                                                                  nsIFrame* aFrame,
                                                                  DisplayItemClip& aClipOnStack,
                                                                  uint32_t aFlags)
 {
   nscoord radii[8];
   bool hasBorderRadius = aFrame->GetContentBoxBorderRadii(radii);
@@ -86,9 +77,16 @@ DisplayListClipState::ClipContainingBloc
   nsRect clipRect = aFrame->GetContentRectRelativeToSelf() +
     aBuilder->ToReferenceFrame(aFrame);
   // If we have a border-radius, we have to clip our content to that
   // radius.
   ClipContainingBlockDescendants(clipRect, hasBorderRadius ? radii : nullptr,
                                  aClipOnStack);
 }
 
+DisplayListClipState::AutoSaveRestore::AutoSaveRestore(nsDisplayListBuilder* aBuilder)
+  : mState(aBuilder->ClipState())
+  , mSavedState(aBuilder->ClipState())
+  , mClipUsed(false)
+  , mRestored(false)
+{}
+
 }
--- a/layout/base/DisplayListClipState.h
+++ b/layout/base/DisplayListClipState.h
@@ -3,16 +3,18 @@
  * 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 DISPLAYLISTCLIPSTATE_H_
 #define DISPLAYLISTCLIPSTATE_H_
 
 #include "DisplayItemClip.h"
 
+#include "mozilla/DebugOnly.h"
+
 class nsIFrame;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 
 /**
  * All clip coordinates are in appunits relative to the reference frame
  * for the display item we're building.
@@ -35,26 +37,35 @@ public:
   {
     return mClipContainingBlockDescendants;
   }
   const DisplayItemClip* GetClipForContentDescendants() const
   {
     return mClipContentDescendants;
   }
 
+  class AutoSaveRestore;
+  friend class AutoSaveRestore;
+
+  class AutoClipContainingBlockDescendantsToContentBox;
+  friend class AutoClipContainingBlockDescendantsToContentBox;
+
+  class AutoClipMultiple;
+  friend class AutoClipMultiple;
+
+  enum {
+    ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT = 0x01
+  };
+
+private:
   void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
   {
     mClipContainingBlockDescendants = aClip;
     mCurrentCombinedClip = nullptr;
   }
-  void SetClipForContentDescendants(const DisplayItemClip* aClip)
-  {
-    mClipContentDescendants = aClip;
-    mCurrentCombinedClip = nullptr;
-  }
 
   void Clear()
   {
     mClipContentDescendants = nullptr;
     mClipContainingBlockDescendants = nullptr;
     mCurrentCombinedClip = nullptr;
   }
 
@@ -65,38 +76,29 @@ public:
    */
   void ClipContainingBlockDescendants(const nsRect& aRect,
                                       const nscoord* aRadii,
                                       DisplayItemClip& aClipOnStack);
 
   void ClipContentDescendants(const nsRect& aRect,
                               DisplayItemClip& aClipOnStack);
 
-  enum {
-    ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT = 0x01
-  };
   /**
    * Clips containing-block descendants to the frame's content-box,
    * taking border-radius into account.
    * If aFlags contains ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT then
    * we assume display items will not draw outside the content rect, so
    * clipping is only required if there is a border-radius. This is an
    * optimization to reduce the amount of clipping required.
    */
   void ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
                                                   nsIFrame* aFrame,
                                                   DisplayItemClip& aClipOnStack,
-                                                  uint32_t aFlags = 0);
+                                                  uint32_t aFlags);
 
-  class AutoSaveRestore;
-  friend class AutoSaveRestore;
-
-  class AutoClipContainingBlockDescendantsToContentBox;
-
-private:
   /**
    * All content descendants (i.e. following placeholder frames to their
    * out-of-flows if necessary) should be clipped by mClipContentDescendants.
    * Null if no clipping applies.
    */
   const DisplayItemClip* mClipContentDescendants;
   /**
    * All containing-block descendants (i.e. frame descendants), including
@@ -110,39 +112,137 @@ private:
    * mClipContainingBlockDescendants.
    * Allocated in the nsDisplayListBuilder arena. Null if none has been
    * allocated or both mClipContentDescendants and mClipContainingBlockDescendants
    * are null.
    */
   const DisplayItemClip* mCurrentCombinedClip;
 };
 
+/**
+ * A class to automatically save and restore the current clip state. Also
+ * offers methods for modifying the clip state. Only one modification is allowed
+ * to be in scope at a time using one of these objects; multiple modifications
+ * require nested objects. The interface is written this way to prevent
+ * dangling pointers to DisplayItemClips.
+ */
 class DisplayListClipState::AutoSaveRestore {
 public:
-  AutoSaveRestore(DisplayListClipState& aState)
-    : mState(aState)
-    , mSavedState(aState)
-  {}
+  AutoSaveRestore(nsDisplayListBuilder* aBuilder);
   void Restore()
   {
     mState = mSavedState;
+    mRestored = true;
   }
   ~AutoSaveRestore()
   {
     mState = mSavedState;
   }
+
+  void Clear()
+  {
+    NS_ASSERTION(!mRestored, "Already restored!");
+    mState.Clear();
+    mClipUsed = false;
+  }
+
+  /**
+   * Intersects the given clip rect (with optional aRadii) with the current
+   * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
+   * the result, stored in aClipOnStack.
+   */
+  void ClipContainingBlockDescendants(const nsRect& aRect,
+                                      const nscoord* aRadii)
+  {
+    NS_ASSERTION(!mRestored, "Already restored!");
+    NS_ASSERTION(!mClipUsed, "mClip already used");
+    mClipUsed = true;
+    mState.ClipContainingBlockDescendants(aRect, aRadii, mClip);
+  }
+
+  void ClipContentDescendants(const nsRect& aRect)
+  {
+    NS_ASSERTION(!mRestored, "Already restored!");
+    NS_ASSERTION(!mClipUsed, "mClip already used");
+    mClipUsed = true;
+    mState.ClipContentDescendants(aRect, mClip);
+  }
+
+  /**
+   * Clips containing-block descendants to the frame's content-box,
+   * taking border-radius into account.
+   * If aFlags contains ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT then
+   * we assume display items will not draw outside the content rect, so
+   * clipping is only required if there is a border-radius. This is an
+   * optimization to reduce the amount of clipping required.
+   */
+  void ClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
+                                                  nsIFrame* aFrame,
+                                                  uint32_t aFlags = 0)
+  {
+    NS_ASSERTION(!mRestored, "Already restored!");
+    NS_ASSERTION(!mClipUsed, "mClip already used");
+    mClipUsed = true;
+    mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags);
+  }
+
 protected:
   DisplayListClipState& mState;
   DisplayListClipState mSavedState;
+  DisplayItemClip mClip;
+  DebugOnly<bool> mClipUsed;
+  DebugOnly<bool> mRestored;
 };
 
 class DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox : public AutoSaveRestore {
 public:
   AutoClipContainingBlockDescendantsToContentBox(nsDisplayListBuilder* aBuilder,
                                                  nsIFrame* aFrame,
-                                                 uint32_t aFlags = 0);
+                                                 uint32_t aFlags = 0)
+    : AutoSaveRestore(aBuilder)
+  {
+    mClipUsed = true;
+    mState.ClipContainingBlockDescendantsToContentBox(aBuilder, aFrame, mClip, aFlags);
+  }
+};
+
+/**
+ * Do not use this outside of nsFrame::BuildDisplayListForChild, use
+ * multiple AutoSaveRestores instead. We provide this class just to ensure
+ * BuildDisplayListForChild is as efficient as possible.
+ */
+class DisplayListClipState::AutoClipMultiple : public AutoSaveRestore {
+public:
+  AutoClipMultiple(nsDisplayListBuilder* aBuilder)
+    : AutoSaveRestore(aBuilder)
+    , mExtraClipUsed(false)
+  {}
+
+  /**
+   * *aClip must survive longer than this object. Be careful!!!
+   */
+  void SetClipForContainingBlockDescendants(const DisplayItemClip* aClip)
+  {
+    mState.SetClipForContainingBlockDescendants(aClip);
+  }
+
+  /**
+   * Intersects the given clip rect (with optional aRadii) with the current
+   * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to
+   * the result, stored in aClipOnStack.
+   */
+  void ClipContainingBlockDescendantsExtra(const nsRect& aRect,
+                                           const nscoord* aRadii)
+  {
+    NS_ASSERTION(!mRestored, "Already restored!");
+    NS_ASSERTION(!mExtraClipUsed, "mExtraClip already used");
+    mExtraClipUsed = true;
+    mState.ClipContainingBlockDescendants(aRect, aRadii, mExtraClip);
+  }
+
 protected:
-  DisplayItemClip mClip;
+  DisplayItemClip mExtraClip;
+  DebugOnly<bool> mExtraClipUsed;
 };
 
 }
 
 #endif /* DISPLAYLISTCLIPSTATE_H_ */
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -352,19 +352,18 @@ nsFileControlFrame::BuildDisplayList(nsD
   }
 
   // Clip height only
   nsRect clipRect(aBuilder->ToReferenceFrame(this), GetSize());
   clipRect.width = GetVisualOverflowRect().XMost();
 
   nsDisplayListCollection tempList;
   {
-    DisplayListClipState::AutoSaveRestore saveClipState(aBuilder->ClipState());
-    DisplayItemClip clipOnStack;
-    aBuilder->ClipState().ClipContainingBlockDescendants(clipRect, nullptr, clipOnStack);
+    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
+    clipState.ClipContainingBlockDescendants(clipRect, nullptr);
 
     // Our background is inherited to the text input, and we don't really want to
     // paint it or out padding and borders (which we never have anyway, per
     // styles in forms.css) -- doing it just makes us look ugly in some cases and
     // has no effect in others.
     nsBlockFrame::BuildDisplayList(aBuilder, aDirtyRect, tempList);
   }
 
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -112,41 +112,34 @@ nsHTMLButtonControlFrame::BuildDisplayLi
                                            const nsRect&           aDirtyRect,
                                            const nsDisplayListSet& aLists)
 {
   nsDisplayList onTop;
   if (IsVisibleForPainting(aBuilder)) {
     mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
   }
 
-  bool overflowClip =
-    IsInput() || StyleDisplay()->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE;
-  nsRect rect;
-  nscoord radii[8];  
   nsDisplayListCollection set;
 
-  {
-    DisplayListClipState::AutoSaveRestore saveClipState(aBuilder->ClipState());
-    DisplayItemClip overflowClipOnStack;
+  // Do not allow the child subtree to receive events.
+  if (!aBuilder->IsForEventDelivery()) {
+    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
-    if (overflowClip) {
+    if (IsInput() || StyleDisplay()->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE) {
       nsMargin border = StyleBorder()->GetComputedBorder();
-      rect = nsRect(aBuilder->ToReferenceFrame(this), GetSize());
+      nsRect rect(aBuilder->ToReferenceFrame(this), GetSize());
       rect.Deflate(border);
+      nscoord radii[8];
       bool hasRadii = GetPaddingBoxBorderRadii(radii);
-      aBuilder->ClipState().ClipContainingBlockDescendants(rect,
-          hasRadii ? radii : nullptr, overflowClipOnStack);
+      clipState.ClipContainingBlockDescendants(rect, hasRadii ? radii : nullptr);
     }
 
-    // Do not allow the child subtree to receive events.
-    if (!aBuilder->IsForEventDelivery()) {
-      BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
-                               DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
-      // That should put the display items in set.Content()
-    }
+    BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
+                             DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
+    // That should put the display items in set.Content()
   }
   
   // Put the foreground outline and focus rects on top of the children
   set.Content()->AppendToTop(&onTop);
   set.MoveTo(aLists);
   
   DisplayOutline(aBuilder, aLists);
 
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -97,35 +97,33 @@ IsHorizontalOverflowVisible(nsIFrame* aF
   while (f && f->StyleContext()->GetPseudo() &&
          f->GetType() != nsGkAtoms::scrollFrame) {
     f = f->GetParent();
   }
   return !f || f->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE;
 }
 
 static void
-ClipMarker(nsDisplayListBuilder* aBuilder,
-           nsIFrame*             aFrame,
-           const nsRect&         aContentArea,
-           const nsRect&         aMarkerRect,
-           DisplayItemClip&      aClipOnStack)
+ClipMarker(const nsRect&                          aContentArea,
+           const nsRect&                          aMarkerRect,
+           DisplayListClipState::AutoSaveRestore& aClipState)
 {
   nscoord rightOverflow = aMarkerRect.XMost() - aContentArea.XMost();
   nsRect markerRect = aMarkerRect;
   if (rightOverflow > 0) {
     // Marker overflows on the right side (content width < marker width).
     markerRect.width -= rightOverflow;
-    aBuilder->ClipState().ClipContentDescendants(markerRect, aClipOnStack);
+    aClipState.ClipContentDescendants(markerRect);
   } else {
     nscoord leftOverflow = aContentArea.x - aMarkerRect.x;
     if (leftOverflow > 0) {
       // Marker overflows on the left side
       markerRect.width -= leftOverflow;
       markerRect.x += leftOverflow;
-      aBuilder->ClipState().ClipContentDescendants(markerRect, aClipOnStack);
+      aClipState.ClipContentDescendants(markerRect);
     }
   }
 }
 
 static void
 InflateLeft(nsRect* aRect, nscoord aDelta)
 {
   nscoord xmost = aRect->XMost();
@@ -703,43 +701,39 @@ TextOverflow::CanHaveTextOverflow(nsDisp
 
 void
 TextOverflow::CreateMarkers(const nsLineBox* aLine,
                             bool             aCreateLeft,
                             bool             aCreateRight,
                             const nsRect&    aInsideMarkersArea)
 {
   if (aCreateLeft) {
-    DisplayListClipState::AutoSaveRestore saveClip(mBuilder->ClipState());
+    DisplayListClipState::AutoSaveRestore clipState(mBuilder);
 
     nsRect markerRect = nsRect(aInsideMarkersArea.x - mLeft.mIntrinsicWidth,
                                aLine->mBounds.y,
                                mLeft.mIntrinsicWidth, aLine->mBounds.height);
     markerRect += mBuilder->ToReferenceFrame(mBlock);
-    DisplayItemClip clipOnStack;
-    ClipMarker(mBuilder, mBlock,
-               mContentArea + mBuilder->ToReferenceFrame(mBlock),
-               markerRect, clipOnStack);
+    ClipMarker(mContentArea + mBuilder->ToReferenceFrame(mBlock),
+               markerRect, clipState);
     nsDisplayItem* marker = new (mBuilder)
       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
                                   aLine->GetAscent(), mLeft.mStyle, 0);
     mMarkerList.AppendNewToTop(marker);
   }
 
   if (aCreateRight) {
-    DisplayListClipState::AutoSaveRestore saveClip(mBuilder->ClipState());
+    DisplayListClipState::AutoSaveRestore clipState(mBuilder);
 
     nsRect markerRect = nsRect(aInsideMarkersArea.XMost(),
                                aLine->mBounds.y,
                                mRight.mIntrinsicWidth, aLine->mBounds.height);
     markerRect += mBuilder->ToReferenceFrame(mBlock);
-    DisplayItemClip clipOnStack;
-    ClipMarker(mBuilder, mBlock,
-               mContentArea + mBuilder->ToReferenceFrame(mBlock),
-               markerRect, clipOnStack);
+    ClipMarker(mContentArea + mBuilder->ToReferenceFrame(mBlock),
+               markerRect, clipState);
     nsDisplayItem* marker = new (mBuilder)
       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
                                   aLine->GetAscent(), mRight.mStyle, 1);
     mMarkerList.AppendNewToTop(marker);
   }
 }
 
 void
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1576,52 +1576,52 @@ nsIFrame::GetClipPropClipRect(const nsSt
  * in aBuilder->ClipState() to clip all content descendants. Returns true
  * if the property applies, and if so also returns the clip rect (relative
  * to aFrame) in *aRect.
  */
 static bool
 ApplyClipPropClipping(nsDisplayListBuilder* aBuilder,
                       const nsIFrame* aFrame,
                       const nsStyleDisplay* aDisp,
-                      nsRect* aRect, DisplayItemClip& aClipOnStack)
+                      nsRect* aRect,
+                      DisplayListClipState::AutoSaveRestore& aClipState)
 {
   if (!aFrame->GetClipPropClipRect(aDisp, aRect, aFrame->GetSize()))
     return false;
 
   nsRect clipRect = *aRect + aBuilder->ToReferenceFrame(aFrame);
-  aBuilder->ClipState().ClipContentDescendants(clipRect, aClipOnStack);
+  aClipState.ClipContentDescendants(clipRect);
   return true;
 }
 
 /**
  * If the CSS 'overflow' property applies to this frame, and is not
  * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
  * for that overflow in aBuilder->ClipState() to clip all containing-block
  * descendants.
  */
 static void
 ApplyOverflowClipping(nsDisplayListBuilder* aBuilder,
                       const nsIFrame* aFrame,
                       const nsStyleDisplay* aDisp,
-                      DisplayItemClip& aClipOnStack)
+                      DisplayListClipState::AutoClipMultiple& aClipState)
 {
   // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
   // frames, and any non-visible value for blocks in a paginated context).
   // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
   // is required by comboboxes which make their display text (an inline frame)
   // have clipping.
   if (!nsFrame::ShouldApplyOverflowClipping(aFrame, aDisp)) {
     return;
   }
   nsRect rect = aFrame->GetPaddingRectRelativeToSelf() +
       aBuilder->ToReferenceFrame(aFrame);
   nscoord radii[8];
   bool haveRadii = aFrame->GetPaddingBoxBorderRadii(radii);
-  aBuilder->ClipState().ClipContainingBlockDescendants(rect,
-    haveRadii ? radii : nullptr, aClipOnStack);
+  aClipState.ClipContainingBlockDescendantsExtra(rect, haveRadii ? radii : nullptr);
 }
 
 #ifdef DEBUG
 static void PaintDebugBorder(nsIFrame* aFrame, nsRenderingContext* aCtx,
      const nsRect& aDirtyRect, nsPoint aPt) {
   nsRect r(aPt, aFrame->GetSize());
   if (aFrame->HasView()) {
     aCtx->SetColor(NS_RGB(0,0,255));
@@ -1797,43 +1797,42 @@ nsIFrame::BuildDisplayListForStackingCon
       }
     }
     inTransform = true;
   }
 
   bool useOpacity = HasOpacity() && !nsSVGUtils::CanOptimizeOpacity(this);
   bool usingSVGEffects = nsSVGIntegrationUtils::UsingEffectsForFrame(this);
 
-  DisplayListClipState::AutoSaveRestore saveClipState(aBuilder->ClipState());
+  DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
   if (isTransformed || useOpacity || usingSVGEffects) {
     // We don't need to pass ancestor clipping down to our children;
     // everything goes inside a display item's child list, and the display
     // item itself will be clipped.
     // For transforms we also need to clear ancestor clipping because it's
     // relative to the wrong display item reference frame anyway.
-    aBuilder->ClipState().Clear();
+    clipState.Clear();
   }
 
   nsDisplayListCollection set;
   {    
     nsDisplayListBuilder::AutoBuildingDisplayList rootSetter(aBuilder, true);
-    DisplayListClipState::AutoSaveRestore clipState(aBuilder->ClipState());
+    DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     nsDisplayListBuilder::AutoInTransformSetter
       inTransformSetter(aBuilder, inTransform);
 
     if (usingSVGEffects) {
       dirtyRect =
         nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
     }
 
     nsRect clipPropClip;
-    DisplayItemClip clipPropClipOnStack;
     if (ApplyClipPropClipping(aBuilder, this, disp, &clipPropClip,
-                              clipPropClipOnStack)) {
+                              nestedClipState)) {
       dirtyRect.IntersectRect(dirtyRect, clipPropClip);
     }
 
     MarkAbsoluteFramesForDisplayList(aBuilder, dirtyRect);
 
     // Preserve3DChildren() also guarantees that applyAbsPosClipping and usingSVGEffects are false
     // We only modify the preserve-3d rect if we are the top of a preserve-3d heirarchy
     if (Preserves3DChildren()) {
@@ -1894,17 +1893,17 @@ nsIFrame::BuildDisplayListForStackingCon
 #endif
   resultList.AppendToTop(set.Outlines());
   // 8, 9: non-negative z-index children
   resultList.AppendToTop(set.PositionedDescendants());
 
   if (!isTransformed) {
     // Restore saved clip state now so that any display items we create below
     // are clipped properly.
-    saveClipState.Restore();
+    clipState.Restore();
   }
 
   /* If there are any SVG effects, wrap the list up in an SVG effects item
    * (which also handles CSS group opacity). Note that we create an SVG effects
    * item even if resultList is empty, since a filter can produce graphical
    * output even if the element being filtered wouldn't otherwise do so.
    */
   if (usingSVGEffects) {
@@ -1928,17 +1927,17 @@ nsIFrame::BuildDisplayListForStackingCon
    * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
    * we can skip this step, as the computed transform will already include our own.
    *
    * We also traverse into sublists created by nsDisplayWrapList or nsDisplayOpacity, so that
    * we find all the correct children.
    */
   if (isTransformed && !resultList.IsEmpty()) {
     // Restore clip state now so nsDisplayTransform is clipped properly.
-    saveClipState.Restore();
+    clipState.Restore();
 
     if (Preserves3DChildren()) {
       WrapPreserve3DList(this, aBuilder, &resultList);
     } else {
       resultList.AppendNewToTop(
         new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList));
     }
   }
@@ -2109,50 +2108,47 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // Don't build an nsDisplayFixedPosition if our root scroll frame is not
   // active, that's pointless and the extra layer(s) created may be wasteful.
   bool buildFixedPositionItem = disp->mPosition == NS_STYLE_POSITION_FIXED &&
     !child->GetParent()->GetParent() && !aBuilder->IsInFixedPosition() &&
     IsRootScrollFrameActive(PresContext()->PresShell()) && !isSVG;
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem);
-  DisplayListClipState::AutoSaveRestore clipState(aBuilder->ClipState());
+  DisplayListClipState::AutoClipMultiple clipState(aBuilder);
 
   if (savedOutOfFlowData) {
     clipState.SetClipForContainingBlockDescendants(
       savedOutOfFlowData->mContainingBlockClip);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
   // descendants may not be clipped by it. Even if we end up with unnecessary
   // display items, they'll be pruned during ComputeVisibility.
-  DisplayItemClip overflowClipOnStack;
   nsIFrame* parent = child->GetParent();
   const nsStyleDisplay* parentDisp =
     parent == this ? ourDisp : parent->StyleDisplay();
-  ApplyOverflowClipping(aBuilder, parent, parentDisp, overflowClipOnStack);
+  ApplyOverflowClipping(aBuilder, parent, parentDisp, clipState);
 
   nsDisplayList list;
   nsDisplayList extraPositionedDescendants;
   if (isStackingContext) {
     // True stacking context.
     // For stacking contexts, BuildDisplayListForStackingContext handles
     // clipping and MarkAbsoluteFramesForDisplayList.
     child->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
     aBuilder->DisplayCaret(child, dirty, &list);
   } else {
     nsRect clipRect;
-    DisplayItemClip clipPropClipOnStack;
-    if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect,
-                              clipPropClipOnStack)) {
+    if (ApplyClipPropClipping(aBuilder, child, disp, &clipRect, clipState)) {
       // clipRect is in builder-reference-frame coordinates,
       // dirty/clippedDirtyRect are in child coordinates
       dirty.IntersectRect(dirty, clipRect);
     }
 
     child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
 
     if (!pseudoStackingContext) {
@@ -2183,17 +2179,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
 #ifdef DEBUG
     DisplayDebugBorders(aBuilder, child, aLists);
 #endif
   }
     
   // Clear clip rect for the construction of the items below. Since we're
   // clipping all their contents, they themselves don't need to be clipped.
-  aBuilder->ClipState().Clear();
+  clipState.Clear();
 
   if (isPositioned || isVisuallyAtomic ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
     // go in this level.
     if (!list.IsEmpty()) {
       // Make sure the root of a fixed position frame sub-tree gets the
       // correct displaylist item type.
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2123,42 +2123,40 @@ nsGfxScrollFrameInner::BuildDisplayList(
 
     nsPoint horzShift = nsPoint(sHorzExpandScrollPort * dirtyRectBefore.width, 0);
     dirtyRect = dirtyRect.Union(dirtyRectBefore - horzShift);
     dirtyRect = dirtyRect.Union(dirtyRectBefore + horzShift);
   }
 
   nsDisplayListCollection set;
   {
-    DisplayListClipState::AutoSaveRestore saveClipState(aBuilder->ClipState());
-    DisplayItemClip clipOnStack;
+    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
     if (usingDisplayport) {
       nsRect clip = displayPort + aBuilder->ToReferenceFrame(mOuter);
       if (mIsRoot) {
-        aBuilder->ClipState().ClipContentDescendants(clip, clipOnStack);
+        clipState.ClipContentDescendants(clip);
       } else {
-        aBuilder->ClipState().ClipContainingBlockDescendants(clip, nullptr, clipOnStack);
+        clipState.ClipContainingBlockDescendants(clip, nullptr);
       }
     } else {
       nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
       // Our override of GetBorderRadii ensures we never have a radius at
       // the corners where we have a scrollbar.
       if (mIsRoot) {
 #ifdef DEBUG
         nscoord radii[8];
 #endif
         NS_ASSERTION(!mOuter->GetPaddingBoxBorderRadii(radii),
                      "Roots with radii not supported");
-        aBuilder->ClipState().ClipContentDescendants(clip, clipOnStack);
+        clipState.ClipContentDescendants(clip);
       } else {
         nscoord radii[8];
         bool haveRadii = mOuter->GetPaddingBoxBorderRadii(radii);
-        aBuilder->ClipState().ClipContainingBlockDescendants(clip,
-            haveRadii ? radii : nullptr, clipOnStack);
+        clipState.ClipContainingBlockDescendants(clip, haveRadii ? radii : nullptr);
       }
     }
 
     mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, aLists);
   }
 
   // Since making new layers is expensive, only use nsDisplayScrollLayer
   // if the area is scrollable and we're the content process.
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -513,23 +513,22 @@ nsPageFrame::BuildDisplayList(nsDisplayL
     clipRect.height = expectedPageContentHeight;
     NS_ASSERTION(clipRect.y < child->GetSize().height,
                  "Should be clipping to region inside the page content bounds");
   }
   clipRect += aBuilder->ToReferenceFrame(child);
 
   nsDisplayList content;
   {
-    DisplayListClipState::AutoSaveRestore saveClip(aBuilder->ClipState());
+    DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
-    DisplayItemClip clipOnStack;
     // Overwrite current clip, since we're going to wrap in a transform
     // and the current clip is no longer meaningful.
-    aBuilder->ClipState().Clear();
-    aBuilder->ClipState().ClipContainingBlockDescendants(clipRect, nullptr, clipOnStack);
+    clipState.Clear();
+    clipState.ClipContainingBlockDescendants(clipRect, nullptr);
 
     child->BuildDisplayListForStackingContext(aBuilder,
       child->GetVisualOverflowRectRelativeToSelf(), &content);
 
     // We may need to paint out-of-flow frames whose placeholders are
     // on other pages. Add those pages to our display list. Note that
     // out-of-flow frames can't be placed after their placeholders so
     // we don't have to process earlier pages. The display lists for
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -378,38 +378,36 @@ nsSubDocumentFrame::BuildDisplayList(nsD
       dirty = aDirtyRect + GetOffsetToCrossDoc(subdocRootFrame);
       // and convert into the appunits of the subdoc
       dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
     }
 
     aBuilder->EnterPresShell(subdocRootFrame, dirty);
   }
 
-  DisplayListClipState::AutoSaveRestore saveClip(aBuilder->ClipState());
-  DisplayItemClip clipOnStack;
+  DisplayListClipState::AutoSaveRestore clipState(aBuilder);
   if (ShouldClipSubdocument()) {
-    aBuilder->ClipState().ClipContainingBlockDescendantsToContentBox(aBuilder, this,
-      clipOnStack);
+    clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
   }
 
   nsIScrollableFrame *sf = presShell->GetRootScrollFrameAsScrollable();
   bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
   bool needsOwnLayer = constructZoomItem ||
     presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive());
 
   nsDisplayList childItems;
 
   {
-    DisplayListClipState::AutoSaveRestore willClearClip(aBuilder->ClipState());
+    DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
     if (needsOwnLayer) {
       // Clear current clip. There's no point in propagating it down, since
       // the layer we will construct will be clipped by the current clip.
       // In fact for nsDisplayZoom propagating it down would be incorrect since
       // nsDisplayZoom changes the meaning of appunits.
-      aBuilder->ClipState().Clear();
+      nestedClipState.Clear();
     }
 
     if (subdocRootFrame) {
       subdocRootFrame->
         BuildDisplayListForStackingContext(aBuilder, dirty, &childItems);
     }
 
     if (!aBuilder->IsForEventDelivery()) {
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -943,22 +943,21 @@ RenderFrameParent::GetRootLayer() const
 void
 RenderFrameParent::BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                     nsSubDocumentFrame* aFrame,
                                     const nsRect& aDirtyRect,
                                     const nsDisplayListSet& aLists)
 {
   // We're the subdoc for <browser remote="true"> and it has
   // painted content.  Display its shadow layer tree.
-  DisplayListClipState::AutoSaveRestore saveClip(aBuilder->ClipState());
+  DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
   nsPoint offset = aBuilder->ToReferenceFrame(aFrame);
   nsRect bounds = aFrame->EnsureInnerView()->GetBounds() + offset;
-  DisplayItemClip clipOnStack;
-  aBuilder->ClipState().ClipContentDescendants(bounds, clipOnStack);
+  clipState.ClipContentDescendants(bounds);
 
   ContainerLayer* container = GetRootLayer();
   if (aBuilder->IsForEventDelivery() && container) {
     ViewTransform offset =
       ViewTransform(GetContentRectLayerOffset(aFrame, aBuilder), 1, 1);
     BuildListForLayer(container, mFrameLoader, offset,
                       aBuilder, *aLists.Content(), aFrame);
   } else {