diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 2d8bb4616da..0c2be7aa216 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -61,6 +61,19 @@ 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 @@ -81,10 +94,18 @@ struct InlineBackgroundData 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; @@ -98,12 +119,9 @@ struct InlineBackgroundData 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; @@ -140,6 +158,39 @@ struct InlineBackgroundData 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); @@ -157,13 +208,22 @@ struct InlineBackgroundData } 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) { @@ -236,6 +296,7 @@ protected: void Init(nsIFrame* aFrame) { + mLeftBorderData.Reset(); mBidiEnabled = aFrame->PresContext()->BidiEnabled(); if (mBidiEnabled) { // Find the containing block frame @@ -252,8 +313,11 @@ protected: // 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)) { @@ -268,6 +332,10 @@ protected: // 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); @@ -371,6 +439,94 @@ MakeBevelColor(mozilla::css::Side whichSide, uint8_t style, 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 @@ -449,10 +605,6 @@ nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext, 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 @@ -474,59 +626,67 @@ nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext, // 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]; @@ -543,32 +703,25 @@ nsCSSRendering::PaintBorderWithStyleBorder(nsPresContext* aPresContext, } 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(); @@ -688,7 +841,7 @@ nsCSSRendering::PaintOutline(nsPresContext* aPresContext, outlineWidths, outlineRadii, outlineColors, - nullptr, 0, + nullptr, bgColor); br.DrawBorders(); @@ -741,7 +894,7 @@ nsCSSRendering::PaintFocus(nsPresContext* aPresContext, focusWidths, focusRadii, focusColors, - nullptr, 0, + nullptr, NS_RGB(255, 0, 0)); br.DrawBorders(); @@ -996,13 +1149,10 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, 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)) { @@ -1013,17 +1163,29 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, 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(); @@ -1047,6 +1209,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, 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) @@ -1127,7 +1290,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, } 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) { @@ -1139,6 +1302,36 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext, 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; @@ -1188,18 +1381,20 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext, 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) { @@ -1221,13 +1416,9 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext, 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); @@ -4935,10 +5126,8 @@ nsContextBoxBlur::GetBlurRadiusMargin(nscoord aBlurRadius, 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; } diff --git a/layout/base/nsCSSRenderingBorders.cpp b/layout/base/nsCSSRenderingBorders.cpp index 405eb1bca27..05f36195371 100644 --- a/layout/base/nsCSSRenderingBorders.cpp +++ b/layout/base/nsCSSRenderingBorders.cpp @@ -120,7 +120,6 @@ nsCSSBorderRenderer::nsCSSBorderRenderer(int32_t aAppUnitsPerPixel, gfxCornerSizes& aBorderRadii, const nscolor* aBorderColors, nsBorderColors* const* aCompositeColors, - int aSkipSides, nscolor aBackgroundColor) : mContext(aDestContext), mOuterRect(aOuterRect), @@ -130,7 +129,6 @@ nsCSSBorderRenderer::nsCSSBorderRenderer(int32_t aAppUnitsPerPixel, mBorderColors(aBorderColors), mCompositeColors(aCompositeColors), mAUPP(aAppUnitsPerPixel), - mSkipSides(aSkipSides), mBackgroundColor(aBackgroundColor) { if (!mCompositeColors) { diff --git a/layout/base/nsCSSRenderingBorders.h b/layout/base/nsCSSRenderingBorders.h index 41a2415b4ec..461a765abd3 100644 --- a/layout/base/nsCSSRenderingBorders.h +++ b/layout/base/nsCSSRenderingBorders.h @@ -79,7 +79,6 @@ struct nsCSSBorderRenderer { gfxCornerSizes& aBorderRadii, const nscolor* aBorderColors, nsBorderColors* const* aCompositeColors, - int aSkipSides, nscolor aBackgroundColor); gfxCornerSizes mBorderCornerDimensions; @@ -105,8 +104,7 @@ struct nsCSSBorderRenderer { // 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 diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index a055ed52653..bafab1f70a5 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2861,11 +2861,8 @@ nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder, 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(); } } diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 76f0748cf92..43ccd5f91ae 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -837,12 +837,31 @@ nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, // 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; @@ -851,6 +870,9 @@ nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, nsContainerFrame *lastInFlow; for (nsContainerFrame *nif = this; nif; nif = static_cast(nif->GetNextInFlow())) { + if (aData->currentLine == 0) { + aData->currentLine = clonePBM; + } for (nsIFrame *kid = nif->mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { if (aType == nsLayoutUtils::MIN_WIDTH) @@ -879,12 +901,8 @@ nsContainerFrame::DoInlineIntrinsicWidth(nsRenderingContext *aRenderingContext, // 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; } } diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 0034f226325..142eee0f0f0 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -951,6 +951,11 @@ nsIFrame::GetUsedPadding() const 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(); diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index be77bceee01..78c37692b5f 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1782,6 +1782,10 @@ nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const 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; diff --git a/layout/generic/nsInlineFrame.cpp b/layout/generic/nsInlineFrame.cpp index 526041e6364..a59003c8d03 100644 --- a/layout/generic/nsInlineFrame.cpp +++ b/layout/generic/nsInlineFrame.cpp @@ -115,7 +115,11 @@ nsInlineFrame::IsSelfEmpty() !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; @@ -491,9 +495,15 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, 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(); @@ -654,8 +664,10 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, // 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); } @@ -664,11 +676,13 @@ nsInlineFrame::ReflowFrames(nsPresContext* aPresContext, * 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); } @@ -872,6 +886,11 @@ nsInlineFrame::PushFrames(nsPresContext* aPresContext, 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(); diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index fffcfbfc76c..9b74ede0278 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -1076,9 +1076,12 @@ nsLineLayout::ApplyStartMargin(PerFrameData* pfd, // 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; @@ -1160,6 +1163,9 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, * 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()) { @@ -1167,8 +1173,10 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd, } 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); diff --git a/layout/generic/nsSplittableFrame.cpp b/layout/generic/nsSplittableFrame.cpp index 6f35289b3da..8a83e85574c 100644 --- a/layout/generic/nsSplittableFrame.cpp +++ b/layout/generic/nsSplittableFrame.cpp @@ -253,8 +253,12 @@ nsSplittableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) co return LOGICAL_SIDES_B_BOTH; } - int skip = 0; + if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak == + NS_STYLE_BOX_DECORATION_BREAK_CLONE)) { + return 0; + } + int skip = 0; if (GetPrevInFlow()) { skip |= LOGICAL_SIDE_B_START; } diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index ee7304dbff7..0aac7c2dd85 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -550,6 +550,11 @@ nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 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; diff --git a/layout/tables/nsTableColGroupFrame.cpp b/layout/tables/nsTableColGroupFrame.cpp index 37fa8c5befd..071d0d519ff 100644 --- a/layout/tables/nsTableColGroupFrame.cpp +++ b/layout/tables/nsTableColGroupFrame.cpp @@ -343,6 +343,11 @@ nsTableColGroupFrame::RemoveFrame(ChildListID aListID, 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; diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 60613ba7d41..33fa27e0567 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1409,6 +1409,11 @@ nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext, 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 diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index d1c493b629b..67863457e02 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -605,6 +605,11 @@ nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 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; diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index 34aaf02c01c..f8dc2a23488 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -256,6 +256,11 @@ nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 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;