diff options
Diffstat (limited to 'layout/svg')
-rw-r--r-- | layout/svg/SVGGeometryFrame.cpp | 32 | ||||
-rw-r--r-- | layout/svg/SVGGradientFrame.cpp | 3 | ||||
-rw-r--r-- | layout/svg/SVGPatternFrame.cpp | 2 | ||||
-rw-r--r-- | layout/svg/SVGTextFrame.cpp | 169 | ||||
-rw-r--r-- | layout/svg/SVGUtils.h | 6 | ||||
-rw-r--r-- | layout/svg/crashtests/1882921-1.html | 7 | ||||
-rw-r--r-- | layout/svg/crashtests/crashtests.list | 1 |
7 files changed, 152 insertions, 68 deletions
diff --git a/layout/svg/SVGGeometryFrame.cpp b/layout/svg/SVGGeometryFrame.cpp index 7d0bd7cc4c..3d6d6aef7e 100644 --- a/layout/svg/SVGGeometryFrame.cpp +++ b/layout/svg/SVGGeometryFrame.cpp @@ -358,13 +358,31 @@ SVGBBox SVGGeometryFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace, SVGGeometryElement* element = static_cast<SVGGeometryElement*>(GetContent()); - bool getFill = (aFlags & SVGUtils::eBBoxIncludeFillGeometry) || - ((aFlags & SVGUtils::eBBoxIncludeFill) && - !StyleSVG()->mFill.kind.IsNone()); - - bool getStroke = - (aFlags & SVGUtils::eBBoxIncludeStrokeGeometry) || - ((aFlags & SVGUtils::eBBoxIncludeStroke) && SVGUtils::HasStroke(this)); + const bool getFill = (aFlags & SVGUtils::eBBoxIncludeFillGeometry) || + ((aFlags & SVGUtils::eBBoxIncludeFill) && + !StyleSVG()->mFill.kind.IsNone()); + + const bool getStroke = + ((aFlags & SVGUtils::eBBoxIncludeStrokeGeometry) || + ((aFlags & SVGUtils::eBBoxIncludeStroke) && + SVGUtils::HasStroke(this))) && + // If this frame has non-scaling-stroke and we would like to compute its + // stroke, it may cause a potential cyclical dependency if the caller is + // for transform. In this case, we have to fall back to fill-box, so make + // |getStroke| be false. + // https://github.com/w3c/csswg-drafts/issues/9640 + // + // Note: + // 1. We don't care about the computation of the markers below in this + // function because we know the callers don't set + // SVGUtils::eBBoxIncludeMarkers. + // See nsStyleTransformMatrix::GetSVGBox() and + // MotionPathUtils::GetRayContainReferenceSize() for more details. + // 2. We have to break the dependency here *again* because the geometry + // frame may be in the subtree of a SVGContainerFrame, which may not + // set non-scaling-stroke. + !(StyleSVGReset()->HasNonScalingStroke() && + (aFlags & SVGUtils::eAvoidCycleIfNonScalingStroke)); SVGContentUtils::AutoStrokeOptions strokeOptions; if (getStroke) { diff --git a/layout/svg/SVGGradientFrame.cpp b/layout/svg/SVGGradientFrame.cpp index f316c0180d..92558cefcd 100644 --- a/layout/svg/SVGGradientFrame.cpp +++ b/layout/svg/SVGGradientFrame.cpp @@ -157,7 +157,8 @@ gfxMatrix SVGGradientFrame::GetGradientTransform( const SVGAnimatedTransformList* animTransformList = GetGradientTransformList(GetContent()); if (!animTransformList) { - return bboxMatrix; + return bboxMatrix.PreMultiply( + SVGUtils::GetTransformMatrixInUserSpace(this)); } gfxMatrix gradientTransform = diff --git a/layout/svg/SVGPatternFrame.cpp b/layout/svg/SVGPatternFrame.cpp index 20863db17e..7d1e99cedc 100644 --- a/layout/svg/SVGPatternFrame.cpp +++ b/layout/svg/SVGPatternFrame.cpp @@ -467,7 +467,7 @@ gfxMatrix SVGPatternFrame::GetPatternTransform() { SVGAnimatedTransformList* animTransformList = GetPatternTransformList(GetContent()); if (!animTransformList) { - return gfxMatrix(); + return SVGUtils::GetTransformMatrixInUserSpace(this); } return animTransformList->GetAnimValue().GetConsolidationMatrix(); diff --git a/layout/svg/SVGTextFrame.cpp b/layout/svg/SVGTextFrame.cpp index 246be6fe3a..d53637af0a 100644 --- a/layout/svg/SVGTextFrame.cpp +++ b/layout/svg/SVGTextFrame.cpp @@ -800,18 +800,18 @@ SVGBBox TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext, return r; } - // Determine the amount of overflow above and below the frame's mRect. + // Determine the amount of overflow around frame's mRect. // // We need to call InkOverflowRectRelativeToSelf because this includes - // overflowing decorations, which the MeasureText call below does not. We - // assume here the decorations only overflow above and below the frame, never - // horizontally. + // overflowing decorations, which the MeasureText call below does not. nsRect self = mFrame->InkOverflowRectRelativeToSelf(); nsRect rect = mFrame->GetRect(); bool vertical = IsVertical(); - nscoord above = vertical ? -self.x : -self.y; - nscoord below = - vertical ? self.XMost() - rect.width : self.YMost() - rect.height; + nsMargin inkOverflow( + vertical ? -self.x : -self.y, + vertical ? self.YMost() - rect.height : self.XMost() - rect.width, + vertical ? self.XMost() - rect.width : self.YMost() - rect.height, + vertical ? -self.y : -self.x); gfxSkipCharsIterator it = mFrame->EnsureTextRun(nsTextFrame::eInflated); gfxSkipCharsIterator start = it; @@ -838,8 +838,7 @@ SVGBBox TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext, metrics.mBoundingBox.UnionRect(metrics.mBoundingBox, fontBox); // Determine the rectangle that covers the rendered run's fill, - // taking into account the measured vertical overflow due to - // decorations. + // taking into account the measured overflow due to decorations. nscoord baseline = NSToCoordRoundWithClamp(metrics.mBoundingBox.y + metrics.mAscent); gfxFloat x, width; @@ -854,10 +853,10 @@ SVGBBox TextRenderedRun::GetRunUserSpaceRect(nsPresContext* aContext, x = metrics.mBoundingBox.x; width = metrics.mBoundingBox.width; } - nsRect fillInAppUnits( - NSToCoordRoundWithClamp(x), baseline - above, - NSToCoordRoundWithClamp(width), - NSToCoordRoundWithClamp(metrics.mBoundingBox.height) + above + below); + nsRect fillInAppUnits(NSToCoordRoundWithClamp(x), baseline, + NSToCoordRoundWithClamp(width), + NSToCoordRoundWithClamp(metrics.mBoundingBox.height)); + fillInAppUnits.Inflate(inkOverflow); if (textRun->IsVertical()) { // Swap line-relative textMetrics dimensions to physical coordinates. std::swap(fillInAppUnits.x, fillInAppUnits.y); @@ -2407,15 +2406,16 @@ class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks { mContext(aContext), mFrame(aFrame), mCanvasTM(aCanvasTM), - mImgParams(aImgParams), - mColor(0) {} + mImgParams(aImgParams) {} void NotifySelectionBackgroundNeedsFill(const Rect& aBackgroundRect, nscolor aColor, DrawTarget& aDrawTarget) override; - void PaintDecorationLine(Rect aPath, nscolor aColor) override; - void PaintSelectionDecorationLine(Rect aPath, nscolor aColor) override; - void NotifyBeforeText(nscolor aColor) override; + void PaintDecorationLine(Rect aPath, bool aPaintingShadows, + nscolor aColor) override; + void PaintSelectionDecorationLine(Rect aPath, bool aPaintingShadows, + nscolor aColor) override; + void NotifyBeforeText(bool aPaintingShadows, nscolor aColor) override; void NotifyGlyphPathEmitted() override; void NotifyAfterText() override; @@ -2454,6 +2454,12 @@ class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks { */ void StrokeGeometry(); + /* + * Takes a colour and modifies it to account for opacity properties. + */ + void ApplyOpacity(sRGBColor& aColor, const StyleSVGPaint& aPaint, + const StyleSVGOpacity& aOpacity) const; + SVGTextFrame* const mSVGTextFrame; gfxContext& mContext; nsTextFrame* const mFrame; @@ -2466,7 +2472,12 @@ class SVGTextDrawPathCallbacks final : public nsTextFrame::DrawPathCallbacks { * NS_40PERCENT_FOREGROUND_COLOR and NS_TRANSPARENT colors when we are * painting selections or IME decorations. */ - nscolor mColor; + nscolor mColor = NS_RGBA(0, 0, 0, 0); + + /** + * Whether we're painting text shadows. + */ + bool mPaintingShadows = false; }; void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill( @@ -2477,6 +2488,7 @@ void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill( } mColor = aColor; // currently needed by MakeFillPattern + mPaintingShadows = false; GeneralPattern fillPattern; MakeFillPattern(&fillPattern); @@ -2487,8 +2499,10 @@ void SVGTextDrawPathCallbacks::NotifySelectionBackgroundNeedsFill( } } -void SVGTextDrawPathCallbacks::NotifyBeforeText(nscolor aColor) { +void SVGTextDrawPathCallbacks::NotifyBeforeText(bool aPaintingShadows, + nscolor aColor) { mColor = aColor; + mPaintingShadows = aPaintingShadows; SetupContext(); mContext.NewPath(); } @@ -2500,8 +2514,11 @@ void SVGTextDrawPathCallbacks::NotifyGlyphPathEmitted() { void SVGTextDrawPathCallbacks::NotifyAfterText() { mContext.Restore(); } -void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath, nscolor aColor) { +void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath, + bool aPaintingShadows, + nscolor aColor) { mColor = aColor; + mPaintingShadows = aPaintingShadows; AntialiasMode aaMode = SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering); @@ -2514,14 +2531,15 @@ void SVGTextDrawPathCallbacks::PaintDecorationLine(Rect aPath, nscolor aColor) { mContext.Restore(); } -void SVGTextDrawPathCallbacks::PaintSelectionDecorationLine(Rect aPath, - nscolor aColor) { +void SVGTextDrawPathCallbacks::PaintSelectionDecorationLine( + Rect aPath, bool aPaintingShadows, nscolor aColor) { if (IsClipPathChild()) { // Don't paint selection decorations when in a clip path. return; } mColor = aColor; + mPaintingShadows = aPaintingShadows; mContext.Save(); mContext.NewPath(); @@ -2561,6 +2579,17 @@ void SVGTextDrawPathCallbacks::HandleTextGeometry() { } } +void SVGTextDrawPathCallbacks::ApplyOpacity( + sRGBColor& aColor, const StyleSVGPaint& aPaint, + const StyleSVGOpacity& aOpacity) const { + if (aPaint.kind.tag == StyleSVGPaintKind::Tag::Color) { + aColor.a *= + sRGBColor::FromABGR(aPaint.kind.AsColor().CalcColor(*mFrame->Style())) + .a; + } + aColor.a *= SVGUtils::GetOpacity(aOpacity, /*aContextPaint*/ nullptr); +} + void SVGTextDrawPathCallbacks::MakeFillPattern(GeneralPattern* aOutPattern) { if (mColor == NS_SAME_AS_FOREGROUND_COLOR || mColor == NS_40PERCENT_FOREGROUND_COLOR) { @@ -2572,7 +2601,12 @@ void SVGTextDrawPathCallbacks::MakeFillPattern(GeneralPattern* aOutPattern) { return; } - aOutPattern->InitColorPattern(ToDeviceColor(mColor)); + sRGBColor color(sRGBColor::FromABGR(mColor)); + if (mPaintingShadows) { + ApplyOpacity(color, mFrame->StyleSVG()->mFill, + mFrame->StyleSVG()->mFillOpacity); + } + aOutPattern->InitColorPattern(ToDeviceColor(color)); } void SVGTextDrawPathCallbacks::FillAndStrokeGeometry() { @@ -2607,6 +2641,9 @@ void SVGTextDrawPathCallbacks::FillAndStrokeGeometry() { } void SVGTextDrawPathCallbacks::FillGeometry() { + if (mFrame->StyleSVG()->mFill.kind.IsNone()) { + return; + } GeneralPattern fillPattern; MakeFillPattern(&fillPattern); if (fillPattern.GetPattern()) { @@ -2622,39 +2659,44 @@ void SVGTextDrawPathCallbacks::FillGeometry() { void SVGTextDrawPathCallbacks::StrokeGeometry() { // We don't paint the stroke when we are filling with a selection color. - if (mColor == NS_SAME_AS_FOREGROUND_COLOR || - mColor == NS_40PERCENT_FOREGROUND_COLOR) { - if (SVGUtils::HasStroke(mFrame, /*aContextPaint*/ nullptr)) { - GeneralPattern strokePattern; - SVGUtils::MakeStrokePatternFor(mFrame, &mContext, &strokePattern, - mImgParams, /*aContextPaint*/ nullptr); - if (strokePattern.GetPattern()) { - if (!mFrame->GetParent()->GetContent()->IsSVGElement()) { - // The cast that follows would be unsafe - MOZ_ASSERT(false, "Our nsTextFrame's parent's content should be SVG"); - return; - } - SVGElement* svgOwner = - static_cast<SVGElement*>(mFrame->GetParent()->GetContent()); - - // Apply any stroke-specific transform - gfxMatrix outerSVGToUser; - if (SVGUtils::GetNonScalingStrokeTransform(mFrame, &outerSVGToUser) && - outerSVGToUser.Invert()) { - mContext.Multiply(outerSVGToUser); - } + if (!(mColor == NS_SAME_AS_FOREGROUND_COLOR || + mColor == NS_40PERCENT_FOREGROUND_COLOR || mPaintingShadows)) { + return; + } - RefPtr<Path> path = mContext.GetPath(); - SVGContentUtils::AutoStrokeOptions strokeOptions; - SVGContentUtils::GetStrokeOptions(&strokeOptions, svgOwner, - mFrame->Style(), - /*aContextPaint*/ nullptr); - DrawOptions drawOptions; - drawOptions.mAntialiasMode = - SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering); - mContext.GetDrawTarget()->Stroke(path, strokePattern, strokeOptions); - } + if (!SVGUtils::HasStroke(mFrame, /*aContextPaint*/ nullptr)) { + return; + } + + GeneralPattern strokePattern; + if (mPaintingShadows) { + sRGBColor color(sRGBColor::FromABGR(mColor)); + ApplyOpacity(color, mFrame->StyleSVG()->mStroke, + mFrame->StyleSVG()->mStrokeOpacity); + strokePattern.InitColorPattern(ToDeviceColor(color)); + } else { + SVGUtils::MakeStrokePatternFor(mFrame, &mContext, &strokePattern, + mImgParams, /*aContextPaint*/ nullptr); + } + if (strokePattern.GetPattern()) { + SVGElement* svgOwner = + SVGElement::FromNode(mFrame->GetParent()->GetContent()); + + // Apply any stroke-specific transform + gfxMatrix outerSVGToUser; + if (SVGUtils::GetNonScalingStrokeTransform(mFrame, &outerSVGToUser) && + outerSVGToUser.Invert()) { + mContext.Multiply(outerSVGToUser); } + + RefPtr<Path> path = mContext.GetPath(); + SVGContentUtils::AutoStrokeOptions strokeOptions; + SVGContentUtils::GetStrokeOptions(&strokeOptions, svgOwner, mFrame->Style(), + /*aContextPaint*/ nullptr); + DrawOptions drawOptions; + drawOptions.mAntialiasMode = + SVGUtils::ToAntialiasMode(mFrame->StyleText()->mTextRendering); + mContext.GetDrawTarget()->Stroke(path, strokePattern, strokeOptions); } } @@ -4911,11 +4953,20 @@ bool SVGTextFrame::ShouldRenderAsPath(nsTextFrame* aFrame, const nsStyleSVG* style = aFrame->StyleSVG(); - // Fill is a non-solid paint, has a non-default fill-rule or has - // non-1 opacity. + // Fill is a non-solid paint or is not opaque. if (!(style->mFill.kind.IsNone() || - (style->mFill.kind.IsColor() && style->mFillOpacity.IsOpacity() && - style->mFillOpacity.AsOpacity() == 1))) { + (style->mFill.kind.IsColor() && + SVGUtils::GetOpacity(style->mFillOpacity, /*aContextPaint*/ nullptr) == + 1.0f))) { + return true; + } + + // If we're going to need to draw a non-opaque shadow. + // It's possible nsTextFrame will support non-opaque shadows in the future, + // in which case this test can be removed. + if (style->mFill.kind.IsColor() && aFrame->StyleText()->HasTextShadow() && + NS_GET_A(style->mFill.kind.AsColor().CalcColor(*aFrame->Style())) != + 0xFF) { return true; } diff --git a/layout/svg/SVGUtils.h b/layout/svg/SVGUtils.h index f28879b796..809f75e344 100644 --- a/layout/svg/SVGUtils.h +++ b/layout/svg/SVGUtils.h @@ -346,6 +346,12 @@ class SVGUtils final { // For a frame with a clip-path, if this flag is set then the result // will not be clipped to the bbox of the content inside the clip-path. eDoNotClipToBBoxOfContentInsideClipPath = 1 << 10, + // For some cases, e.g. when using transform-box: stroke-box, we may have + // the cyclical dependency if any of the elements in the subtree has + // non-scaling-stroke. In this case, we should break it and use + // transform-box:fill-box instead. + // https://github.com/w3c/csswg-drafts/issues/9640 + eAvoidCycleIfNonScalingStroke = 1 << 11, }; /** * This function in primarily for implementing the SVG DOM function getBBox() diff --git a/layout/svg/crashtests/1882921-1.html b/layout/svg/crashtests/1882921-1.html new file mode 100644 index 0000000000..eafc395b18 --- /dev/null +++ b/layout/svg/crashtests/1882921-1.html @@ -0,0 +1,7 @@ +<script> +document.addEventListener("DOMContentLoaded", () => { + document.execCommand("selectAll", false) +}) +</script> +<svg> +<text fill="url(#x) rgb(76,221,188)">Text</text> diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 8cdef6727c..0d38fed5ab 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -265,3 +265,4 @@ load 1831419.html load 1836831.html load 1840195-1.html load 1848851.html +load 1882921-1.html |