summaryrefslogtreecommitdiffstats
path: root/layout/svg
diff options
context:
space:
mode:
Diffstat (limited to 'layout/svg')
-rw-r--r--layout/svg/SVGGeometryFrame.cpp32
-rw-r--r--layout/svg/SVGGradientFrame.cpp3
-rw-r--r--layout/svg/SVGPatternFrame.cpp2
-rw-r--r--layout/svg/SVGTextFrame.cpp169
-rw-r--r--layout/svg/SVGUtils.h6
-rw-r--r--layout/svg/crashtests/1882921-1.html7
-rw-r--r--layout/svg/crashtests/crashtests.list1
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