Bug 613659 - Implement box-decoration-break layout for border/box-shadow and paddding/margin for inlines. r=cam
authorMats Palmgren <matspal@gmail.com>
Mon, 05 May 2014 17:55:54 +0000
changeset 181570 fdbe3aa72cdb
parent 181569 3d19bd97e60c
child 181571 1035f42d8280
push id26724
push userkwierso@gmail.com
push dateTue, 06 May 2014 03:51:22 +0000
treeherdermozilla-central@6d991528460d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscam
bugs613659
milestone32.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 613659 - Implement box-decoration-break layout for border/box-shadow and paddding/margin for inlines. r=cam
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsCSSRenderingBorders.h
layout/base/nsDisplayList.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsImageFrame.cpp
layout/generic/nsInlineFrame.cpp
layout/generic/nsLineLayout.cpp
layout/generic/nsSplittableFrame.cpp
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableColGroupFrame.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableRowFrame.cpp
layout/tables/nsTableRowGroupFrame.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -56,16 +56,29 @@
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::gfx;
 using mozilla::image::ImageOps;
 using mozilla::CSSSizeOrRatio;
 
 static int gFrameTreeLockCount = 0;
 
+static void
+ApplySkipSides(int aSkipSides, nsMargin* aMargin)
+{
+  if (aSkipSides & SIDE_BIT_LEFT)
+    aMargin->left = 0;
+  if (aSkipSides & SIDE_BIT_TOP)
+    aMargin->top = 0;
+  if (aSkipSides & SIDE_BIT_RIGHT)
+    aMargin->right = 0;
+  if (aSkipSides & SIDE_BIT_BOTTOM)
+    aMargin->bottom = 0;
+}
+
 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
 // recalculating this for each frame in a continuation (perf), hold
 // a cache of various coordinate information that we need in order
 // to paint inline backgrounds.
 struct InlineBackgroundData
 {
   InlineBackgroundData()
       : mFrame(nullptr), mBlockFrame(nullptr)
@@ -76,39 +89,44 @@ struct InlineBackgroundData
   {
   }
 
   void Reset()
   {
     mBoundingBox.SetRect(0,0,0,0);
     mContinuationPoint = mLineContinuationPoint = mUnbrokenWidth = 0;
     mFrame = mBlockFrame = nullptr;
+    mLeftBorderData.Reset();
   }
 
+  /**
+   * Return a continuous rect for (an inline) aFrame relative to the
+   * continuation that draws the left-most part of the background.
+   * This is used when painting backgrounds.
+   */
   nsRect GetContinuousRect(nsIFrame* aFrame)
   {
+    MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::inlineFrame);
+
     SetFrame(aFrame);
 
     nscoord x;
     if (mBidiEnabled) {
       x = mLineContinuationPoint;
 
       // Scan continuations on the same line as aFrame and accumulate the widths
       // of frames that are to the left (if this is an LTR block) or right
       // (if it's RTL) of the current one.
       bool isRtlBlock = (mBlockFrame->StyleVisibility()->mDirection ==
                            NS_STYLE_DIRECTION_RTL);
       nscoord curOffset = aFrame->GetOffsetTo(mBlockFrame).x;
 
-      // No need to use our GetPrevContinuation/GetNextContinuation methods
-      // here, since ib-split siblings are certainly not on the same line.
-
-      nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
       // If the continuation is fluid we know inlineFrame is not on the same line.
       // If it's not fluid, we need to test further to be sure.
+      nsIFrame* inlineFrame = aFrame->GetPrevContinuation();
       while (inlineFrame && !inlineFrame->GetNextInFlow() &&
              AreOnSameLine(aFrame, inlineFrame)) {
         nscoord frameXOffset = inlineFrame->GetOffsetTo(mBlockFrame).x;
         if(isRtlBlock == (frameXOffset >= curOffset)) {
           x += inlineFrame->GetSize().width;
         }
         inlineFrame = inlineFrame->GetPrevContinuation();
       }
@@ -135,16 +153,49 @@ struct InlineBackgroundData
     }
 
     // Assume background-origin: border and return a rect with offsets
     // relative to (0,0).  If we have a different background-origin,
     // then our rect should be deflated appropriately by our caller.
     return nsRect(-x, 0, mUnbrokenWidth, mFrame->GetSize().height);
   }
 
+  /**
+   * Return a continuous rect for (an inline) aFrame relative to the
+   * continuation that should draw the left-border.  This is used when painting
+   * borders and clipping backgrounds.  This may NOT be the same continuous rect
+   * as for drawing backgrounds; the continuation with the left-border might be
+   * somewhere in the middle of that rect (e.g. BIDI), in those cases we need
+   * the reverse background order starting at the left-border continuation.
+   */
+  nsRect GetBorderContinuousRect(nsIFrame* aFrame, nsRect aBorderArea)
+  {
+    // Calling GetContinuousRect(aFrame) here may lead to Reset/Init which
+    // resets our mLeftBorderData so we save it ...
+    LeftBorderData saved(mLeftBorderData);
+    nsRect joinedBorderArea = GetContinuousRect(aFrame);
+    if (!saved.mIsValid || saved.mFrame != mLeftBorderData.mFrame) {
+      if (aFrame == mLeftBorderData.mFrame) {
+        mLeftBorderData.SetX(joinedBorderArea.x);
+      } else if (mLeftBorderData.mFrame) {
+        mLeftBorderData.SetX(GetContinuousRect(mLeftBorderData.mFrame).x);
+      }
+    } else {
+      // ... and restore it when possible.
+      mLeftBorderData.mX = saved.mX;
+    }
+    if (joinedBorderArea.x > mLeftBorderData.mX) {
+      joinedBorderArea.x =
+        -(mUnbrokenWidth + joinedBorderArea.x - aBorderArea.width);
+    } else {
+      joinedBorderArea.x -= mLeftBorderData.mX;
+    }
+    return joinedBorderArea;
+  }
+
   nsRect GetBoundingRect(nsIFrame* aFrame)
   {
     SetFrame(aFrame);
 
     // Move the offsets relative to (0,0) which puts the bounding box into
     // our coordinate system rather than our parent's.  We do this by
     // moving it the back distance from us to the bounding box.
     // This also assumes background-origin: border, so our caller will
@@ -152,23 +203,32 @@ struct InlineBackgroundData
     nsRect boundingBox(mBoundingBox);
     nsPoint point = mFrame->GetPosition();
     boundingBox.MoveBy(-point.x, -point.y);
 
     return boundingBox;
   }
 
 protected:
