From b241a84d884293e1ce49a79c75e362c02c0899f9 Mon Sep 17 00:00:00 2001 From: yami <34216515+kn-yami@users.noreply.github.com> Date: Sun, 16 Dec 2018 16:17:20 +0100 Subject: issue #908 - implement missing parts of CSS mask --- layout/base/nsCSSRendering.cpp | 215 ++++++++++++++++++++++++++++++++--------- layout/base/nsDisplayList.cpp | 22 ++--- layout/base/nsDisplayList.h | 8 +- layout/base/nsLayoutUtils.cpp | 127 +++++++++++++++++++++++- layout/base/nsLayoutUtils.h | 5 + 5 files changed, 319 insertions(+), 58 deletions(-) (limited to 'layout/base') diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 71ebfad22..ff9edf742 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1803,6 +1803,44 @@ SetupDirtyRects(const nsRect& aBGClipArea, const nsRect& aCallerDirtyRect, "second should be empty if first is"); } +static bool +IsSVGStyleGeometryBox(StyleGeometryBox aBox) +{ + return (aBox == StyleGeometryBox::Fill || aBox == StyleGeometryBox::Stroke || + aBox == StyleGeometryBox::View); +} + +static bool +IsHTMLStyleGeometryBox(StyleGeometryBox aBox) +{ + return (aBox == StyleGeometryBox::Content || + aBox == StyleGeometryBox::Padding || + aBox == StyleGeometryBox::Border || + aBox == StyleGeometryBox::Margin); +} + +static StyleGeometryBox +ComputeBoxValue(nsIFrame* aForFrame, StyleGeometryBox aBox) +{ + // Except , all svg elements are not associate with CSS layout box. + if (aForFrame->IsFrameOfType(nsIFrame::eSVG) && + (aForFrame->GetType() != nsGkAtoms::svgOuterSVGFrame)) { + // For SVG elements without associated CSS layout box, the values + // content-box, padding-box, border-box and margin-box compute to fill-box. + if (IsHTMLStyleGeometryBox(aBox)) { + return StyleGeometryBox::Fill; + } + } else { + // For elements with associated CSS layout box, the values fill-box, + // stroke-box and view-box compute to the initial value of mask-clip. + if (IsSVGStyleGeometryBox(aBox)) { + return StyleGeometryBox::Border; + } + } + + return aBox; +} + /* static */ void nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer, nsIFrame* aForFrame, const nsStyleBorder& aBorder, @@ -1810,6 +1848,55 @@ nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer, bool aWillPaintBorder, nscoord aAppUnitsPerPixel, /* out */ ImageLayerClipState* aClipState) { + StyleGeometryBox layerClip = ComputeBoxValue(aForFrame, aLayer.mClip); + + if (IsSVGStyleGeometryBox(layerClip)) { + MOZ_ASSERT(aForFrame->IsFrameOfType(nsIFrame::eSVG) && + (aForFrame->GetType() != nsGkAtoms::svgOuterSVGFrame)); + + aClipState->mHasAdditionalBGClipArea = false; + aClipState->mCustomClip = false; + + // The coordinate space of clipArea is svg user space. + nsRect clipArea = + nsLayoutUtils::ComputeGeometryBox(aForFrame, layerClip); + + nsRect strokeBox = (layerClip == StyleGeometryBox::Stroke) + ? clipArea + : nsLayoutUtils::ComputeGeometryBox(aForFrame, StyleGeometryBox::Stroke); + nsRect clipAreaRelativeToStrokeBox = clipArea - strokeBox.TopLeft(); + + // aBorderArea is the stroke-box area in a coordinate space defined by + // the caller. This coordinate space can be svg user space of aForFrame, + // the space of aForFrame's reference-frame, or anything else. + // + // Which coordinate space chosen for aBorderArea is not matter. What + // matter is to ensure returning aClipState->mBGClipArea in the consistent + // coordiante space with aBorderArea. So we evaluate the position of clip + // area base on the position of aBorderArea here. + aClipState->mBGClipArea = + clipAreaRelativeToStrokeBox + aBorderArea.TopLeft(); + + SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect, + aAppUnitsPerPixel, &aClipState->mDirtyRect, + &aClipState->mDirtyRectGfx); + return; + } + + if (layerClip == StyleGeometryBox::NoClip) { + aClipState->mBGClipArea = aCallerDirtyRect; + aClipState->mHasAdditionalBGClipArea = false; + aClipState->mCustomClip = false; + + SetupDirtyRects(aClipState->mBGClipArea, aCallerDirtyRect, + aAppUnitsPerPixel, &aClipState->mDirtyRect, + &aClipState->mDirtyRectGfx); + return; + } + + MOZ_ASSERT(!aForFrame->IsFrameOfType(nsIFrame::eSVG) || + aForFrame->GetType() == nsGkAtoms::svgOuterSVGFrame); + // Compute the outermost boundary of the area that might be painted. // Same coordinate space as aBorderArea. Sides skipSides = aForFrame->GetSkipSides(); @@ -1819,16 +1906,15 @@ nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer, bool haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea, clipBorderArea, aClipState->mRadii); - uint8_t backgroundClip = aLayer.mClip; - bool isSolidBorder = aWillPaintBorder && IsOpaqueBorder(aBorder); - if (isSolidBorder && backgroundClip == NS_STYLE_IMAGELAYER_CLIP_BORDER) { + if (isSolidBorder && layerClip == StyleGeometryBox::Border) { // If we have rounded corners, we need to inflate the background // drawing area a bit to avoid seams between the border and // background. - backgroundClip = haveRoundedCorners ? - NS_STYLE_IMAGELAYER_CLIP_MOZ_ALMOST_PADDING : NS_STYLE_IMAGELAYER_CLIP_PADDING; + layerClip = haveRoundedCorners + ? StyleGeometryBox::MozAlmostPadding + : StyleGeometryBox::Padding; } aClipState->mBGClipArea = clipBorderArea; @@ -1844,7 +1930,7 @@ nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer, // but the background is also clipped at a non-scrolling 'padding-box' // like the content. (See below.) // Therefore, only 'content-box' makes a difference here. - if (backgroundClip == NS_STYLE_IMAGELAYER_CLIP_CONTENT) { + if (layerClip == StyleGeometryBox::Content) { nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame); // Clip at a rectangle attached to the scrolled content. aClipState->mHasAdditionalBGClipArea = true; @@ -1864,22 +1950,30 @@ nsCSSRendering::GetImageLayerClip(const nsStyleImageLayers::Layer& aLayer, // Also clip at a non-scrolling, rounded-corner 'padding-box', // same as the scrolled content because of the 'overflow' property. - backgroundClip = NS_STYLE_IMAGELAYER_CLIP_PADDING; + layerClip = StyleGeometryBox::Padding; } - if (backgroundClip != NS_STYLE_IMAGELAYER_CLIP_BORDER && - backgroundClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) { + // See the comment of StyleGeometryBox::Margin. + // Hitting this assertion means we decide to turn on margin-box support for + // positioned mask from CSS parser and style system. In this case, you + // should *inflate* mBGClipArea by the margin returning from + // aForFrame->GetUsedMargin() in the code chunk bellow. + MOZ_ASSERT(layerClip != StyleGeometryBox::Margin, + "StyleGeometryBox::Margin rendering is not supported yet.\n"); + + if (layerClip != StyleGeometryBox::Border && + layerClip != StyleGeometryBox::Text) { nsMargin border = aForFrame->GetUsedBorder(); - if (backgroundClip == NS_STYLE_IMAGELAYER_CLIP_MOZ_ALMOST_PADDING) { + if (layerClip == StyleGeometryBox::MozAlmostPadding) { // Reduce |border| by 1px (device pixels) on all sides, if // possible, so that we don't get antialiasing seams between the - // background and border. + // {background|mask} and border. border.top = std::max(0, border.top - aAppUnitsPerPixel); border.right = std::max(0, border.right - aAppUnitsPerPixel); border.bottom = std::max(0, border.bottom - aAppUnitsPerPixel); border.left = std::max(0, border.left - aAppUnitsPerPixel); - } else if (backgroundClip != NS_STYLE_IMAGELAYER_CLIP_PADDING) { - NS_ASSERTION(backgroundClip == NS_STYLE_IMAGELAYER_CLIP_CONTENT, + } else if (layerClip != StyleGeometryBox::Padding) { + NS_ASSERTION(layerClip == StyleGeometryBox::Content, "unexpected background-clip"); border += aForFrame->GetUsedPadding(); } @@ -3163,7 +3257,7 @@ nsCSSRendering::PaintBackgroundWithSC(const PaintBGParams& aParams, if (drawBackgroundImage) { bool clipSet = false; - uint8_t currentBackgroundClip = NS_STYLE_IMAGELAYER_CLIP_BORDER; + StyleGeometryBox currentBackgroundClip = StyleGeometryBox::Border; NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT_WITH_RANGE(i, layers, layers.mImageCount - 1, nLayers + (layers.mImageCount - startLayer - 1)) { @@ -3239,16 +3333,43 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext, nsIFrame** aAttachedToFrame, bool* aOutIsTransformedFixed) { - // Compute background origin area relative to aBorderArea now as we may need - // it to compute the effective image size for a CSS gradient. - nsRect bgPositioningArea; + // Compute {background|mask} origin area relative to aBorderArea now as we + // may need it to compute the effective image size for a CSS gradient. + nsRect positionArea; + + StyleGeometryBox layerOrigin = + ComputeBoxValue(aForFrame, aLayer.mOrigin); + + if (IsSVGStyleGeometryBox(layerOrigin)) { + MOZ_ASSERT(aForFrame->IsFrameOfType(nsIFrame::eSVG) && + (aForFrame->GetType() != nsGkAtoms::svgOuterSVGFrame)); + *aAttachedToFrame = aForFrame; + + positionArea = + nsLayoutUtils::ComputeGeometryBox(aForFrame, layerOrigin); + + nsPoint toStrokeBoxOffset = nsPoint(0, 0); + if (layerOrigin != StyleGeometryBox::Stroke) { + nsRect strokeBox = + nsLayoutUtils::ComputeGeometryBox(aForFrame, + StyleGeometryBox::Stroke); + toStrokeBoxOffset = positionArea.TopLeft() - strokeBox.TopLeft(); + } + + // For SVG frames, the return value is relative to the stroke box + return nsRect(toStrokeBoxOffset, positionArea.Size()); + } + + MOZ_ASSERT(!aForFrame->IsFrameOfType(nsIFrame::eSVG) || + aForFrame->GetType() == nsGkAtoms::svgOuterSVGFrame); + nsIAtom* frameType = aForFrame->GetType(); nsIFrame* geometryFrame = aForFrame; if (MOZ_UNLIKELY(frameType == nsGkAtoms::scrollFrame && NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL == aLayer.mAttachment)) { nsIScrollableFrame* scrollableFrame = do_QueryFrame(aForFrame); - bgPositioningArea = nsRect( + positionArea = nsRect( scrollableFrame->GetScrolledFrame()->GetPosition() // For the dir=rtl case: + scrollableFrame->GetScrollRange().TopLeft(), @@ -3256,20 +3377,20 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext, // The ScrolledRect’s size does not include the borders or scrollbars, // reverse the handling of background-origin // compared to the common case below. - if (aLayer.mOrigin == NS_STYLE_IMAGELAYER_ORIGIN_BORDER) { + if (layerOrigin == StyleGeometryBox::Border) { nsMargin border = geometryFrame->GetUsedBorder(); border.ApplySkipSides(geometryFrame->GetSkipSides()); - bgPositioningArea.Inflate(border); - bgPositioningArea.Inflate(scrollableFrame->GetActualScrollbarSizes()); - } else if (aLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_PADDING) { + positionArea.Inflate(border); + positionArea.Inflate(scrollableFrame->GetActualScrollbarSizes()); + } else if (layerOrigin != StyleGeometryBox::Padding) { nsMargin padding = geometryFrame->GetUsedPadding(); padding.ApplySkipSides(geometryFrame->GetSkipSides()); - bgPositioningArea.Deflate(padding); - NS_ASSERTION(aLayer.mOrigin == NS_STYLE_IMAGELAYER_ORIGIN_CONTENT, + positionArea.Deflate(padding); + NS_ASSERTION(layerOrigin == StyleGeometryBox::Content, "unknown background-origin value"); } *aAttachedToFrame = aForFrame; - return bgPositioningArea; + return positionArea; } if (MOZ_UNLIKELY(frameType == nsGkAtoms::canvasFrame)) { @@ -3279,25 +3400,31 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext, // finished and this page only displays the continuations of // absolutely positioned content). if (geometryFrame) { - bgPositioningArea = geometryFrame->GetRect(); + positionArea = geometryFrame->GetRect(); } } else { - bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size()); + positionArea = nsRect(nsPoint(0,0), aBorderArea.Size()); } - // Background images are tiled over the 'background-clip' area - // but the origin of the tiling is based on the 'background-origin' area - // XXX: Bug 1303623 will bring in new origin value, we should iterate from - // NS_STYLE_IMAGELAYER_ORIGIN_MARGIN instead of - // NS_STYLE_IMAGELAYER_ORIGIN_BORDER. - if (aLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_BORDER && geometryFrame) { + // See the comment of StyleGeometryBox::Margin. + // Hitting this assertion means we decide to turn on margin-box support for + // positioned mask from CSS parser and style system. In this case, you + // should *inflate* positionArea by the margin returning from + // geometryFrame->GetUsedMargin() in the code chunk bellow. + MOZ_ASSERT(aLayer.mOrigin != StyleGeometryBox::Margin, + "StyleGeometryBox::Margin rendering is not supported yet.\n"); + + // {background|mask} images are tiled over the '{background|mask}-clip' area + // but the origin of the tiling is based on the '{background|mask}-origin' + // area. + if (layerOrigin != StyleGeometryBox::Border && geometryFrame) { nsMargin border = geometryFrame->GetUsedBorder(); - if (aLayer.mOrigin != NS_STYLE_IMAGELAYER_ORIGIN_PADDING) { + if (layerOrigin != StyleGeometryBox::Padding) { border += geometryFrame->GetUsedPadding(); - NS_ASSERTION(aLayer.mOrigin == NS_STYLE_IMAGELAYER_ORIGIN_CONTENT, + NS_ASSERTION(layerOrigin == StyleGeometryBox::Content, "unknown background-origin value"); } - bgPositioningArea.Deflate(border); + positionArea.Deflate(border); } nsIFrame* attachedToFrame = aForFrame; @@ -3325,7 +3452,7 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext, } else { // Set the background positioning area to the viewport's area // (relative to aForFrame) - bgPositioningArea = + positionArea = nsRect(-aForFrame->GetOffsetTo(attachedToFrame), attachedToFrame->GetSize()); if (!pageContentFrame) { @@ -3334,14 +3461,14 @@ nsCSSRendering::ComputeImageLayerPositioningArea(nsPresContext* aPresContext, aPresContext->PresShell()->GetRootScrollFrameAsScrollable(); if (scrollableFrame) { nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes(); - bgPositioningArea.Deflate(scrollbars); + positionArea.Deflate(scrollbars); } } } } *aAttachedToFrame = attachedToFrame; - return bgPositioningArea; + return positionArea; } // Implementation of the formula for computation of background-repeat round @@ -3569,7 +3696,7 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, bool transformedFixed = false; // Compute background origin area relative to aBorderArea now as we may need // it to compute the effective image size for a CSS gradient. - nsRect bgPositioningArea = + nsRect positionArea = ComputeImageLayerPositioningArea(aPresContext, aForFrame, aBorderArea, aLayer, &attachedToFrame, &transformedFixed); if (aOutIsTransformedFixed) { @@ -3594,7 +3721,7 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, // not a pure optimization since it can affect the values of pixels at the // edge of the viewport --- whether they're sampled from a putative "next // tile" or not.) - bgClipRect.IntersectRect(bgClipRect, bgPositioningArea + aBorderArea.TopLeft()); + bgClipRect.IntersectRect(bgClipRect, positionArea + aBorderArea.TopLeft()); } } @@ -3605,7 +3732,7 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, // Also as required for proper background positioning when background-position // is defined with percentages. CSSSizeOrRatio intrinsicSize = state.mImageRenderer.ComputeIntrinsicSize(); - nsSize bgPositionSize = bgPositioningArea.Size(); + nsSize bgPositionSize = positionArea.Size(); nsSize imageSize = ComputeDrawnSizeForBackground(intrinsicSize, bgPositionSize, aLayer.mSize, @@ -3650,8 +3777,8 @@ nsCSSRendering::PrepareImageLayer(nsPresContext* aPresContext, } } - imageTopLeft += bgPositioningArea.TopLeft(); - state.mAnchor += bgPositioningArea.TopLeft(); + imageTopLeft += positionArea.TopLeft(); + state.mAnchor += positionArea.TopLeft(); state.mDestArea = nsRect(imageTopLeft + aBorderArea.TopLeft(), imageSize); state.mFillArea = state.mDestArea; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 2b9ad7ff8..d619576ba 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -3014,7 +3014,7 @@ nsDisplayBackgroundImage::ComputeVisibility(nsDisplayListBuilder* aBuilder, /* static */ nsRegion nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem, - uint8_t aClip, + StyleGeometryBox aClip, const nsRect& aRect, const nsRect& aBackgroundRect) { @@ -3028,10 +3028,10 @@ nsDisplayBackgroundImage::GetInsideClipRegion(nsDisplayItem* aItem, if (frame->GetType() == nsGkAtoms::canvasFrame) { nsCanvasFrame* canvasFrame = static_cast(frame); clipRect = canvasFrame->CanvasArea() + aItem->ToReferenceFrame(); - } else if (aClip == NS_STYLE_IMAGELAYER_CLIP_PADDING || - aClip == NS_STYLE_IMAGELAYER_CLIP_CONTENT) { + } else if (aClip == StyleGeometryBox::Padding || + aClip == StyleGeometryBox::Content) { nsMargin border = frame->GetUsedBorder(); - if (aClip == NS_STYLE_IMAGELAYER_CLIP_CONTENT) { + if (aClip == StyleGeometryBox::Content) { border += frame->GetUsedPadding(); } border.ApplySkipSides(frame->GetSkipSides()); @@ -3064,7 +3064,7 @@ nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL && layer.mRepeat.mXRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE && layer.mRepeat.mYRepeat != NS_STYLE_IMAGELAYER_REPEAT_SPACE && - layer.mClip != NS_STYLE_IMAGELAYER_CLIP_TEXT) { + layer.mClip != StyleGeometryBox::Text) { result = GetInsideClipRegion(this, layer.mClip, mBounds, mBackgroundRect); } } @@ -3143,9 +3143,9 @@ nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, CheckForBorderItem(this, flags); gfxContext* ctx = aCtx->ThebesContext(); - uint8_t clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip; + StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip; - if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + if (clip == StyleGeometryBox::Text) { if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) { return; } @@ -3161,7 +3161,7 @@ nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, image::DrawResult result = nsCSSRendering::PaintBackground(params); - if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + if (clip == StyleGeometryBox::Text) { ctx->PopGroupAndBlend(); } @@ -3583,8 +3583,8 @@ nsDisplayBackgroundColor::Paint(nsDisplayListBuilder* aBuilder, nsLayoutUtils::RectToGfxRect(mBackgroundRect, mFrame->PresContext()->AppUnitsPerDevPixel()); - uint8_t clip = mBackgroundStyle->mImage.mLayers[0].mClip; - if (clip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[0].mClip; + if (clip == StyleGeometryBox::Text) { if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) { return; } @@ -3618,7 +3618,7 @@ nsDisplayBackgroundColor::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, const nsStyleImageLayers::Layer& bottomLayer = mBackgroundStyle->BottomLayer(); - if (bottomLayer.mClip == NS_STYLE_IMAGELAYER_CLIP_TEXT) { + if (bottomLayer.mClip == StyleGeometryBox::Text) { return nsRegion(); } diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 9603a95b8..c9f773f5b 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -2713,6 +2713,8 @@ private: */ class nsDisplayBackgroundImage : public nsDisplayImageContainer { public: + typedef mozilla::StyleGeometryBox StyleGeometryBox; + /** * aLayer signifies which background layer this item represents. * aIsThemed should be the value of aFrame->IsThemed. @@ -2790,8 +2792,10 @@ public: virtual already_AddRefed GetImage() override; virtual nsRect GetDestRect() override; - static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, uint8_t aClip, - const nsRect& aRect, const nsRect& aBackgroundRect); + static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, + StyleGeometryBox aClip, + const nsRect& aRect, + const nsRect& aBackgroundRect); virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) override; diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 19200d5a7..f0341f9ef 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -117,6 +117,7 @@ #include "mozilla/StyleSetHandle.h" #include "mozilla/StyleSetHandleInlines.h" #include "RegionBuilder.h" +#include "SVGSVGElement.h" #ifdef MOZ_XUL #include "nsXULPopupManager.h" @@ -6935,7 +6936,7 @@ nsLayoutUtils::GetFrameTransparency(nsIFrame* aBackgroundFrame, const nsStyleBackground* bg = bgSC->StyleBackground(); if (NS_GET_A(bg->mBackgroundColor) < 255 || // bottom layer's clip is used for the color - bg->BottomLayer().mClip != NS_STYLE_IMAGELAYER_CLIP_BORDER) + bg->BottomLayer().mClip != StyleGeometryBox::Border) return eTransparencyTransparent; return eTransparencyOpaque; } @@ -9293,3 +9294,127 @@ nsLayoutUtils::IsInvisibleBreak(nsINode* aNode, nsIFrame** aNextLineFrame) return lineNonEmpty; } + +static nsRect +ComputeSVGReferenceRect(nsIFrame* aFrame, + StyleGeometryBox aGeometryBox) +{ + MOZ_ASSERT(aFrame->GetContent()->IsSVGElement()); + nsRect r; + + // For SVG elements without associated CSS layout box, the used value for + // content-box, padding-box, border-box and margin-box is fill-box. + switch (aGeometryBox) { + case StyleGeometryBox::Stroke: { + // XXX Bug 1299876 + // The size of srtoke-box is not correct if this graphic element has + // specific stroke-linejoin or stroke-linecap. + gfxRect bbox = nsSVGUtils::GetBBox(aFrame, + nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeStroke); + r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, + nsPresContext::AppUnitsPerCSSPixel()); + break; + } + case StyleGeometryBox::View: { + nsIContent* content = aFrame->GetContent(); + nsSVGElement* element = static_cast(content); + SVGSVGElement* svgElement = element->GetCtx(); + MOZ_ASSERT(svgElement); + + if (svgElement && svgElement->HasViewBoxRect()) { + // If a ‘viewBox‘ attribute is specified for the SVG viewport creating + // element: + // 1. The reference box is positioned at the origin of the coordinate + // system established by the ‘viewBox‘ attribute. + // 2. The dimension of the reference box is set to the width and height + // values of the ‘viewBox‘ attribute. + nsSVGViewBox* viewBox = svgElement->GetViewBox(); + const nsSVGViewBoxRect& value = viewBox->GetAnimValue(); + r = nsRect(nsPresContext::CSSPixelsToAppUnits(value.x), + nsPresContext::CSSPixelsToAppUnits(value.y), + nsPresContext::CSSPixelsToAppUnits(value.width), + nsPresContext::CSSPixelsToAppUnits(value.height)); + } else { + // No viewBox is specified, uses the nearest SVG viewport as reference + // box. + svgFloatSize viewportSize = svgElement->GetViewportSize(); + r = nsRect(0, 0, + nsPresContext::CSSPixelsToAppUnits(viewportSize.width), + nsPresContext::CSSPixelsToAppUnits(viewportSize.height)); + } + + break; + } + case StyleGeometryBox::NoBox: + case StyleGeometryBox::Border: + case StyleGeometryBox::Content: + case StyleGeometryBox::Padding: + case StyleGeometryBox::Margin: + case StyleGeometryBox::Fill: { + gfxRect bbox = nsSVGUtils::GetBBox(aFrame, + nsSVGUtils::eBBoxIncludeFill); + r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, + nsPresContext::AppUnitsPerCSSPixel()); + break; + } + default:{ + MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type"); + gfxRect bbox = nsSVGUtils::GetBBox(aFrame, + nsSVGUtils::eBBoxIncludeFill); + r = nsLayoutUtils::RoundGfxRectToAppRect(bbox, + nsPresContext::AppUnitsPerCSSPixel()); + break; + } + } + + return r; +} + +static nsRect +ComputeHTMLReferenceRect(nsIFrame* aFrame, + StyleGeometryBox aGeometryBox) +{ + nsRect r; + + // For elements with associated CSS layout box, the used value for fill-box, + // stroke-box and view-box is border-box. + switch (aGeometryBox) { + case StyleGeometryBox::Content: + r = aFrame->GetContentRectRelativeToSelf(); + break; + case StyleGeometryBox::Padding: + r = aFrame->GetPaddingRectRelativeToSelf(); + break; + case StyleGeometryBox::Margin: + r = aFrame->GetMarginRectRelativeToSelf(); + break; + case StyleGeometryBox::NoBox: + case StyleGeometryBox::Border: + case StyleGeometryBox::Fill: + case StyleGeometryBox::Stroke: + case StyleGeometryBox::View: + r = aFrame->GetRectRelativeToSelf(); + break; + default: + MOZ_ASSERT_UNREACHABLE("unknown StyleGeometryBox type"); + r = aFrame->GetRectRelativeToSelf(); + break; + } + + return r; +} + +/* static */ nsRect +nsLayoutUtils::ComputeGeometryBox(nsIFrame* aFrame, + StyleGeometryBox aGeometryBox) +{ + // We use ComputeSVGReferenceRect for all SVG elements, except + // element, which does have an associated CSS layout box. In this case we + // should still use ComputeHTMLReferenceRect for region computing. + nsRect r = aFrame->IsFrameOfType(nsIFrame::eSVG) && + (aFrame->GetType() != nsGkAtoms::svgOuterSVGFrame) + ? ComputeSVGReferenceRect(aFrame, aGeometryBox) + : ComputeHTMLReferenceRect(aFrame, aGeometryBox); + + return r; +} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 97fc410b0..63253fd10 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -30,6 +30,7 @@ #include "mozilla/ReflowOutput.h" #include "ImageContainer.h" #include "gfx2DGlue.h" +#include "nsStyleConsts.h" #include #include @@ -152,6 +153,7 @@ public: typedef mozilla::CSSRect CSSRect; typedef mozilla::ScreenMargin ScreenMargin; typedef mozilla::LayoutDeviceIntSize LayoutDeviceIntSize; + typedef mozilla::StyleGeometryBox StyleGeometryBox; /** * Finds previously assigned ViewID for the given content element, if any. @@ -2870,6 +2872,9 @@ public: */ static bool IsInvisibleBreak(nsINode* aNode, nsIFrame** aNextLineFrame = nullptr); + static nsRect ComputeGeometryBox(nsIFrame* aFrame, + StyleGeometryBox aGeometryBox); + private: static uint32_t sFontSizeInflationEmPerLine; static uint32_t sFontSizeInflationMinTwips; -- cgit v1.2.3