-  nsIFrame*     mFrame;
-  nsBlockFrame* mBlockFrame;
-  nsRect        mBoundingBox;
-  nscoord       mContinuationPoint;
-  nscoord       mUnbrokenWidth;
-  nscoord       mLineContinuationPoint;
-  bool          mBidiEnabled;
+  struct LeftBorderData {
+    nsIFrame* mFrame;   // the continuation that may have a left-border
+    nscoord   mX;       // cached GetContinuousRect(mFrame).x
+    bool      mIsValid; // true if mX is valid
+    void Reset() { mFrame = nullptr; mIsValid = false; }
+    void SetX(nscoord aX) { mX = aX; mIsValid = true; }
+  };
+
+  nsIFrame*      mFrame;
+  nsBlockFrame*  mBlockFrame;
+  nsRect         mBoundingBox;
+  nscoord        mContinuationPoint;
+  nscoord        mUnbrokenWidth;
+  nscoord        mLineContinuationPoint;
+  LeftBorderData mLeftBorderData;
+  bool           mBidiEnabled;
 
   void SetFrame(nsIFrame* aFrame)
   {
     NS_PRECONDITION(aFrame, "Need a frame");
     NS_ASSERTION(gFrameTreeLockCount > 0,
                  "Can't call this when frame tree is not locked");
 
     if (aFrame == mFrame) {
@@ -231,48 +291,56 @@ protected:
         NS_ASSERTION(nextCont, "How did that happen?");
       }
     }
     return nextCont;
   }
 
   void Init(nsIFrame* aFrame)
   {
+    mLeftBorderData.Reset();
     mBidiEnabled = aFrame->PresContext()->BidiEnabled();
     if (mBidiEnabled) {
       // Find the containing block frame
       nsIFrame* frame = aFrame;
       do {
         frame = frame->GetParent();
         mBlockFrame = do_QueryFrame(frame);
       }
       while (frame && frame->IsFrameOfType(nsIFrame::eLineParticipant));
 
       NS_ASSERTION(mBlockFrame, "Cannot find containing block.");
     }
 
     // Start with the previous flow frame as our continuation point
     // is the total of the widths of the previous frames.
     nsIFrame* inlineFrame = GetPrevContinuation(aFrame);
-
     while (inlineFrame) {
+      if (!mLeftBorderData.mFrame &&
+          !(inlineFrame->GetSkipSides() & SIDE_BIT_LEFT)) {
+        mLeftBorderData.mFrame = inlineFrame;
+      }
       nsRect rect = inlineFrame->GetRect();
       mContinuationPoint += rect.width;
       if (mBidiEnabled && !AreOnSameLine(aFrame, inlineFrame)) {
         mLineContinuationPoint += rect.width;
       }
       mUnbrokenWidth += rect.width;
       mBoundingBox.UnionRect(mBoundingBox, rect);
       inlineFrame = GetPrevContinuation(inlineFrame);
     }
 
     // Next add this frame and subsequent frames to the bounding box and
     // unbroken width.
     inlineFrame = aFrame;
     while (inlineFrame) {
+      if (!mLeftBorderData.mFrame &&
+          !(inlineFrame->GetSkipSides() & SIDE_BIT_LEFT)) {
+        mLeftBorderData.mFrame = inlineFrame;
+      }
       nsRect rect = inlineFrame->GetRect();
       mUnbrokenWidth += rect.width;
       mBoundingBox.UnionRect(mBoundingBox, rect);
       inlineFrame = GetNextContinuation(inlineFrame);
     }
 
     mFrame = aFrame;
   }
@@ -366,16 +434,104 @@ MakeBevelColor(mozilla::css::Side whichS
   case NS_SIDE_LEFT:
   default:
     theColor = colors[0];
     break;
   }
   return theColor;
 }
 
+static bool
+GetRadii(nsIFrame* aForFrame, const nsStyleBorder& aBorder,
+         const nsRect& aOrigBorderArea, const nsRect& aBorderArea,
+         gfxCornerSizes* aBgRadii)
+{
+  nscoord radii[8];
+  bool haveRoundedCorners;
+  nsSize sz = aBorderArea.Size();
+  nsSize frameSize = aForFrame->GetSize();
+  if (&aBorder == aForFrame->StyleBorder() &&
+      frameSize == aOrigBorderArea.Size()) {
+    haveRoundedCorners = aForFrame->GetBorderRadii(sz, sz, 0, radii);
+   } else {
+    haveRoundedCorners =
+      nsIFrame::ComputeBorderRadii(aBorder.mBorderRadius, frameSize, sz, 0, radii);
+  }
+  if (haveRoundedCorners) {
+    auto d2a = aForFrame->PresContext()->AppUnitsPerDevPixel();
+    nsCSSRendering::ComputePixelRadii(radii, d2a, aBgRadii);
+  }
+  return haveRoundedCorners;
+}
+
+static nsRect
+JoinBoxesForVerticalSlice(nsIFrame* aFrame, const nsRect& aBorderArea)
+{
+  // Inflate vertically as if our continuations were laid out vertically
+  // adjacent. Note that we don't touch the width.
+  nsRect borderArea = aBorderArea;
+  nscoord h = 0;
+  nsIFrame* f = aFrame->GetNextContinuation();
+  for (; f; f = f->GetNextContinuation()) {
+    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
+               "anonymous ib-split block shouldn't have border/background");
+    h += f->GetRect().height;
+  }
+  borderArea.height += h;
+  h = 0;
+  f = aFrame->GetPrevContinuation();
+  for (; f; f = f->GetPrevContinuation()) {
+    MOZ_ASSERT(!(f->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT),
+               "anonymous ib-split block shouldn't have border/background");
+    h += f->GetRect().height;
+  }
+  borderArea.y -= h;
+  borderArea.height += h;
+  return borderArea;
+}
+
+/**
+ * Inflate aBorderArea which is relative to aFrame's origin to calculate
+ * a hypothetical non-split frame area for all the continuations.
+ * See "Joining Boxes for 'slice'" in
+ * http://dev.w3.org/csswg/css-break/#break-decoration
+ */
+enum InlineBoxOrder { eForBorder, eForBackground };
+static nsRect
+JoinBoxesForSlice(nsIFrame* aFrame, const nsRect& aBorderArea,
+                  InlineBoxOrder aOrder)
+{
+  if (aFrame->GetType() == nsGkAtoms::inlineFrame) {
+    return (aOrder == eForBorder
+            ? gInlineBGData->GetBorderContinuousRect(aFrame, aBorderArea)
+            : gInlineBGData->GetContinuousRect(aFrame)) +
+      aBorderArea.TopLeft();
+  }
+  return JoinBoxesForVerticalSlice(aFrame, aBorderArea);
+}
+
+static bool
+IsBoxDecorationSlice(const nsStyleBorder& aStyleBorder)
+{
+  return aStyleBorder.mBoxDecorationBreak ==
+           NS_STYLE_BOX_DECORATION_BREAK_SLICE;
+}
+
+static nsRect
+BoxDecorationRectForBorder(nsIFrame* aFrame, const nsRect& aBorderArea,
+                           const nsStyleBorder* aStyleBorder = nullptr)
+{
+  if (!aStyleBorder) {
+    aStyleBorder = aFrame->StyleBorder();
+  }
+  return ::IsBoxDecorationSlice(*aStyleBorder)
+           ? ::JoinBoxesForSlice(aFrame, aBorderArea, eForBorder)
+           : aBorderArea;
+}
+
 //----------------------------------------------------------------------
 // Thebes Border Rendering Code Start
 
 /*
  * Compute the float-pixel radii that should be used for drawing
  * this border/outline, given the various input bits.
  */
 /* static */ void
@@ -444,20 +600,16 @@ nsCSSRendering::PaintBorderWithStyleBord
                                            nsRenderingContext& aRenderingContext,
                                            nsIFrame* aForFrame,
                                            const nsRect& aDirtyRect,
                                            const nsRect& aBorderArea,
                                            const nsStyleBorder& aStyleBorder,
                                            nsStyleContext* aStyleContext,
                                            int aSkipSides)
 {
-  nsMargin            border;
-  nscoord             twipsRadii[8];
-  nsCompatibility     compatMode = aPresContext->CompatibilityMode();
-
   SN("++ PaintBorder");
 
   // Check to see if we have an appearance defined.  If so, we let the theme
   // renderer draw the border.  DO not get the data from aForFrame, since the passed in style context
   // may be different!  Always use |aStyleContext|!
   const nsStyleDisplay* displayData = aStyleContext->StyleDisplay();
   if (displayData->mAppearance) {
     nsITheme *theme = aPresContext->GetTheme();
@@ -469,111 +621,112 @@ nsCSSRendering::PaintBorderWithStyleBord
     DrawBorderImage(aPresContext, aRenderingContext, aForFrame,
                     aBorderArea, aStyleBorder, aDirtyRect);
     return;
   }
 
   // Get our style context's color struct.
   const nsStyleColor* ourColor = aStyleContext->StyleColor();
 
-  // in NavQuirks mode we want to use the parent's context as a starting point
-  // for determining the background color
-  nsIFrame* bgFrame = nsCSSRendering::FindNonTransparentBackgroundFrame
-    (aForFrame, compatMode == eCompatibility_NavQuirks ? true : false);
+  // In NavQuirks mode we want to use the parent's context as a starting point
+  // for determining the background color.
+  bool quirks = aPresContext->CompatibilityMode() == eCompatibility_NavQuirks;
+  nsIFrame* bgFrame = FindNonTransparentBackgroundFrame(aForFrame, quirks);
   nsStyleContext* bgContext = bgFrame->StyleContext();
   nscolor bgColor =
     bgContext->GetVisitedDependentColor(eCSSProperty_background_color);
 
-  border = aStyleBorder.GetComputedBorder();
+  nsMargin border = aStyleBorder.GetComputedBorder();
   if ((0 == border.left) && (0 == border.right) &&
       (0 == border.top) && (0 == border.bottom)) {
     // Empty border area
     return;
   }
 
-  nsSize frameSize = aForFrame->GetSize();
-  if (&aStyleBorder == aForFrame->StyleBorder() &&
-      frameSize == aBorderArea.Size()) {
-    aForFrame->GetBorderRadii(twipsRadii);
+  // Compute the outermost boundary of the area that might be painted.
+  // Same coordinate space as aBorderArea & aBGClipRect.
+  nsRect joinedBorderArea =
+    ::BoxDecorationRectForBorder(aForFrame, aBorderArea, &aStyleBorder);
+  gfxCornerSizes bgRadii;
+  ::GetRadii(aForFrame, aStyleBorder, aBorderArea, joinedBorderArea, &bgRadii);
+
+
+  SF(" joinedBorderArea: %d %d %d %d\n", joinedBorderArea.x, joinedBorderArea.y,
+     joinedBorderArea.width, joinedBorderArea.height);
+
+  // start drawing
+  gfxContext* ctx = aRenderingContext.ThebesContext();
+  ctx->Save();
+
+  if (::IsBoxDecorationSlice(aStyleBorder)) {
+    if (aSkipSides == 0) {
+      // No continuations most likely, or ::first-letter that wants all border-
+      // sides on the first continuation.
+      joinedBorderArea = aBorderArea;
+    } else if (joinedBorderArea.IsEqualEdges(aBorderArea)) {
+      // No need for a clip, just skip the sides we don't want.
+      ::ApplySkipSides(aSkipSides, &border);
+    } else {
+      // We're drawing borders around the joined continuation boxes so we need
+      // to clip that to the slice that we want for this frame.
+      aRenderingContext.IntersectClip(aBorderArea);
+    }
   } else {
-    nsIFrame::ComputeBorderRadii(aStyleBorder.mBorderRadius, frameSize,
-                                 aBorderArea.Size(), aSkipSides, twipsRadii);
+    MOZ_ASSERT(joinedBorderArea.IsEqualEdges(aBorderArea),
+               "Should use aBorderArea for box-decoration-break:clone");
+    MOZ_ASSERT(aForFrame->GetSkipSides() == 0,
+               "Should not skip sides for box-decoration-break:clone except "
+               "::first-letter/line continuations or other frame types that "
+               "don't have borders but those shouldn't reach this point.");
   }
 
-  // Turn off rendering for all of the zero sized sides
-  if (aSkipSides & SIDE_BIT_TOP) border.top = 0;
-  if (aSkipSides & SIDE_BIT_RIGHT) border.right = 0;
-  if (aSkipSides & SIDE_BIT_BOTTOM) border.bottom = 0;
-  if (aSkipSides & SIDE_BIT_LEFT) border.left = 0;
-
-  // get the inside and outside parts of the border
-  nsRect outerRect(aBorderArea);
-
-  SF(" outerRect: %d %d %d %d\n", outerRect.x, outerRect.y, outerRect.width, outerRect.height);
-
-  // we can assume that we're already clipped to aDirtyRect -- I think? (!?)
-
-  // Get our conversion values
+  // Convert to dev pixels.
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-
-  // convert outer and inner rects
-  gfxRect oRect(nsLayoutUtils::RectToGfxRect(outerRect, twipsPerPixel));
-
-  // convert the border widths
+  gfxRect joinedBorderAreaPx =
+    nsLayoutUtils::RectToGfxRect(joinedBorderArea, twipsPerPixel);
   gfxFloat borderWidths[4] = { gfxFloat(border.top / twipsPerPixel),
                                gfxFloat(border.right / twipsPerPixel),
                                gfxFloat(border.bottom / twipsPerPixel),
                                gfxFloat(border.left / twipsPerPixel) };
 
-  // convert the radii
-  gfxCornerSizes borderRadii;
-  ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
-
   uint8_t borderStyles[4];
   nscolor borderColors[4];
   nsBorderColors *compositeColors[4];
 
   // pull out styles, colors, composite colors
   NS_FOR_CSS_SIDES (i) {
     bool foreground;
     borderStyles[i] = aStyleBorder.GetBorderStyle(i);
     aStyleBorder.GetBorderColor(i, borderColors[i], foreground);
     aStyleBorder.GetCompositeColors(i, &compositeColors[i]);
 
     if (foreground)
       borderColors[i] = ourColor->mColor;
   }
 
   SF(" borderStyles: %d %d %d %d\n", borderStyles[0], borderStyles[1], borderStyles[2], borderStyles[3]);
-
-  // start drawing
-  gfxContext *ctx = aRenderingContext.ThebesContext();
-
-  ctx->Save();
+  //SF ("bgRadii: %f %f %f %f\n", bgRadii[0], bgRadii[1], bgRadii[2], bgRadii[3]);
 
 #if 0
-  // this will draw a transparent red backround underneath the oRect area
+  // this will draw a transparent red backround underneath the border area
   ctx->Save();
-  ctx->Rectangle(oRect);
+  ctx->Rectangle(joinedBorderAreaPx);
   ctx->SetColor(gfxRGBA(1.0, 0.0, 0.0, 0.5));
   ctx->Fill();
   ctx->Restore();
 #endif
 
-  //SF ("borderRadii: %f %f %f %f\n", borderRadii[0], borderRadii[1], borderRadii[2], borderRadii[3]);
-
   nsCSSBorderRenderer br(twipsPerPixel,
                          ctx,
-                         oRect,
+                         joinedBorderAreaPx,
                          borderStyles,
                          borderWidths,
-                         borderRadii,
+                         bgRadii,
                          borderColors,
                          compositeColors,
-                         aSkipSides,
                          bgColor);
   br.DrawBorders();
 
   ctx->Restore();
 
   SN();
 }
 
@@ -683,17 +836,17 @@ nsCSSRendering::PaintOutline(nsPresConte
 
   nsCSSBorderRenderer br(twipsPerPixel,
                          ctx,
                          oRect,
                          outlineStyles,
                          outlineWidths,
                          outlineRadii,
                          outlineColors,
-                         nullptr, 0,
+                         nullptr,
                          bgColor);
   br.DrawBorders();
 
   ctx->Restore();
 
   SN();
 }
 
@@ -736,17 +889,17 @@ nsCSSRendering::PaintFocus(nsPresContext
   // and PaintOutline do.)
   nsCSSBorderRenderer br(oneDevPixel,
                          ctx,
                          focusRect,
                          focusStyles,
                          focusWidths,
                          focusRadii,
                          focusColors,
-                         nullptr, 0,
+                         nullptr,
                          NS_RGB(255, 0, 0));
   br.DrawBorders();
 
   ctx->Restore();
 
   SN();
 }
 
@@ -991,44 +1144,53 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
                                     const nsRect& aFrameArea,
                                     const nsRect& aDirtyRect,
                                     float aOpacity)
 {
   const nsStyleBorder* styleBorder = aForFrame->StyleBorder();
   nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
   if (!shadows)
     return;
-  nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-
+
+  gfxContextAutoSaveRestore gfxStateRestorer;
   bool hasBorderRadius;
   bool nativeTheme; // mutually exclusive with hasBorderRadius
-  gfxCornerSizes borderRadii;
-
-  // Get any border radius, since box-shadow must also have rounded corners if the frame does
   const nsStyleDisplay* styleDisplay = aForFrame->StyleDisplay();
   nsITheme::Transparency transparency;
   if (aForFrame->IsThemed(styleDisplay, &transparency)) {
     // We don't respect border-radius for native-themed widgets
     hasBorderRadius = false;
     // For opaque (rectangular) theme widgets we can take the generic
     // border-box path with border-radius disabled.
     nativeTheme = transparency != nsITheme::eOpaque;
   } else {
     nativeTheme = false;
+    hasBorderRadius = true; // we'll update this below
+  }
+
+  nsRect frameRect = nativeTheme ?
+    aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() :
+    aFrameArea;
+  frameRect = ::BoxDecorationRectForBorder(aForFrame, frameRect);
+
+  // Get any border radius, since box-shadow must also have rounded corners if
+  // the frame does.
+  gfxCornerSizes borderRadii;
+  const nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
+  if (hasBorderRadius) {
     nscoord twipsRadii[8];
     NS_ASSERTION(aFrameArea.Size() == aForFrame->VisualBorderRectRelativeToSelf().Size(),
                  "unexpected size");
-    hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
+    nsSize sz = frameRect.Size();
+    hasBorderRadius = aForFrame->GetBorderRadii(sz, sz, 0, twipsRadii);
     if (hasBorderRadius) {
       ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
     }
   }
 
-  nsRect frameRect =
-    nativeTheme ? aForFrame->GetVisualOverflowRectRelativeToSelf() + aFrameArea.TopLeft() : aFrameArea;
   gfxRect frameGfxRect(nsLayoutUtils::RectToGfxRect(frameRect, twipsPerPixel));
   frameGfxRect.Round();
 
   // We don't show anything that intersects with the frame we're blurring on. So tell the
   // blurrer not to do unnecessary work there.
   gfxRect skipGfxRect = frameGfxRect;
   bool useSkipGfxRect = true;
   if (nativeTheme) {
@@ -1042,16 +1204,17 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
       aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
     skipGfxRect = nsLayoutUtils::RectToGfxRect(paddingRect, twipsPerPixel);
   } else if (hasBorderRadius) {
     skipGfxRect.Deflate(gfxMargin(
         std::max(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
         std::max(borderRadii[C_BL].height, borderRadii[C_BR].height), 0));
   }
 
+  int skipSides = aForFrame->GetSkipSides();
   for (uint32_t i = shadows->Length(); i > 0; --i) {
     nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
     if (shadowItem->mInset)
       continue;
 
     nsRect shadowRect = frameRect;
     shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
     if (!nativeTheme) {
@@ -1122,28 +1285,58 @@ nsCSSRendering::PaintBoxShadowOuter(nsPr
       aPresContext->GetTheme()->DrawWidgetBackground(wrapperCtx, aForFrame,
           styleDisplay->mAppearance, aFrameArea, nativeRect);
 
       blurringArea.DoPaint();
       renderContext->Restore();
     } else {
       renderContext->Save();
       // Clip out the area of the actual frame so the shadow is not shown within
-      // the frame
+      // the frame.
       renderContext->NewPath();
       renderContext->Rectangle(shadowGfxRectPlusBlur);
       if (hasBorderRadius) {
         renderContext->RoundedRectangle(frameGfxRect, borderRadii);
       } else {
         renderContext->Rectangle(frameGfxRect);
       }
 
       renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
       renderContext->Clip();
 
+      // Clip the shadow so that we only get the part that applies to aForFrame.
+      nsRect fragmentClip = shadowRectPlusBlur;
+      if (skipSides) {
+        if (skipSides & SIDE_BIT_LEFT) {
+          nscoord xmost = fragmentClip.XMost();
+          fragmentClip.x = aFrameArea.x;
+          fragmentClip.width = xmost - fragmentClip.x;
+        }
+        if (skipSides & SIDE_BIT_RIGHT) {
+          nscoord xmost = fragmentClip.XMost();
+          nscoord overflow = xmost - aFrameArea.XMost();
+          if (overflow > 0) {
+            fragmentClip.width -= overflow;
+          }
+        }
+        if (skipSides & SIDE_BIT_TOP) {
+          nscoord ymost = fragmentClip.YMost();
+          fragmentClip.y = aFrameArea.y;
+          fragmentClip.height = ymost - fragmentClip.y;
+        }
+        if (skipSides & SIDE_BIT_BOTTOM) {
+          nscoord ymost = fragmentClip.YMost();
+          nscoord overflow = ymost - aFrameArea.YMost();
+          if (overflow > 0) {
+            fragmentClip.height -= overflow;
+          }
+        }
+      }
+      aRenderingContext.IntersectClip(fragmentClip);
+
       gfxCornerSizes clipRectRadii;
       if (hasBorderRadius) {
         gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
 
         gfxFloat borderSizes[4];
 
         borderSizes[NS_SIDE_LEFT] = spreadDistance;
         borderSizes[NS_SIDE_TOP] = spreadDistance;
@@ -1183,28 +1376,30 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
       !nsContentUtils::IsChromeDoc(aForFrame->GetContent()->GetCurrentDoc())) {
     // There's no way of getting hold of a shape corresponding to a
     // "padding-box" for native-themed widgets, so just don't draw
     // inner box-shadows for them. But we allow chrome to paint inner
     // box shadows since chrome can be aware of the platform theme.
     return;
   }
 
+  NS_ASSERTION(aForFrame->GetType() == nsGkAtoms::fieldSetFrame ||
+               aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
+
+  nsRect frameRect = ::BoxDecorationRectForBorder(aForFrame, aFrameArea);
+  nsRect paddingRect = frameRect;
+  nsMargin border = aForFrame->GetUsedBorder();
+  paddingRect.Deflate(border);
+
   // Get any border radius, since box-shadow must also have rounded corners
   // if the frame does.
   nscoord twipsRadii[8];
-  NS_ASSERTION(aForFrame->GetType() == nsGkAtoms::fieldSetFrame ||
-               aFrameArea.Size() == aForFrame->GetSize(), "unexpected size");
-  bool hasBorderRadius = aForFrame->GetBorderRadii(twipsRadii);
-  nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
-
-  nsRect paddingRect = aFrameArea;
-  nsMargin border = aForFrame->GetUsedBorder();
-  aForFrame->ApplySkipSides(border);
-  paddingRect.Deflate(border);
+  nsSize sz = frameRect.Size();
+  bool hasBorderRadius = aForFrame->GetBorderRadii(sz, sz, 0, twipsRadii);
+  const nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   gfxCornerSizes innerRadii;
   if (hasBorderRadius) {
     gfxCornerSizes borderRadii;
 
     ComputePixelRadii(twipsRadii, twipsPerPixel, &borderRadii);
     gfxFloat borderSizes[4] = {
       gfxFloat(border.top / twipsPerPixel),
@@ -1216,23 +1411,19 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
                                            &innerRadii);
   }
 
   for (uint32_t i = shadows->Length(); i > 0; --i) {
     nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
     if (!shadowItem->mInset)
       continue;
 
-    /*
-     * shadowRect: the frame's padding rect
-     * shadowPaintRect: the area to paint on the temp surface, larger than shadowRect
-     *                  so that blurs still happen properly near the edges
-     * shadowClipRect: the area on the temporary surface within shadowPaintRect
-     *                 that we will NOT paint in
-     */
+    // shadowPaintRect: the area to paint on the temp surface
+    // shadowClipRect: the area on the temporary surface within shadowPaintRect
+    //                 that we will NOT paint in
     nscoord blurRadius = shadowItem->mRadius;
     nsMargin blurMargin =
       nsContextBoxBlur::GetBlurRadiusMargin(blurRadius, twipsPerPixel);
     nsRect shadowPaintRect = paddingRect;
     shadowPaintRect.Inflate(blurMargin);
 
     nsRect shadowClipRect = paddingRect;
     shadowClipRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
@@ -4930,20 +5121,18 @@ nsContextBoxBlur::GetContext()
 
 /* static */ nsMargin
 nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius,
                                       int32_t aAppUnitsPerDevPixel)
 {
   gfxIntSize blurRadius = ComputeBlurRadius(aBlurRadius, aAppUnitsPerDevPixel);
 
   nsMargin result;
-  result.top    = blurRadius.height * aAppUnitsPerDevPixel;
-  result.right  = blurRadius.width  * aAppUnitsPerDevPixel;
-  result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
-  result.left   = blurRadius.width  * aAppUnitsPerDevPixel;
+  result.top = result.bottom = blurRadius.height * aAppUnitsPerDevPixel;
+  result.left = result.right = blurRadius.width  * aAppUnitsPerDevPixel;
   return result;
 }
 
 /* static */ void
 nsContextBoxBlur::BlurRectangle(gfxContext* aDestinationCtx,
                                 const nsRect& aRect,
                                 int32_t aAppUnitsPerDevPixel,
                                 gfxCornerSizes* aCornerRadii,
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -115,27 +115,25 @@ typedef enum {
 nsCSSBorderRenderer::nsCSSBorderRenderer(int32_t aAppUnitsPerPixel,
                                          gfxContext* aDestContext,
                                          gfxRect& aOuterRect,
                                          const uint8_t* aBorderStyles,
                                          const gfxFloat* aBorderWidths,
                                          gfxCornerSizes& aBorderRadii,
                                          const nscolor* aBorderColors,
                                          nsBorderColors* const* aCompositeColors,
-                                         int aSkipSides,
                                          nscolor aBackgroundColor)
   : mContext(aDestContext),
     mOuterRect(aOuterRect),
     mBorderStyles(aBorderStyles),
     mBorderWidths(aBorderWidths),
     mBorderRadii(aBorderRadii),
     mBorderColors(aBorderColors),
     mCompositeColors(aCompositeColors),
     mAUPP(aAppUnitsPerPixel),
-    mSkipSides(aSkipSides),
     mBackgroundColor(aBackgroundColor)
 {
   if (!mCompositeColors) {
     static nsBorderColors * const noColors[4] = { nullptr };
     mCompositeColors = &noColors[0];
   }
 
   mInnerRect = mOuterRect;
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -74,17 +74,16 @@ struct nsCSSBorderRenderer {
   nsCSSBorderRenderer(int32_t aAppUnitsPerPixel,
                       gfxContext* aDestContext,
                       gfxRect& aOuterRect,
                       const uint8_t* aBorderStyles,
                       const gfxFloat* aBorderWidths,
                       gfxCornerSizes& aBorderRadii,
                       const nscolor* aBorderColors,
                       nsBorderColors* const* aCompositeColors,
-                      int aSkipSides,
                       nscolor aBackgroundColor);
 
   gfxCornerSizes mBorderCornerDimensions;
 
   // destination context
   gfxContext* mContext;
 
   // the rectangle of the outside and the inside of the border
@@ -100,18 +99,17 @@ struct nsCSSBorderRenderer {
 
   // colors
   const nscolor* mBorderColors;
   nsBorderColors* const* mCompositeColors;
 
   // core app units per pixel
   int32_t mAUPP;
 
-  // misc -- which sides to skip, the background color
-  int mSkipSides;
+  // the background color
   nscolor mBackgroundColor;
 
   // calculated values
   bool mOneUnitBorder;
   bool mNoBorderRadius;
   bool mAvoidStroke;
 
   // For all the sides in the bitmask, would they be rendered
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2856,21 +2856,18 @@ nsDisplayBoxShadowOuter::Paint(nsDisplay
   nsPoint offset = ToReferenceFrame();
   nsRect borderRect = mFrame->VisualBorderRectRelativeToSelf() + offset;
   nsPresContext* presContext = mFrame->PresContext();
   nsAutoTArray<nsRect,10> rects;
   ComputeDisjointRectangles(mVisibleRegion, &rects);
 
   PROFILER_LABEL("nsDisplayBoxShadowOuter", "Paint");
   for (uint32_t i = 0; i < rects.Length(); ++i) {
-    aCtx->PushState();
-    aCtx->IntersectClip(rects[i]);
     nsCSSRendering::PaintBoxShadowOuter(presContext, *aCtx, mFrame,
                                         borderRect, rects[i], mOpacity);
-    aCtx->PopState();
   }
 }
 
 nsRect
 nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
   *aSnap = false;
   return mBounds;
 }
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -832,30 +832,52 @@ nsContainerFrame::DoInlineIntrinsicWidth
 
   // This goes at the beginning no matter how things are broken and how
   // messy the bidi situations are, since per CSS2.1 section 8.6
   // (implemented in bug 328168), the startSide border is always on the
   // first line.
   // This frame is a first-in-flow, but it might have a previous bidi
   // continuation, in which case that continuation should handle the startSide
   // border.
+  // For box-decoration-break:clone we setup clonePBM = startPBM + endPBM and
+  // add that to each line.  For box-decoration-break:slice clonePBM is zero.
+  nscoord clonePBM = 0; // PBM = PaddingBorderMargin
+  const bool sliceBreak =
+    styleBorder->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_SLICE;
   if (!GetPrevContinuation()) {
-    aData->currentLine +=
+    nscoord startPBM =
       // clamp negative calc() to 0
       std::max(GetCoord(stylePadding->mPadding.Get(startSide), 0), 0) +
       styleBorder->GetComputedBorderWidth(startSide) +
       GetCoord(styleMargin->mMargin.Get(startSide), 0);
+    if (MOZ_LIKELY(sliceBreak)) {
+      aData->currentLine += startPBM;
+    } else {
+      clonePBM = startPBM;
+    }
+  }
+
+  nscoord endPBM =
+    // clamp negative calc() to 0
+    std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
+    styleBorder->GetComputedBorderWidth(endSide) +
+    GetCoord(styleMargin->mMargin.Get(endSide), 0);
+  if (MOZ_UNLIKELY(!sliceBreak)) {
+    clonePBM += endPBM;
   }
 
   const nsLineList_iterator* savedLine = aData->line;
   nsIFrame* const savedLineContainer = aData->lineContainer;
 
   nsContainerFrame *lastInFlow;
   for (nsContainerFrame *nif = this; nif;
        nif = static_cast<nsContainerFrame*>(nif->GetNextInFlow())) {
+    if (aData->currentLine == 0) {
+      aData->currentLine = clonePBM;
+    }
     for (nsIFrame *kid = nif->mFrames.FirstChild(); kid;
          kid = kid->GetNextSibling()) {
       if (aType == nsLayoutUtils::MIN_WIDTH)
         kid->AddInlineMinWidth(aRenderingContext,
                                static_cast<InlineMinWidthData*>(aData));
       else
         kid->AddInlinePrefWidth(aRenderingContext,
                                 static_cast<InlinePrefWidthData*>(aData));
@@ -874,22 +896,18 @@ nsContainerFrame::DoInlineIntrinsicWidth
 
   // This goes at the end no matter how things are broken and how
   // messy the bidi situations are, since per CSS2.1 section 8.6
   // (implemented in bug 328168), the endSide border is always on the
   // last line.
   // We reached the last-in-flow, but it might have a next bidi
   // continuation, in which case that continuation should handle
   // the endSide border.
-  if (!lastInFlow->GetNextContinuation()) {
-    aData->currentLine +=
-      // clamp negative calc() to 0
-      std::max(GetCoord(stylePadding->mPadding.Get(endSide), 0), 0) +
-      styleBorder->GetComputedBorderWidth(endSide) +
-      GetCoord(styleMargin->mMargin.Get(endSide), 0);
+  if (MOZ_LIKELY(!lastInFlow->GetNextContinuation() && sliceBreak)) {
+    aData->currentLine += endPBM;
   }
 }
 
 /* virtual */ nsSize
 nsContainerFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
                                   nsSize aCBSize, nscoord aAvailableWidth,
                                   nsSize aMargin, nsSize aBorder,
                                   nsSize aPadding, bool aShrinkWrap)
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -946,16 +946,21 @@ nsIFrame::GetUsedPadding() const
     NS_ASSERTION(hasPadding, "We should have padding here! (out of memory?)");
   }
   return padding;
 }
 
 int
 nsIFrame::GetSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   // Convert the logical skip sides to physical sides using the frame's
   // writing mode
   WritingMode writingMode = GetWritingMode();
   int logicalSkip = GetLogicalSkipSides(aReflowState);
   int skip = 0;
 
   if (logicalSkip & LOGICAL_SIDE_B_START) {
     if (writingMode.IsVertical()) {
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1777,16 +1777,20 @@ nsImageFrame::List(FILE* out, const char
   }
   fprintf_stderr(out, "%s\n", str.get());
 }
 #endif
 
 int
 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
   int skip = 0;
   if (nullptr != GetPrevInFlow()) {
     skip |= LOGICAL_SIDE_B_START;
   }
   if (nullptr != GetNextInFlow()) {
     skip |= LOGICAL_SIDE_B_END;
   }
   return skip;
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -110,17 +110,21 @@ nsInlineFrame::IsSelfEmpty()
     border->GetComputedBorderWidth(NS_SIDE_RIGHT) != 0 ||
     !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetRight()) ||
     !IsMarginZero(margin->mMargin.GetRight());
   bool haveLeft =
     border->GetComputedBorderWidth(NS_SIDE_LEFT) != 0 ||
     !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetLeft()) ||
     !IsMarginZero(margin->mMargin.GetLeft());
   if (haveLeft || haveRight) {
-    if (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
+    // We skip this block and return false for box-decoration-break:clone since
+    // in that case all the continuations will have the border/padding/margin.
+    if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
+        StyleBorder()->mBoxDecorationBreak ==
+          NS_STYLE_BOX_DECORATION_BREAK_SLICE) {
       bool haveStart, haveEnd;
       if (NS_STYLE_DIRECTION_LTR == StyleVisibility()->mDirection) {
         haveStart = haveLeft;
         haveEnd = haveRight;
       } else {
         haveStart = haveRight;
         haveEnd = haveLeft;
       }
@@ -486,19 +490,25 @@ nsInlineFrame::ReflowFrames(nsPresContex
   nsresult rv = NS_OK;
   aStatus = NS_FRAME_COMPLETE;
 
   nsLineLayout* lineLayout = aReflowState.mLineLayout;
   bool inFirstLine = aReflowState.mLineLayout->GetInFirstLine();
   RestyleManager* restyleManager = aPresContext->RestyleManager();
   WritingMode wm = aReflowState.GetWritingMode();
   nscoord startEdge = 0;
+  const bool boxDecorationBreakClone =
+    MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                   NS_STYLE_BOX_DECORATION_BREAK_CLONE);
   // Don't offset by our start borderpadding if we have a prev continuation or
-  // if we're in a part of an {ib} split other than the first one.
-  if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) {
+  // if we're in a part of an {ib} split other than the first one. For
+  // box-decoration-break:clone we always offset our start since all
+  // continuations have border/padding.
+  if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
+      boxDecorationBreakClone) {
     startEdge = aReflowState.ComputedLogicalBorderPadding().IStart(wm);
   }
   nscoord availableISize = aReflowState.AvailableISize();
   NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
                "should no longer use available widths");
   // Subtract off inline axis border+padding from availableISize
   availableISize -= startEdge;
   availableISize -= aReflowState.ComputedLogicalBorderPadding().IEnd(wm);
@@ -649,31 +659,35 @@ nsInlineFrame::ReflowFrames(nsPresContex
   // that are empty we force to empty so that things like collapsed
   // whitespace in an inline element don't affect the line-height.
   aMetrics.ISize() = lineLayout->EndSpan(this);
 
   // Compute final width.
 
   // Make sure to not include our start border and padding if we have a prev
   // continuation or if we're in a part of an {ib} split other than the first
-  // one.
-  if (!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) {
+  // one.  For box-decoration-break:clone we always include our start border
+  // and padding since all continuations have them.
+  if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
+      boxDecorationBreakClone) {
     aMetrics.ISize() += aReflowState.ComputedLogicalBorderPadding().IStart(wm);
   }
 
   /*
    * We want to only apply the end border and padding if we're the last
    * continuation and either not in an {ib} split or the last part of it.  To
    * be the last continuation we have to be complete (so that we won't get a
    * next-in-flow) and have no non-fluid continuations on our continuation
-   * chain.
+   * chain.  For box-decoration-break:clone we always apply the end border and
+   * padding since all continuations have them.
    */
-  if (NS_FRAME_IS_COMPLETE(aStatus) &&
-      !LastInFlow()->GetNextContinuation() &&
-      !FrameIsNonLastInIBSplit()) {
+  if ((NS_FRAME_IS_COMPLETE(aStatus) &&
+       !LastInFlow()->GetNextContinuation() &&
+       !FrameIsNonLastInIBSplit()) ||
+      boxDecorationBreakClone) {
     aMetrics.Width() += aReflowState.ComputedLogicalBorderPadding().IEnd(wm);
   }
 
   nsRefPtr<nsFontMetrics> fm;
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
   aReflowState.rendContext->SetFont(fm);
 
@@ -867,16 +881,21 @@ nsInlineFrame::PushFrames(nsPresContext*
 }
 
 
 //////////////////////////////////////////////////////////////////////
 
 int
 nsInlineFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
   if (!IsFirst()) {
     nsInlineFrame* prev = (nsInlineFrame*) GetPrevContinuation();
     if ((GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET) ||
         (prev && (prev->mRect.height || prev->mRect.width))) {
       // Prev continuation is not empty therefore we don't render our start
       // border edge.
       skip |= LOGICAL_SIDE_I_START;
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1071,19 +1071,22 @@ nsLineLayout::ApplyStartMargin(PerFrameD
   WritingMode frameWM = pfd->mFrame->GetWritingMode();
   WritingMode lineWM = mRootSpan->mWritingMode;
 
   // Only apply start-margin on the first-in flow for inline frames,
   // and make sure to not apply it to any inline other than the first
   // in an ib split.  Note that the ib sibling (block-in-inline
   // sibling) annotations only live on the first continuation, but we
   // don't want to apply the start margin for later continuations
-  // anyway.
-  if (pfd->mFrame->GetPrevContinuation() ||
-      pfd->mFrame->FrameIsNonFirstInIBSplit()) {
+  // anyway.  For box-decoration-break:clone we apply the start-margin
+  // on all continuations.
+  if ((pfd->mFrame->GetPrevContinuation() ||
+       pfd->mFrame->FrameIsNonFirstInIBSplit()) &&
+      aReflowState.mStyleBorder->mBoxDecorationBreak ==
+        NS_STYLE_BOX_DECORATION_BREAK_SLICE) {
     // Zero this out so that when we compute the max-element-width of
     // the frame we will properly avoid adding in the starting margin.
     pfd->mMargin.IStart(frameWM) = 0;
   }
   if ((pfd->mFrame->LastInFlow()->GetNextContinuation() ||
       pfd->mFrame->FrameIsNonLastInIBSplit())
     && !pfd->GetFlag(PFD_ISLETTERFRAME)) {
     pfd->mMargin.IEnd(frameWM) = 0;
@@ -1155,25 +1158,30 @@ nsLineLayout::CanPlaceFrame(PerFrameData
    * 1) The frame is not complete (in this case it will get a next-in-flow)
    * 2) The frame is complete but has a non-fluid continuation on its
    *    continuation chain.  Note that if it has a fluid continuation, that
    *    continuation will get destroyed later, so we don't want to drop the
    *    end-margin in that case.
    * 3) The frame is in an {ib} split and is not the last part.
    *
    * However, none of that applies if this is a letter frame (XXXbz why?)
+   *
+   * For box-decoration-break:clone we apply the end margin on all
+   * continuations (that are not letter frames).
    */
   if (pfd->mFrame->GetPrevContinuation() ||
       pfd->mFrame->FrameIsNonFirstInIBSplit()) {
     pfd->mMargin.IStart(frameWM) = 0;
   }
   if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) ||
        pfd->mFrame->LastInFlow()->GetNextContinuation() ||
-       pfd->mFrame->FrameIsNonLastInIBSplit())
-      && !pfd->GetFlag(PFD_ISLETTERFRAME)) {
+       pfd->mFrame->FrameIsNonLastInIBSplit()) &&
+      !pfd->GetFlag(PFD_ISLETTERFRAME) &&
+      pfd->mFrame->StyleBorder()->mBoxDecorationBreak ==
+        NS_STYLE_BOX_DECORATION_BREAK_SLICE) {
     pfd->mMargin.IEnd(frameWM) = 0;
   }
   LogicalMargin usedMargins = pfd->mMargin.ConvertTo(lineWM, frameWM);
   nscoord startMargin = usedMargins.IStart(lineWM);
   nscoord endMargin = usedMargins.IEnd(lineWM);
 
   if (!(lineWM.IsBidiLTR() && frameWM.IsBidiLTR())) {
     pfd->mBounds.IStart(lineWM) += startMargin;
--- a/layout/generic/nsSplittableFrame.cpp
+++ b/layout/generic/nsSplittableFrame.cpp
@@ -248,18 +248,22 @@ nsSplittableFrame::GetEffectiveComputedH
 
 int
 nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
   if (IS_TRUE_OVERFLOW_CONTAINER(this)) {
     return LOGICAL_SIDES_B_BOTH;
   }
 
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
-
   if (GetPrevInFlow()) {
     skip |= LOGICAL_SIDE_B_START;
   }
 
   if (aReflowState) {
     // We're in the midst of reflow right now, so it's possible that we haven't
     // created a nif yet. If our content height is going to exceed our available
     // height, though, then we're going to need a next-in-flow, it just hasn't
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -545,16 +545,21 @@ nsTableCellFrame::BuildDisplayList(nsDis
   // which isn't right (e.g., would end up on top of our child floats for
   // event handling).
   BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
 }
 
 int
 nsTableCellFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
   if (nullptr != GetPrevInFlow()) {
     skip |= LOGICAL_SIDE_B_START;
   }
   if (nullptr != GetNextInFlow()) {
     skip |= LOGICAL_SIDE_B_END;
   }
   return skip;
--- a/layout/tables/nsTableColGroupFrame.cpp
+++ b/layout/tables/nsTableColGroupFrame.cpp
@@ -338,16 +338,21 @@ nsTableColGroupFrame::RemoveFrame(ChildL
   }
 
   return NS_OK;
 }
 
 int
 nsTableColGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
   if (nullptr != GetPrevInFlow()) {
     skip |= 1 << LOGICAL_SIDE_B_START;
   }
   if (nullptr != GetNextInFlow()) {
     skip |= 1 << LOGICAL_SIDE_B_END;
   }
   return skip;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1404,16 +1404,21 @@ nsTableFrame::PaintTableBorderBackground
       PaintBCBorders(aRenderingContext, aDirtyRect - aPt);
     }
   }
 }
 
 int
 nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
   // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
   // account for pagination
   if (nullptr != GetPrevInFlow()) {
     skip |= LOGICAL_SIDE_B_START;
   }
   if (nullptr != GetNextInFlow()) {
     skip |= LOGICAL_SIDE_B_END;
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -600,16 +600,21 @@ nsTableRowFrame::BuildDisplayList(nsDisp
     }
   }
   nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
 }
 
 int
 nsTableRowFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
   if (nullptr != GetPrevInFlow()) {
     skip |= LOGICAL_SIDE_B_START;
   }
   if (nullptr != GetNextInFlow()) {
     skip |= LOGICAL_SIDE_B_END;
   }
   return skip;
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -251,16 +251,21 @@ nsTableRowGroupFrame::BuildDisplayList(n
   }  
   nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect,
                                         aLists, item, DisplayRows);
 }
 
 int
 nsTableRowGroupFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
+  if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
+                     NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
+    return 0;
+  }
+
   int skip = 0;
   if (nullptr != GetPrevInFlow()) {
     skip |= LOGICAL_SIDE_B_START;
   }
   if (nullptr != GetNextInFlow()) {
     skip |= LOGICAL_SIDE_B_END;
   }
   return skip;