summaryrefslogtreecommitdiffstats
path: root/dom/svg
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:42 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:42 +0000
commitda4c7e7ed675c3bf405668739c3012d140856109 (patch)
treecdd868dba063fecba609a1d819de271f0d51b23e /dom/svg
parentAdding upstream version 125.0.3. (diff)
downloadfirefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz
firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/svg')
-rw-r--r--dom/svg/SVGAElement.cpp10
-rw-r--r--dom/svg/SVGAElement.h4
-rw-r--r--dom/svg/SVGContentUtils.cpp85
-rw-r--r--dom/svg/SVGElement.cpp2
-rw-r--r--dom/svg/SVGElement.h4
-rw-r--r--dom/svg/SVGFEImageElement.cpp17
-rw-r--r--dom/svg/SVGFEImageElement.h1
-rw-r--r--dom/svg/SVGGeometryElement.cpp2
-rw-r--r--dom/svg/SVGGraphicsElement.cpp6
-rw-r--r--dom/svg/SVGImageElement.cpp17
-rw-r--r--dom/svg/SVGImageElement.h2
-rw-r--r--dom/svg/SVGPathData.cpp337
-rw-r--r--dom/svg/SVGPathData.h14
-rw-r--r--dom/svg/SVGPathElement.cpp2
-rw-r--r--dom/svg/SVGPathSegUtils.cpp144
-rw-r--r--dom/svg/SVGPathSegUtils.h12
-rw-r--r--dom/svg/SVGSVGElement.cpp39
-rw-r--r--dom/svg/SVGUseElement.cpp5
-rw-r--r--dom/svg/SVGUseElement.h1
-rw-r--r--dom/svg/crashtests/1858792.html15
-rw-r--r--dom/svg/crashtests/crashtests.list1
-rw-r--r--dom/svg/test/getCTM-helper.svg2
-rw-r--r--dom/svg/test/test_getCTM.html38
23 files changed, 437 insertions, 323 deletions
diff --git a/dom/svg/SVGAElement.cpp b/dom/svg/SVGAElement.cpp
index d27e8735ad..1533cceb34 100644
--- a/dom/svg/SVGAElement.cpp
+++ b/dom/svg/SVGAElement.cpp
@@ -257,4 +257,14 @@ SVGElement::StringAttributesInfo SVGAElement::GetStringInfo() {
ArrayLength(sStringInfo));
}
+void SVGAElement::DidAnimateAttribute(int32_t aNameSpaceID,
+ nsAtom* aAttribute) {
+ if ((aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink) &&
+ aAttribute == nsGkAtoms::href) {
+ Link::ResetLinkState(true, Link::ElementHasHref());
+ }
+ SVGAElementBase::DidAnimateAttribute(aNameSpaceID, aAttribute);
+}
+
} // namespace mozilla::dom
diff --git a/dom/svg/SVGAElement.h b/dom/svg/SVGAElement.h
index 166a146436..bbb552fce6 100644
--- a/dom/svg/SVGAElement.h
+++ b/dom/svg/SVGAElement.h
@@ -24,8 +24,7 @@ namespace dom {
using SVGAElementBase = SVGGraphicsElement;
-class SVGAElement final : public SVGAElementBase,
- public Link {
+class SVGAElement final : public SVGAElementBase, public Link {
protected:
using Element::GetText;
@@ -93,6 +92,7 @@ class SVGAElement final : public SVGAElementBase,
virtual ~SVGAElement() = default;
StringAttributesInfo GetStringInfo() override;
+ void DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) override;
enum { HREF, XLINK_HREF, TARGET };
SVGAnimatedString mStringAttributes[3];
diff --git a/dom/svg/SVGContentUtils.cpp b/dom/svg/SVGContentUtils.cpp
index 0913ae48c5..72534c12e8 100644
--- a/dom/svg/SVGContentUtils.cpp
+++ b/dom/svg/SVGContentUtils.cpp
@@ -493,6 +493,14 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
return ret;
};
+ auto postTranslateFrameOffset = [](nsIFrame* aFrame, nsIFrame* aAncestorFrame,
+ gfx::Matrix& aMatrix) {
+ auto point = aFrame->GetOffsetTo(aAncestorFrame);
+ aMatrix =
+ aMatrix.PostTranslate(nsPresContext::AppUnitsToFloatCSSPixels(point.x),
+ nsPresContext::AppUnitsToFloatCSSPixels(point.y));
+ };
+
gfxMatrix matrix = getLocalTransformHelper(aElement, aHaveRecursed);
SVGElement* element = aElement;
@@ -530,39 +538,62 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
matrix = getLocalTransformHelper(aElement, true);
}
- if (auto* f = element->GetPrimaryFrame()) {
- if (f->IsSVGOuterSVGFrame()) {
- nsMargin bp = f->GetUsedBorderAndPadding();
- matrix.PostTranslate(
- NSAppUnitsToFloatPixels(bp.left, AppUnitsPerCSSPixel()),
- NSAppUnitsToFloatPixels(bp.top, AppUnitsPerCSSPixel()));
- }
+ gfx::Matrix tm = gfx::ToMatrix(matrix);
+ nsIFrame* frame = element->GetPrimaryFrame();
+ if (!frame) {
+ return tm;
+ }
+ if (frame->IsSVGOuterSVGFrame()) {
+ nsMargin bp = frame->GetUsedBorderAndPadding();
+ int32_t appUnitsPerCSSPixel = AppUnitsPerCSSPixel();
+ tm.PostTranslate(NSAppUnitsToFloatPixels(bp.left, appUnitsPerCSSPixel),
+ NSAppUnitsToFloatPixels(bp.top, appUnitsPerCSSPixel));
}
if (!ancestor || !ancestor->IsElement()) {
- return gfx::ToMatrix(matrix);
+ return tm;
}
if (auto* ancestorSVG = SVGElement::FromNode(ancestor)) {
- return gfx::ToMatrix(matrix) * GetCTMInternal(ancestorSVG, true, true);
- }
-
- // XXX this does not take into account CSS transform, or that the non-SVG
- // content that we've hit may itself be inside an SVG foreignObject higher up
- Document* currentDoc = aElement->GetComposedDoc();
- float x = 0.0f, y = 0.0f;
- if (currentDoc && element->IsSVGElement(nsGkAtoms::svg)) {
- PresShell* presShell = currentDoc->GetPresShell();
- if (presShell) {
- nsIFrame* frame = element->GetPrimaryFrame();
- nsIFrame* ancestorFrame = presShell->GetRootFrame();
- if (frame && ancestorFrame) {
- nsPoint point = frame->GetOffsetTo(ancestorFrame);
- x = nsPresContext::AppUnitsToFloatCSSPixels(point.x);
- y = nsPresContext::AppUnitsToFloatCSSPixels(point.y);
- }
- }
+ return tm * GetCTMInternal(ancestorSVG, true, true);
}
- return ToMatrix(matrix).PostTranslate(x, y);
+ nsIFrame* parentFrame = frame->GetParent();
+ if (!parentFrame) {
+ return tm;
+ }
+ postTranslateFrameOffset(frame, parentFrame, tm);
+
+ nsIContent* nearestSVGAncestor = ancestor;
+ while (nearestSVGAncestor && !nearestSVGAncestor->IsSVGElement()) {
+ nearestSVGAncestor = nearestSVGAncestor->GetFlattenedTreeParent();
+ }
+
+ nsIFrame* ancestorFrame;
+ if (nearestSVGAncestor) {
+ ancestorFrame = nearestSVGAncestor->GetPrimaryFrame();
+ } else {
+ Document* currentDoc = aElement->GetComposedDoc();
+ PresShell* presShell = currentDoc ? currentDoc->GetPresShell() : nullptr;
+ ancestorFrame = presShell ? presShell->GetRootFrame() : nullptr;
+ }
+ if (!ancestorFrame) {
+ return tm;
+ }
+ auto transformToAncestor = nsLayoutUtils::GetTransformToAncestor(
+ RelativeTo{parentFrame, ViewportType::Layout},
+ RelativeTo{ancestorFrame, ViewportType::Layout}, nsIFrame::IN_CSS_UNITS);
+ gfx::Matrix result2d;
+ if (transformToAncestor.CanDraw2D(&result2d)) {
+ tm = tm * result2d;
+ } else {
+ // The transform from our outer SVG matrix to the root is a 3D
+ // transform. We can't really process that so give up and just
+ // return the overall translation from the outer SVG to the root.
+ postTranslateFrameOffset(parentFrame, ancestorFrame, tm);
+ }
+ return nearestSVGAncestor
+ ? tm * GetCTMInternal(static_cast<SVGElement*>(nearestSVGAncestor),
+ true, true)
+ : tm;
}
gfx::Matrix SVGContentUtils::GetCTM(SVGElement* aElement, bool aScreenCTM) {
diff --git a/dom/svg/SVGElement.cpp b/dom/svg/SVGElement.cpp
index 57c1cc19cb..213bc6be0e 100644
--- a/dom/svg/SVGElement.cpp
+++ b/dom/svg/SVGElement.cpp
@@ -2080,7 +2080,7 @@ void SVGElement::DidChangeStringList(bool aIsConditionalProcessingAttribute,
void SVGElement::DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) {
if (auto* frame = GetPrimaryFrame()) {
frame->AttributeChanged(aNameSpaceID, aAttribute,
- MutationEvent_Binding::SMIL);
+ MutationEvent_Binding::MODIFICATION);
SVGObserverUtils::InvalidateRenderingObservers(frame);
return;
}
diff --git a/dom/svg/SVGElement.h b/dom/svg/SVGElement.h
index 08f70ab482..d9ad8b79ca 100644
--- a/dom/svg/SVGElement.h
+++ b/dom/svg/SVGElement.h
@@ -538,9 +538,9 @@ class SVGElement : public SVGElementBase // nsIContent
static SVGEnumMapping sSVGUnitTypesMap[];
- private:
- void DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute);
+ virtual void DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute);
+ private:
void UnsetAttrInternal(int32_t aNameSpaceID, nsAtom* aName, bool aNotify);
SVGAnimatedClass mClassAttribute;
diff --git a/dom/svg/SVGFEImageElement.cpp b/dom/svg/SVGFEImageElement.cpp
index f9c5f96758..826ba82228 100644
--- a/dom/svg/SVGFEImageElement.cpp
+++ b/dom/svg/SVGFEImageElement.cpp
@@ -371,4 +371,21 @@ void SVGFEImageElement::Notify(imgIRequest* aRequest, int32_t aType,
}
}
+void SVGFEImageElement::DidAnimateAttribute(int32_t aNameSpaceID,
+ nsAtom* aAttribute) {
+ if ((aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink) &&
+ aAttribute == nsGkAtoms::href) {
+ bool hrefIsSet =
+ mStringAttributes[SVGFEImageElement::HREF].IsExplicitlySet() ||
+ mStringAttributes[SVGFEImageElement::XLINK_HREF].IsExplicitlySet();
+ if (hrefIsSet) {
+ LoadSVGImage(true, true);
+ } else {
+ CancelImageRequests(true);
+ }
+ }
+ SVGFEImageElementBase::DidAnimateAttribute(aNameSpaceID, aAttribute);
+}
+
} // namespace mozilla::dom
diff --git a/dom/svg/SVGFEImageElement.h b/dom/svg/SVGFEImageElement.h
index 41b28b83a9..4df686d2d5 100644
--- a/dom/svg/SVGFEImageElement.h
+++ b/dom/svg/SVGFEImageElement.h
@@ -94,6 +94,7 @@ class SVGFEImageElement final : public SVGFEImageElementBase,
}
private:
+ void DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) override;
nsresult LoadSVGImage(bool aForce, bool aNotify);
bool ShouldLoadImage() const;
diff --git a/dom/svg/SVGGeometryElement.cpp b/dom/svg/SVGGeometryElement.cpp
index d012a911b8..de5756ed7d 100644
--- a/dom/svg/SVGGeometryElement.cpp
+++ b/dom/svg/SVGGeometryElement.cpp
@@ -187,7 +187,7 @@ bool SVGGeometryElement::IsPointInFill(const DOMPointInit& aPoint) {
bool SVGGeometryElement::IsPointInStroke(const DOMPointInit& aPoint) {
// stroke-* attributes and the d attribute are presentation attributes, so we
// flush the layout before building the path.
- if (nsCOMPtr<Document> doc = GetComposedDoc()) {
+ if (auto* doc = GetComposedDoc()) {
doc->FlushPendingNotifications(FlushType::Layout);
}
diff --git a/dom/svg/SVGGraphicsElement.cpp b/dom/svg/SVGGraphicsElement.cpp
index 139b97a76a..29b6b76728 100644
--- a/dom/svg/SVGGraphicsElement.cpp
+++ b/dom/svg/SVGGraphicsElement.cpp
@@ -125,8 +125,7 @@ already_AddRefed<SVGRect> SVGGraphicsElement::GetBBox(
}
already_AddRefed<SVGMatrix> SVGGraphicsElement::GetCTM() {
- Document* currentDoc = GetComposedDoc();
- if (currentDoc) {
+ if (auto* currentDoc = GetComposedDoc()) {
// Flush all pending notifications so that our frames are up to date
currentDoc->FlushPendingNotifications(FlushType::Layout);
}
@@ -137,8 +136,7 @@ already_AddRefed<SVGMatrix> SVGGraphicsElement::GetCTM() {
}
already_AddRefed<SVGMatrix> SVGGraphicsElement::GetScreenCTM() {
- Document* currentDoc = GetComposedDoc();
- if (currentDoc) {
+ if (auto* currentDoc = GetComposedDoc()) {
// Flush all pending notifications so that our frames are up to date
currentDoc->FlushPendingNotifications(FlushType::Layout);
}
diff --git a/dom/svg/SVGImageElement.cpp b/dom/svg/SVGImageElement.cpp
index 264c03da09..4a1332fb00 100644
--- a/dom/svg/SVGImageElement.cpp
+++ b/dom/svg/SVGImageElement.cpp
@@ -310,4 +310,21 @@ SVGElement::StringAttributesInfo SVGImageElement::GetStringInfo() {
ArrayLength(sStringInfo));
}
+void SVGImageElement::DidAnimateAttribute(int32_t aNameSpaceID,
+ nsAtom* aAttribute) {
+ if ((aNameSpaceID == kNameSpaceID_None ||
+ aNameSpaceID == kNameSpaceID_XLink) &&
+ aAttribute == nsGkAtoms::href) {
+ bool hrefIsSet =
+ mStringAttributes[SVGImageElement::HREF].IsExplicitlySet() ||
+ mStringAttributes[SVGImageElement::XLINK_HREF].IsExplicitlySet();
+ if (hrefIsSet) {
+ LoadSVGImage(true, true);
+ } else {
+ CancelImageRequests(true);
+ }
+ }
+ SVGImageElementBase::DidAnimateAttribute(aNameSpaceID, aAttribute);
+}
+
} // namespace mozilla::dom
diff --git a/dom/svg/SVGImageElement.h b/dom/svg/SVGImageElement.h
index 06770e2576..591179b3a9 100644
--- a/dom/svg/SVGImageElement.h
+++ b/dom/svg/SVGImageElement.h
@@ -102,6 +102,8 @@ class SVGImageElement final : public SVGImageElementBase,
gfx::Rect GeometryBounds(const gfx::Matrix& aToBoundsSpace);
protected:
+ void DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) override;
+
nsresult LoadSVGImage(bool aForce, bool aNotify);
bool ShouldLoadImage() const;
diff --git a/dom/svg/SVGPathData.cpp b/dom/svg/SVGPathData.cpp
index a1f5b2ac98..2b2eaca46b 100644
--- a/dom/svg/SVGPathData.cpp
+++ b/dom/svg/SVGPathData.cpp
@@ -31,36 +31,14 @@ static inline bool IsMoveto(uint16_t aSegType) {
return aSegType == PATHSEG_MOVETO_ABS || aSegType == PATHSEG_MOVETO_REL;
}
-static inline bool IsMoveto(StylePathCommand::Tag aSegType) {
- return aSegType == StylePathCommand::Tag::MoveTo;
-}
-
static inline bool IsValidType(uint16_t aSegType) {
return SVGPathSegUtils::IsValidType(aSegType);
}
-static inline bool IsValidType(StylePathCommand::Tag aSegType) {
- return aSegType != StylePathCommand::Tag::Unknown;
-}
-
static inline bool IsClosePath(uint16_t aSegType) {
return aSegType == PATHSEG_CLOSEPATH;
}
-static inline bool IsClosePath(StylePathCommand::Tag aSegType) {
- return aSegType == StylePathCommand::Tag::ClosePath;
-}
-
-static inline bool IsCubicType(StylePathCommand::Tag aType) {
- return aType == StylePathCommand::Tag::CurveTo ||
- aType == StylePathCommand::Tag::SmoothCurveTo;
-}
-
-static inline bool IsQuadraticType(StylePathCommand::Tag aType) {
- return aType == StylePathCommand::Tag::QuadBezierCurveTo ||
- aType == StylePathCommand::Tag::SmoothQuadBezierCurveTo;
-}
-
nsresult SVGPathData::CopyFrom(const SVGPathData& rhs) {
if (!mData.Assign(rhs.mData, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
@@ -200,13 +178,13 @@ bool SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(
}
// We skip all moveto commands except for the initial moveto.
- if (!cmd.IsMoveTo() || !firstMoveToIsChecked) {
+ if (!cmd.IsMove() || !firstMoveToIsChecked) {
if (!aOutput->AppendElement(state.length, fallible)) {
return false;
}
}
- if (cmd.IsMoveTo() && !firstMoveToIsChecked) {
+ if (cmd.IsMove() && !firstMoveToIsChecked) {
firstMoveToIsChecked = true;
}
}
@@ -550,6 +528,8 @@ already_AddRefed<Path> SVGPathData::BuildPath(PathBuilder* aBuilder,
return aBuilder->Finish();
}
+#undef MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT
+
already_AddRefed<Path> SVGPathData::BuildPathForMeasuring() const {
// Since the path that we return will not be used for painting it doesn't
// matter what we pass to CreatePathBuilder as aFillRule. Hawever, we do want
@@ -576,15 +556,33 @@ already_AddRefed<Path> SVGPathData::BuildPathForMeasuring(
return BuildPath(aPath, builder, StyleStrokeLinecap::Butt, 0);
}
-// We could simplify this function because this is only used by CSS motion path
-// and clip-path, which don't render the SVG Path. i.e. The returned path is
-// used as a reference.
-/* static */
-already_AddRefed<Path> SVGPathData::BuildPath(
- Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
- StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth, const Point& aOffset,
+static inline StyleCSSFloat GetRotate(const StyleCSSFloat& aAngle) {
+ return aAngle;
+}
+
+static inline StyleCSSFloat GetRotate(const StyleAngle& aAngle) {
+ return aAngle.ToDegrees();
+}
+
+static inline StyleCSSFloat Resolve(const StyleCSSFloat& aValue,
+ CSSCoord aBasis) {
+ return aValue;
+}
+
+static inline StyleCSSFloat Resolve(const LengthPercentage& aValue,
+ CSSCoord aBasis) {
+ return aValue.ResolveToCSSPixels(aBasis);
+}
+
+template <typename Angle, typename LP>
+static already_AddRefed<Path> BuildPathInternal(
+ Span<const StyleGenericShapeCommand<Angle, LP>> aPath,
+ PathBuilder* aBuilder, StyleStrokeLinecap aStrokeLineCap,
+ Float aStrokeWidth, const CSSSize& aPercentageBasis, const Point& aOffset,
float aZoomFactor) {
- if (aPath.IsEmpty() || !aPath[0].IsMoveTo()) {
+ using Command = StyleGenericShapeCommand<Angle, LP>;
+
+ if (aPath.IsEmpty() || !aPath[0].IsMove()) {
return nullptr; // paths without an initial moveto are invalid
}
@@ -592,14 +590,24 @@ already_AddRefed<Path> SVGPathData::BuildPath(
bool subpathHasLength = false; // visual length
bool subpathContainsNonMoveTo = false;
- StylePathCommand::Tag segType = StylePathCommand::Tag::Unknown;
- StylePathCommand::Tag prevSegType = StylePathCommand::Tag::Unknown;
+ const Command* seg = nullptr;
+ const Command* prevSeg = nullptr;
Point pathStart(0.0, 0.0); // start point of [sub]path
Point segStart(0.0, 0.0);
Point segEnd;
Point cp1, cp2; // previous bezier's control points
Point tcp1, tcp2; // temporaries
+ auto maybeApproximateZeroLengthSubpathSquareCaps =
+ [&](const Command* aPrevSeg, const Command* aSeg) {
+ if (!subpathHasLength && hasLineCaps && aStrokeWidth > 0 &&
+ subpathContainsNonMoveTo && aPrevSeg && aSeg &&
+ (!aPrevSeg->IsMove() || aSeg->IsClose())) {
+ ApproximateZeroLengthSubpathSquareCaps(aBuilder, segStart,
+ aStrokeWidth);
+ }
+ };
+
auto scale = [aOffset, aZoomFactor](const Point& p) {
return Point(p.x * aZoomFactor, p.y * aZoomFactor) + aOffset;
};
@@ -608,41 +616,39 @@ already_AddRefed<Path> SVGPathData::BuildPath(
// then cp2 is its second control point. If the previous segment was a
// quadratic curve, then cp1 is its (only) control point.
- for (const StylePathCommand& cmd : aPath) {
- segType = cmd.tag;
- switch (segType) {
- case StylePathCommand::Tag::ClosePath:
+ for (const auto& cmd : aPath) {
+ seg = &cmd;
+ switch (cmd.tag) {
+ case Command::Tag::Close:
// set this early to allow drawing of square caps for "M{x},{y} Z":
subpathContainsNonMoveTo = true;
- MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
+ maybeApproximateZeroLengthSubpathSquareCaps(prevSeg, seg);
segEnd = pathStart;
aBuilder->Close();
break;
- case StylePathCommand::Tag::MoveTo: {
- MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
- const Point& p = cmd.move_to.point.ConvertsToGfxPoint();
- pathStart = segEnd =
- cmd.move_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
+ case Command::Tag::Move: {
+ maybeApproximateZeroLengthSubpathSquareCaps(prevSeg, seg);
+ const Point& p = cmd.move.point.ToGfxPoint(aPercentageBasis);
+ pathStart = segEnd = cmd.move.by_to == StyleByTo::To ? p : segStart + p;
aBuilder->MoveTo(scale(segEnd));
subpathHasLength = false;
break;
}
- case StylePathCommand::Tag::LineTo: {
- const Point& p = cmd.line_to.point.ConvertsToGfxPoint();
- segEnd =
- cmd.line_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
+ case Command::Tag::Line: {
+ const Point& p = cmd.line.point.ToGfxPoint(aPercentageBasis);
+ segEnd = cmd.line.by_to == StyleByTo::To ? p : segStart + p;
if (segEnd != segStart) {
subpathHasLength = true;
aBuilder->LineTo(scale(segEnd));
}
break;
}
- case StylePathCommand::Tag::CurveTo:
- cp1 = cmd.curve_to.control1.ConvertsToGfxPoint();
- cp2 = cmd.curve_to.control2.ConvertsToGfxPoint();
- segEnd = cmd.curve_to.point.ConvertsToGfxPoint();
+ case Command::Tag::CubicCurve:
+ cp1 = cmd.cubic_curve.control1.ToGfxPoint(aPercentageBasis);
+ cp2 = cmd.cubic_curve.control2.ToGfxPoint(aPercentageBasis);
+ segEnd = cmd.cubic_curve.point.ToGfxPoint(aPercentageBasis);
- if (cmd.curve_to.absolute == StyleIsAbsolute::No) {
+ if (cmd.cubic_curve.by_to == StyleByTo::By) {
cp1 += segStart;
cp2 += segStart;
segEnd += segStart;
@@ -654,11 +660,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
- case StylePathCommand::Tag::QuadBezierCurveTo:
- cp1 = cmd.quad_bezier_curve_to.control1.ConvertsToGfxPoint();
- segEnd = cmd.quad_bezier_curve_to.point.ConvertsToGfxPoint();
+ case Command::Tag::QuadCurve:
+ cp1 = cmd.quad_curve.control1.ToGfxPoint(aPercentageBasis);
+ segEnd = cmd.quad_curve.point.ToGfxPoint(aPercentageBasis);
- if (cmd.quad_bezier_curve_to.absolute == StyleIsAbsolute::No) {
+ if (cmd.quad_curve.by_to == StyleByTo::By) {
cp1 += segStart;
segEnd += segStart; // set before setting tcp2!
}
@@ -673,11 +679,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
- case StylePathCommand::Tag::EllipticalArc: {
- const auto& arc = cmd.elliptical_arc;
- Point radii(arc.rx, arc.ry);
- segEnd = arc.point.ConvertsToGfxPoint();
- if (arc.absolute == StyleIsAbsolute::No) {
+ case Command::Tag::Arc: {
+ const auto& arc = cmd.arc;
+ const Point& radii = arc.radii.ToGfxPoint(aPercentageBasis);
+ segEnd = arc.point.ToGfxPoint(aPercentageBasis);
+ if (arc.by_to == StyleByTo::By) {
segEnd += segStart;
}
if (segEnd != segStart) {
@@ -685,8 +691,11 @@ already_AddRefed<Path> SVGPathData::BuildPath(
if (radii.x == 0.0f || radii.y == 0.0f) {
aBuilder->LineTo(scale(segEnd));
} else {
- SVGArcConverter converter(segStart, segEnd, radii, arc.angle,
- arc.large_arc_flag._0, arc.sweep_flag._0);
+ const bool arc_is_large = arc.arc_size == StyleArcSize::Large;
+ const bool arc_is_cw = arc.arc_sweep == StyleArcSweep::Cw;
+ SVGArcConverter converter(segStart, segEnd, radii,
+ GetRotate(arc.rotate), arc_is_large,
+ arc_is_cw);
while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) {
aBuilder->BezierTo(scale(cp1), scale(cp2), scale(segEnd));
}
@@ -694,11 +703,12 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
}
- case StylePathCommand::Tag::HorizontalLineTo:
- if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::Yes) {
- segEnd = Point(cmd.horizontal_line_to.x, segStart.y);
+ case Command::Tag::HLine: {
+ const float x = Resolve(cmd.h_line.x, aPercentageBasis.width);
+ if (cmd.h_line.by_to == StyleByTo::To) {
+ segEnd = Point(x, segStart.y);
} else {
- segEnd = segStart + Point(cmd.horizontal_line_to.x, 0.0f);
+ segEnd = segStart + Point(x, 0.0f);
}
if (segEnd != segStart) {
@@ -706,12 +716,13 @@ already_AddRefed<Path> SVGPathData::BuildPath(
aBuilder->LineTo(scale(segEnd));
}
break;
-
- case StylePathCommand::Tag::VerticalLineTo:
- if (cmd.vertical_line_to.absolute == StyleIsAbsolute::Yes) {
- segEnd = Point(segStart.x, cmd.vertical_line_to.y);
+ }
+ case Command::Tag::VLine: {
+ const float y = Resolve(cmd.v_line.y, aPercentageBasis.height);
+ if (cmd.v_line.by_to == StyleByTo::To) {
+ segEnd = Point(segStart.x, y);
} else {
- segEnd = segStart + Point(0.0f, cmd.vertical_line_to.y);
+ segEnd = segStart + Point(0.0f, y);
}
if (segEnd != segStart) {
@@ -719,13 +730,13 @@ already_AddRefed<Path> SVGPathData::BuildPath(
aBuilder->LineTo(scale(segEnd));
}
break;
+ }
+ case Command::Tag::SmoothCubic:
+ cp1 = prevSeg && prevSeg->IsCubicType() ? segStart * 2 - cp2 : segStart;
+ cp2 = cmd.smooth_cubic.control2.ToGfxPoint(aPercentageBasis);
+ segEnd = cmd.smooth_cubic.point.ToGfxPoint(aPercentageBasis);
- case StylePathCommand::Tag::SmoothCurveTo:
- cp1 = IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
- cp2 = cmd.smooth_curve_to.control2.ConvertsToGfxPoint();
- segEnd = cmd.smooth_curve_to.point.ConvertsToGfxPoint();
-
- if (cmd.smooth_curve_to.absolute == StyleIsAbsolute::No) {
+ if (cmd.smooth_cubic.by_to == StyleByTo::By) {
cp2 += segStart;
segEnd += segStart;
}
@@ -736,18 +747,15 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
- case StylePathCommand::Tag::SmoothQuadBezierCurveTo: {
- cp1 = IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
+ case Command::Tag::SmoothQuad: {
+ cp1 = prevSeg && prevSeg->IsQuadraticType() ? segStart * 2 - cp1
+ : segStart;
// Convert quadratic curve to cubic curve:
tcp1 = segStart + (cp1 - segStart) * 2 / 3;
- const Point& p =
- cmd.smooth_quad_bezier_curve_to.point.ConvertsToGfxPoint();
+ const Point& p = cmd.smooth_quad.point.ToGfxPoint(aPercentageBasis);
// set before setting tcp2!
- segEnd =
- cmd.smooth_quad_bezier_curve_to.absolute == StyleIsAbsolute::Yes
- ? p
- : segStart + p;
+ segEnd = cmd.smooth_quad.by_to == StyleByTo::To ? p : segStart + p;
tcp2 = cp1 + (segEnd - cp1) / 3;
if (segEnd != segStart || segEnd != cp1) {
@@ -756,24 +764,38 @@ already_AddRefed<Path> SVGPathData::BuildPath(
}
break;
}
- case StylePathCommand::Tag::Unknown:
- MOZ_ASSERT_UNREACHABLE("Unacceptable path segment type");
- return nullptr;
}
- subpathContainsNonMoveTo = !IsMoveto(segType);
- prevSegType = segType;
+ subpathContainsNonMoveTo = !cmd.IsMove();
+ prevSeg = seg;
segStart = segEnd;
}
- MOZ_ASSERT(prevSegType == segType,
- "prevSegType should be left at the final segType");
+ MOZ_ASSERT(prevSeg == seg, "prevSegType should be left at the final segType");
- MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT;
+ maybeApproximateZeroLengthSubpathSquareCaps(prevSeg, seg);
return aBuilder->Finish();
}
+/* static */
+already_AddRefed<Path> SVGPathData::BuildPath(
+ Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
+ StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
+ const CSSSize& aBasis, const gfx::Point& aOffset, float aZoomFactor) {
+ return BuildPathInternal(aPath, aBuilder, aStrokeLineCap, aStrokeWidth,
+ aBasis, aOffset, aZoomFactor);
+}
+
+/* static */
+already_AddRefed<Path> SVGPathData::BuildPath(
+ Span<const StyleShapeCommand> aShape, PathBuilder* aBuilder,
+ StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
+ const CSSSize& aBasis, const gfx::Point& aOffset, float aZoomFactor) {
+ return BuildPathInternal(aShape, aBuilder, aStrokeLineCap, aStrokeWidth,
+ aBasis, aOffset, aZoomFactor);
+}
+
static double AngleOfVector(const Point& aVector) {
// C99 says about atan2 "A domain error may occur if both arguments are
// zero" and "On a domain error, the function returns an implementation-
@@ -1135,48 +1157,44 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
uint32_t pathStartIndex = 0;
// info on previous segment:
- StylePathCommand::Tag prevSegType = StylePathCommand::Tag::Unknown;
+ const StylePathCommand* prevSeg = nullptr;
Point prevSegEnd(0.0, 0.0);
float prevSegEndAngle = 0.0f;
Point prevCP; // if prev seg was a bezier, this was its last control point
- StylePathCommand::Tag segType = StylePathCommand::Tag::Unknown;
for (const StylePathCommand& cmd : aPath) {
- segType = cmd.tag;
Point& segStart = prevSegEnd;
Point segEnd;
float segStartAngle, segEndAngle;
- switch (segType) // to find segStartAngle, segEnd and segEndAngle
+ switch (cmd.tag) // to find segStartAngle, segEnd and segEndAngle
{
- case StylePathCommand::Tag::ClosePath:
+ case StylePathCommand::Tag::Close:
segEnd = pathStart;
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
- case StylePathCommand::Tag::MoveTo: {
- const Point& p = cmd.move_to.point.ConvertsToGfxPoint();
- pathStart = segEnd =
- cmd.move_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
+ case StylePathCommand::Tag::Move: {
+ const Point& p = cmd.move.point.ToGfxPoint();
+ pathStart = segEnd = cmd.move.by_to == StyleByTo::To ? p : segStart + p;
pathStartIndex = aMarks->Length();
// If authors are going to specify multiple consecutive moveto commands
// with markers, me might as well make the angle do something useful:
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
- case StylePathCommand::Tag::LineTo: {
- const Point& p = cmd.line_to.point.ConvertsToGfxPoint();
- segEnd =
- cmd.line_to.absolute == StyleIsAbsolute::Yes ? p : segStart + p;
+ case StylePathCommand::Tag::Line: {
+ const Point& p = cmd.line.point.ToGfxPoint();
+ segEnd = cmd.line.by_to == StyleByTo::To ? p : segStart + p;
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
- case StylePathCommand::Tag::CurveTo: {
- Point cp1 = cmd.curve_to.control1.ConvertsToGfxPoint();
- Point cp2 = cmd.curve_to.control2.ConvertsToGfxPoint();
- segEnd = cmd.curve_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::CubicCurve: {
+ Point cp1 = cmd.cubic_curve.control1.ToGfxPoint();
+ Point cp2 = cmd.cubic_curve.control2.ToGfxPoint();
+ segEnd = cmd.cubic_curve.point.ToGfxPoint();
- if (cmd.curve_to.absolute == StyleIsAbsolute::No) {
+ if (cmd.cubic_curve.by_to == StyleByTo::By) {
cp1 += segStart;
cp2 += segStart;
segEnd += segStart;
@@ -1189,11 +1207,11 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2);
break;
}
- case StylePathCommand::Tag::QuadBezierCurveTo: {
- Point cp1 = cmd.quad_bezier_curve_to.control1.ConvertsToGfxPoint();
- segEnd = cmd.quad_bezier_curve_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::QuadCurve: {
+ Point cp1 = cmd.quad_curve.control1.ToGfxPoint();
+ segEnd = cmd.quad_curve.point.ToGfxPoint();
- if (cmd.quad_bezier_curve_to.absolute == StyleIsAbsolute::No) {
+ if (cmd.quad_curve.by_to == StyleByTo::By) {
cp1 += segStart;
segEnd += segStart; // set before setting tcp2!
}
@@ -1203,16 +1221,15 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1);
break;
}
- case StylePathCommand::Tag::EllipticalArc: {
- const auto& arc = cmd.elliptical_arc;
- float rx = arc.rx;
- float ry = arc.ry;
- float angle = arc.angle;
- bool largeArcFlag = arc.large_arc_flag._0;
- bool sweepFlag = arc.sweep_flag._0;
- Point radii(arc.rx, arc.ry);
- segEnd = arc.point.ConvertsToGfxPoint();
- if (arc.absolute == StyleIsAbsolute::No) {
+ case StylePathCommand::Tag::Arc: {
+ const auto& arc = cmd.arc;
+ float rx = arc.radii.x;
+ float ry = arc.radii.y;
+ float angle = arc.rotate;
+ bool largeArcFlag = arc.arc_size == StyleArcSize::Large;
+ bool sweepFlag = arc.arc_sweep == StyleArcSweep::Cw;
+ segEnd = arc.point.ToGfxPoint();
+ if (arc.by_to == StyleByTo::By) {
segEnd += segStart;
}
@@ -1246,30 +1263,32 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
largeArcFlag, sweepFlag, rx, ry);
break;
}
- case StylePathCommand::Tag::HorizontalLineTo: {
- if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::Yes) {
- segEnd = Point(cmd.horizontal_line_to.x, segStart.y);
+ case StylePathCommand::Tag::HLine: {
+ if (cmd.h_line.by_to == StyleByTo::To) {
+ segEnd = Point(cmd.h_line.x, segStart.y);
} else {
- segEnd = segStart + Point(cmd.horizontal_line_to.x, 0.0f);
+ segEnd = segStart + Point(cmd.h_line.x, 0.0f);
}
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
- case StylePathCommand::Tag::VerticalLineTo: {
- if (cmd.vertical_line_to.absolute == StyleIsAbsolute::Yes) {
- segEnd = Point(segStart.x, cmd.vertical_line_to.y);
+ case StylePathCommand::Tag::VLine: {
+ if (cmd.v_line.by_to == StyleByTo::To) {
+ segEnd = Point(segStart.x, cmd.v_line.y);
} else {
- segEnd = segStart + Point(0.0f, cmd.vertical_line_to.y);
+ segEnd = segStart + Point(0.0f, cmd.v_line.y);
}
segStartAngle = segEndAngle = AngleOfVector(segEnd, segStart);
break;
}
- case StylePathCommand::Tag::SmoothCurveTo: {
- Point cp1 = IsCubicType(prevSegType) ? segStart * 2 - prevCP : segStart;
- Point cp2 = cmd.smooth_curve_to.control2.ConvertsToGfxPoint();
- segEnd = cmd.smooth_curve_to.point.ConvertsToGfxPoint();
-
- if (cmd.smooth_curve_to.absolute == StyleIsAbsolute::No) {
+ case StylePathCommand::Tag::SmoothCubic: {
+ const Point& cp1 = prevSeg && prevSeg->IsCubicType()
+ ? segStart * 2 - prevCP
+ : segStart;
+ Point cp2 = cmd.smooth_cubic.control2.ToGfxPoint();
+ segEnd = cmd.smooth_cubic.point.ToGfxPoint();
+
+ if (cmd.smooth_cubic.by_to == StyleByTo::By) {
cp2 += segStart;
segEnd += segStart;
}
@@ -1281,40 +1300,33 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
segEnd, cp2 == segEnd ? (cp1 == cp2 ? segStart : cp1) : cp2);
break;
}
- case StylePathCommand::Tag::SmoothQuadBezierCurveTo: {
- Point cp1 =
- IsQuadraticType(prevSegType) ? segStart * 2 - prevCP : segStart;
- segEnd =
- cmd.smooth_quad_bezier_curve_to.absolute == StyleIsAbsolute::Yes
- ? cmd.smooth_quad_bezier_curve_to.point.ConvertsToGfxPoint()
- : segStart + cmd.smooth_quad_bezier_curve_to.point
- .ConvertsToGfxPoint();
+ case StylePathCommand::Tag::SmoothQuad: {
+ const Point& cp1 = prevSeg && prevSeg->IsQuadraticType()
+ ? segStart * 2 - prevCP
+ : segStart;
+ segEnd = cmd.smooth_quad.by_to == StyleByTo::To
+ ? cmd.smooth_quad.point.ToGfxPoint()
+ : segStart + cmd.smooth_quad.point.ToGfxPoint();
prevCP = cp1;
segStartAngle = AngleOfVector(cp1 == segStart ? segEnd : cp1, segStart);
segEndAngle = AngleOfVector(segEnd, cp1 == segEnd ? segStart : cp1);
break;
}
- case StylePathCommand::Tag::Unknown:
- // Leave any existing marks in aMarks so we have a visual indication of
- // when things went wrong.
- MOZ_ASSERT_UNREACHABLE("Unknown segment type - path corruption?");
- return;
}
// Set the angle of the mark at the start of this segment:
if (aMarks->Length()) {
SVGMark& mark = aMarks->LastElement();
- if (!IsMoveto(segType) && IsMoveto(prevSegType)) {
+ if (!cmd.IsMove() && prevSeg && prevSeg->IsMove()) {
// start of new subpath
pathStartAngle = mark.angle = segStartAngle;
- } else if (IsMoveto(segType) && !IsMoveto(prevSegType)) {
+ } else if (cmd.IsMove() && !(prevSeg && prevSeg->IsMove())) {
// end of a subpath
- if (prevSegType != StylePathCommand::Tag::ClosePath) {
+ if (!(prevSeg && prevSeg->IsClose())) {
mark.angle = prevSegEndAngle;
}
- } else if (!(segType == StylePathCommand::Tag::ClosePath &&
- prevSegType == StylePathCommand::Tag::ClosePath)) {
+ } else if (!(cmd.IsClose() && prevSeg && prevSeg->IsClose())) {
mark.angle =
SVGContentUtils::AngleBisect(prevSegEndAngle, segStartAngle);
}
@@ -1327,19 +1339,18 @@ void SVGPathData::GetMarkerPositioningData(Span<const StylePathCommand> aPath,
static_cast<float>(segEnd.y), 0.0f,
SVGMark::eMid));
- if (segType == StylePathCommand::Tag::ClosePath &&
- prevSegType != StylePathCommand::Tag::ClosePath) {
+ if (cmd.IsClose() && !(prevSeg && prevSeg->IsClose())) {
aMarks->LastElement().angle = aMarks->ElementAt(pathStartIndex).angle =
SVGContentUtils::AngleBisect(segEndAngle, pathStartAngle);
}
- prevSegType = segType;
+ prevSeg = &cmd;
prevSegEnd = segEnd;
prevSegEndAngle = segEndAngle;
}
if (aMarks->Length()) {
- if (prevSegType != StylePathCommand::Tag::ClosePath) {
+ if (!(prevSeg && prevSeg->IsClose())) {
aMarks->LastElement().angle = prevSegEndAngle;
}
aMarks->LastElement().type = SVGMark::eEnd;
diff --git a/dom/svg/SVGPathData.h b/dom/svg/SVGPathData.h
index 4aa60de3cb..e06e669706 100644
--- a/dom/svg/SVGPathData.h
+++ b/dom/svg/SVGPathData.h
@@ -17,13 +17,13 @@
#include "mozilla/gfx/Types.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/RefPtr.h"
+#include "mozilla/ServoStyleConsts.h"
#include "nsTArray.h"
#include <string.h>
namespace mozilla {
-struct StylePathCommand;
struct SVGMark;
enum class StyleStrokeLinecap : uint8_t;
@@ -190,14 +190,22 @@ class SVGPathData {
Span<const StylePathCommand> aPath);
/**
- * This function tries to build the path from an array of StylePathCommand,
+ * This function tries to build the path from an array of GenericShapeCommand,
* which is generated by cbindgen from Rust (see ServoStyleConsts.h).
* Basically, this is a variant of the above BuildPath() functions.
+ * Note: |StylePathCommand| doesn't accept percentage values, so its |aBasis|
+ * is empty by default.
*/
static already_AddRefed<Path> BuildPath(
Span<const StylePathCommand> aPath, PathBuilder* aBuilder,
StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
- const gfx::Point& aOffset = gfx::Point(), float aZoomFactor = 1.0);
+ const CSSSize& aBasis = {}, const gfx::Point& aOffset = gfx::Point(),
+ float aZoomFactor = 1.0);
+ static already_AddRefed<Path> BuildPath(
+ Span<const StyleShapeCommand> aShape, PathBuilder* aBuilder,
+ StyleStrokeLinecap aStrokeLineCap, Float aStrokeWidth,
+ const CSSSize& aBasis, const gfx::Point& aOffset = gfx::Point(),
+ float aZoomFactor = 1.0);
const_iterator begin() const { return mData.Elements(); }
const_iterator end() const { return mData.Elements() + mData.Length(); }
diff --git a/dom/svg/SVGPathElement.cpp b/dom/svg/SVGPathElement.cpp
index 61d2dab070..e9f500bbfd 100644
--- a/dom/svg/SVGPathElement.cpp
+++ b/dom/svg/SVGPathElement.cpp
@@ -351,7 +351,7 @@ bool SVGPathElement::IsClosedLoop() const {
const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
if (styleSVGReset->mD.IsPath()) {
isClosed = !styleSVGReset->mD.AsPath()._0.IsEmpty() &&
- styleSVGReset->mD.AsPath()._0.AsSpan().rbegin()->IsClosePath();
+ styleSVGReset->mD.AsPath()._0.AsSpan().rbegin()->IsClose();
}
};
diff --git a/dom/svg/SVGPathSegUtils.cpp b/dom/svg/SVGPathSegUtils.cpp
index 149f76f250..dfe6b5eec9 100644
--- a/dom/svg/SVGPathSegUtils.cpp
+++ b/dom/svg/SVGPathSegUtils.cpp
@@ -417,14 +417,13 @@ void SVGPathSegUtils::TraversePathSegment(const float* aData,
void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
SVGPathTraversalState& aState) {
switch (aCommand.tag) {
- case StylePathCommand::Tag::ClosePath:
+ case StylePathCommand::Tag::Close:
TraverseClosePath(nullptr, aState);
break;
- case StylePathCommand::Tag::MoveTo: {
- const Point& p = aCommand.move_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::Move: {
+ const Point& p = aCommand.move.point.ToGfxPoint();
aState.start = aState.pos =
- aCommand.move_to.absolute == StyleIsAbsolute::Yes ? p
- : aState.pos + p;
+ aCommand.move.by_to == StyleByTo::To ? p : aState.pos + p;
if (aState.ShouldUpdateLengthAndControlPoints()) {
// aState.length is unchanged, since move commands don't affect path=
// length.
@@ -432,10 +431,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
}
break;
}
- case StylePathCommand::Tag::LineTo: {
- Point to = aCommand.line_to.absolute == StyleIsAbsolute::Yes
- ? aCommand.line_to.point.ConvertsToGfxPoint()
- : aState.pos + aCommand.line_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::Line: {
+ Point to = aCommand.line.by_to == StyleByTo::To
+ ? aCommand.line.point.ToGfxPoint()
+ : aState.pos + aCommand.line.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
aState.length += CalcDistanceBetweenPoints(aState.pos, to);
aState.cp1 = aState.cp2 = to;
@@ -443,14 +442,14 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::CurveTo: {
- const bool isRelative = aCommand.curve_to.absolute == StyleIsAbsolute::No;
+ case StylePathCommand::Tag::CubicCurve: {
+ const bool isRelative = aCommand.cubic_curve.by_to == StyleByTo::By;
Point to = isRelative
- ? aState.pos + aCommand.curve_to.point.ConvertsToGfxPoint()
- : aCommand.curve_to.point.ConvertsToGfxPoint();
+ ? aState.pos + aCommand.cubic_curve.point.ToGfxPoint()
+ : aCommand.cubic_curve.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
- Point cp1 = aCommand.curve_to.control1.ConvertsToGfxPoint();
- Point cp2 = aCommand.curve_to.control2.ConvertsToGfxPoint();
+ Point cp1 = aCommand.cubic_curve.control1.ToGfxPoint();
+ Point cp2 = aCommand.cubic_curve.control2.ToGfxPoint();
if (isRelative) {
cp1 += aState.pos;
cp2 += aState.pos;
@@ -463,20 +462,15 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::QuadBezierCurveTo: {
- const bool isRelative =
- aCommand.quad_bezier_curve_to.absolute == StyleIsAbsolute::No;
- Point to =
- isRelative
- ? aState.pos +
- aCommand.quad_bezier_curve_to.point.ConvertsToGfxPoint()
- : aCommand.quad_bezier_curve_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::QuadCurve: {
+ const bool isRelative = aCommand.quad_curve.by_to == StyleByTo::By;
+ Point to = isRelative
+ ? aState.pos + aCommand.quad_curve.point.ToGfxPoint()
+ : aCommand.quad_curve.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
- Point cp =
- isRelative
- ? aState.pos + aCommand.quad_bezier_curve_to.control1
- .ConvertsToGfxPoint()
- : aCommand.quad_bezier_curve_to.control1.ConvertsToGfxPoint();
+ Point cp = isRelative
+ ? aState.pos + aCommand.quad_curve.control1.ToGfxPoint()
+ : aCommand.quad_curve.control1.ToGfxPoint();
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
aState.cp1 = cp;
aState.cp2 = to;
@@ -484,21 +478,22 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::EllipticalArc: {
- Point to =
- aCommand.elliptical_arc.absolute == StyleIsAbsolute::Yes
- ? aCommand.elliptical_arc.point.ConvertsToGfxPoint()
- : aState.pos + aCommand.elliptical_arc.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::Arc: {
+ const auto& arc = aCommand.arc;
+ Point to = arc.by_to == StyleByTo::To
+ ? arc.point.ToGfxPoint()
+ : aState.pos + arc.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
- const auto& arc = aCommand.elliptical_arc;
float dist = 0;
- Point radii(arc.rx, arc.ry);
+ Point radii = arc.radii.ToGfxPoint();
if (radii.x == 0.0f || radii.y == 0.0f) {
dist = CalcDistanceBetweenPoints(aState.pos, to);
} else {
Point bez[4] = {aState.pos, Point(0, 0), Point(0, 0), Point(0, 0)};
- SVGArcConverter converter(aState.pos, to, radii, arc.angle,
- arc.large_arc_flag._0, arc.sweep_flag._0);
+ const bool largeArcFlag = arc.arc_size == StyleArcSize::Large;
+ const bool sweepFlag = arc.arc_sweep == StyleArcSweep::Cw;
+ SVGArcConverter converter(aState.pos, to, radii, arc.rotate,
+ largeArcFlag, sweepFlag);
while (converter.GetNextSegment(&bez[1], &bez[2], &bez[3])) {
dist += CalcBezLengthHelper(bez, 4, 0, SplitCubicBezier);
bez[0] = bez[3];
@@ -510,10 +505,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::HorizontalLineTo: {
- Point to(aCommand.horizontal_line_to.absolute == StyleIsAbsolute::Yes
- ? aCommand.horizontal_line_to.x
- : aState.pos.x + aCommand.horizontal_line_to.x,
+ case StylePathCommand::Tag::HLine: {
+ Point to(aCommand.h_line.by_to == StyleByTo::To
+ ? aCommand.h_line.x
+ : aState.pos.x + aCommand.h_line.x,
aState.pos.y);
if (aState.ShouldUpdateLengthAndControlPoints()) {
aState.length += std::fabs(to.x - aState.pos.x);
@@ -522,11 +517,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::VerticalLineTo: {
- Point to(aState.pos.x,
- aCommand.vertical_line_to.absolute == StyleIsAbsolute::Yes
- ? aCommand.vertical_line_to.y
- : aState.pos.y + aCommand.vertical_line_to.y);
+ case StylePathCommand::Tag::VLine: {
+ Point to(aState.pos.x, aCommand.v_line.by_to == StyleByTo::To
+ ? aCommand.v_line.y
+ : aState.pos.y + aCommand.v_line.y);
if (aState.ShouldUpdateLengthAndControlPoints()) {
aState.length += std::fabs(to.y - aState.pos.y);
aState.cp1 = aState.cp2 = to;
@@ -534,20 +528,16 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::SmoothCurveTo: {
- const bool isRelative =
- aCommand.smooth_curve_to.absolute == StyleIsAbsolute::No;
- Point to =
- isRelative
- ? aState.pos + aCommand.smooth_curve_to.point.ConvertsToGfxPoint()
- : aCommand.smooth_curve_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::SmoothCubic: {
+ const bool isRelative = aCommand.smooth_cubic.by_to == StyleByTo::By;
+ Point to = isRelative
+ ? aState.pos + aCommand.smooth_cubic.point.ToGfxPoint()
+ : aCommand.smooth_cubic.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
Point cp1 = aState.pos - (aState.cp2 - aState.pos);
- Point cp2 =
- isRelative
- ? aState.pos +
- aCommand.smooth_curve_to.control2.ConvertsToGfxPoint()
- : aCommand.smooth_curve_to.control2.ConvertsToGfxPoint();
+ Point cp2 = isRelative ? aState.pos +
+ aCommand.smooth_cubic.control2.ToGfxPoint()
+ : aCommand.smooth_cubic.control2.ToGfxPoint();
aState.length +=
(float)CalcLengthOfCubicBezier(aState.pos, cp1, cp2, to);
aState.cp2 = cp2;
@@ -556,12 +546,10 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::SmoothQuadBezierCurveTo: {
- Point to =
- aCommand.smooth_quad_bezier_curve_to.absolute == StyleIsAbsolute::Yes
- ? aCommand.smooth_quad_bezier_curve_to.point.ConvertsToGfxPoint()
- : aState.pos + aCommand.smooth_quad_bezier_curve_to.point
- .ConvertsToGfxPoint();
+ case StylePathCommand::Tag::SmoothQuad: {
+ Point to = aCommand.smooth_quad.by_to == StyleByTo::To
+ ? aCommand.smooth_quad.point.ToGfxPoint()
+ : aState.pos + aCommand.smooth_quad.point.ToGfxPoint();
if (aState.ShouldUpdateLengthAndControlPoints()) {
Point cp = aState.pos - (aState.cp1 - aState.pos);
aState.length += (float)CalcLengthOfQuadraticBezier(aState.pos, cp, to);
@@ -571,8 +559,6 @@ void SVGPathSegUtils::TraversePathSegment(const StylePathCommand& aCommand,
aState.pos = to;
break;
}
- case StylePathCommand::Tag::Unknown:
- MOZ_ASSERT_UNREACHABLE("Unacceptable path segment type");
}
}
@@ -704,8 +690,8 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
for (const StylePathCommand& cmd : aPath) {
switch (cmd.tag) {
- case StylePathCommand::Tag::MoveTo: {
- Point to = cmd.move_to.point.ConvertsToGfxPoint();
+ case StylePathCommand::Tag::Move: {
+ Point to = cmd.move.point.ToGfxPoint();
if (helper.idx != 0) {
// This is overly strict since empty moveto sequences such as "M 10 12
// M 3 2 M 0 0" render nothing, but I expect it won't make us miss a
@@ -731,7 +717,7 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
return Nothing();
}
- if (cmd.move_to.absolute == StyleIsAbsolute::No) {
+ if (cmd.move.by_to == StyleByTo::By) {
to = segStart + to;
}
@@ -744,7 +730,7 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
break;
}
- case StylePathCommand::Tag::ClosePath: {
+ case StylePathCommand::Tag::Close: {
if (!helper.Edge(segStart, pathStart)) {
return Nothing();
}
@@ -754,9 +740,9 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
pathStart = segStart;
break;
}
- case StylePathCommand::Tag::LineTo: {
- Point to = cmd.line_to.point.ConvertsToGfxPoint();
- if (cmd.line_to.absolute == StyleIsAbsolute::No) {
+ case StylePathCommand::Tag::Line: {
+ Point to = cmd.line.point.ToGfxPoint();
+ if (cmd.line.by_to == StyleByTo::By) {
to = segStart + to;
}
@@ -766,9 +752,9 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
segStart = to;
break;
}
- case StylePathCommand::Tag::HorizontalLineTo: {
- Point to = gfx::Point(cmd.horizontal_line_to.x, segStart.y);
- if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::No) {
+ case StylePathCommand::Tag::HLine: {
+ Point to = gfx::Point(cmd.h_line.x, segStart.y);
+ if (cmd.h_line.by_to == StyleByTo::By) {
to.x += segStart.x;
}
@@ -778,9 +764,9 @@ Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath) {
segStart = to;
break;
}
- case StylePathCommand::Tag::VerticalLineTo: {
- Point to = gfx::Point(segStart.x, cmd.vertical_line_to.y);
- if (cmd.horizontal_line_to.absolute == StyleIsAbsolute::No) {
+ case StylePathCommand::Tag::VLine: {
+ Point to = gfx::Point(segStart.x, cmd.v_line.y);
+ if (cmd.h_line.by_to == StyleByTo::By) {
to.y += segStart.y;
}
diff --git a/dom/svg/SVGPathSegUtils.h b/dom/svg/SVGPathSegUtils.h
index f0877d9a59..40a0952bfb 100644
--- a/dom/svg/SVGPathSegUtils.h
+++ b/dom/svg/SVGPathSegUtils.h
@@ -14,8 +14,8 @@
#include "nsDebug.h"
namespace mozilla {
-
-struct StylePathCommand;
+template <typename Angle, typename LP>
+struct StyleGenericShapeCommand;
#define NS_SVG_PATH_SEG_MAX_ARGS 7
#define NS_SVG_PATH_SEG_FIRST_VALID_TYPE \
@@ -264,8 +264,9 @@ class SVGPathSegUtils {
* Traverse the given path segment and update the SVGPathTraversalState
* object. This is identical to the above one but accepts StylePathCommand.
*/
- static void TraversePathSegment(const StylePathCommand& aCommand,
- SVGPathTraversalState& aState);
+ static void TraversePathSegment(
+ const StyleGenericShapeCommand<float, float>& aCommand,
+ SVGPathTraversalState& aState);
};
/// Detect whether the path represents a rectangle (for both filling AND
@@ -280,7 +281,8 @@ class SVGPathSegUtils {
/// practice).
///
/// We could implement something similar for polygons.
-Maybe<gfx::Rect> SVGPathToAxisAlignedRect(Span<const StylePathCommand> aPath);
+Maybe<gfx::Rect> SVGPathToAxisAlignedRect(
+ Span<const StyleGenericShapeCommand<float, float>> aPath);
} // namespace mozilla
diff --git a/dom/svg/SVGSVGElement.cpp b/dom/svg/SVGSVGElement.cpp
index f7282569f9..04f5b8662e 100644
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -193,23 +193,30 @@ float SVGSVGElement::GetCurrentTimeAsFloat() {
}
void SVGSVGElement::SetCurrentTime(float seconds) {
- if (mTimedDocumentRoot) {
- // Make sure the timegraph is up-to-date
- FlushAnimations();
- double fMilliseconds = double(seconds) * PR_MSEC_PER_SEC;
- // Round to nearest whole number before converting, to avoid precision
- // errors
- SMILTime lMilliseconds = SVGUtils::ClampToInt64(NS_round(fMilliseconds));
- mTimedDocumentRoot->SetCurrentTime(lMilliseconds);
- AnimationNeedsResample();
- // Trigger synchronous sample now, to:
- // - Make sure we get an up-to-date paint after this method
- // - re-enable event firing (it got disabled during seeking, and it
- // doesn't get re-enabled until the first sample after the seek -- so
- // let's make that happen now.)
- FlushAnimations();
+ if (!mTimedDocumentRoot) {
+ // we're not the outermost <svg> or not bound to a tree, so silently fail
+ return;
}
- // else we're not the outermost <svg> or not bound to a tree, so silently fail
+ // Make sure the timegraph is up-to-date
+ if (auto* currentDoc = GetComposedDoc()) {
+ currentDoc->FlushPendingNotifications(FlushType::Style);
+ }
+ if (!mTimedDocumentRoot) {
+ return;
+ }
+ FlushAnimations();
+ double fMilliseconds = double(seconds) * PR_MSEC_PER_SEC;
+ // Round to nearest whole number before converting, to avoid precision
+ // errors
+ SMILTime lMilliseconds = SVGUtils::ClampToInt64(NS_round(fMilliseconds));
+ mTimedDocumentRoot->SetCurrentTime(lMilliseconds);
+ AnimationNeedsResample();
+ // Trigger synchronous sample now, to:
+ // - Make sure we get an up-to-date paint after this method
+ // - re-enable event firing (it got disabled during seeking, and it
+ // doesn't get re-enabled until the first sample after the seek -- so
+ // let's make that happen now.)
+ FlushAnimations();
}
void SVGSVGElement::DeselectAll() {
diff --git a/dom/svg/SVGUseElement.cpp b/dom/svg/SVGUseElement.cpp
index 2db8649ba1..0bd9ce63db 100644
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -127,6 +127,11 @@ void SVGUseElement::ProcessAttributeChange(int32_t aNamespaceID,
}
}
+void SVGUseElement::DidAnimateAttribute(int32_t aNameSpaceID,
+ nsAtom* aAttribute) {
+ ProcessAttributeChange(aNameSpaceID, aAttribute);
+}
+
void SVGUseElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aAttribute,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
diff --git a/dom/svg/SVGUseElement.h b/dom/svg/SVGUseElement.h
index 3bdf3fc5bb..47b9db3e0c 100644
--- a/dom/svg/SVGUseElement.h
+++ b/dom/svg/SVGUseElement.h
@@ -143,6 +143,7 @@ class SVGUseElement final : public SVGUseElementBase,
SVGUseElement* mOwningUseElement;
};
+ void DidAnimateAttribute(int32_t aNameSpaceID, nsAtom* aAttribute) override;
SVGUseFrame* GetFrame() const;
LengthAttributesInfo GetLengthInfo() override;
diff --git a/dom/svg/crashtests/1858792.html b/dom/svg/crashtests/1858792.html
new file mode 100644
index 0000000000..3cbdac2382
--- /dev/null
+++ b/dom/svg/crashtests/1858792.html
@@ -0,0 +1,15 @@
+<script>
+window.onload = () => {
+ a.setAttribute("aria-setsize", "512")
+}
+function func() {
+ a.innerHTML = "A"
+}
+</script>
+<svg>
+<foreignObject id="a">
+<iframe></iframe>
+</foreignObject>
+<animate repeatDur="0s" onend="func()"></animate>
+<animate attributeName="x" by="6%">
+<use href="#a">
diff --git a/dom/svg/crashtests/crashtests.list b/dom/svg/crashtests/crashtests.list
index 683fae76cd..bda385bb3e 100644
--- a/dom/svg/crashtests/crashtests.list
+++ b/dom/svg/crashtests/crashtests.list
@@ -98,4 +98,5 @@ load 1572904.html
load 1683907.html
pref(dom.svg.pathSeg.enabled,true) load 1715387.html
load 1837487.html
+load 1858792.html
load 1861736.html
diff --git a/dom/svg/test/getCTM-helper.svg b/dom/svg/test/getCTM-helper.svg
index 835efc5067..5a21feb99f 100644
--- a/dom/svg/test/getCTM-helper.svg
+++ b/dom/svg/test/getCTM-helper.svg
@@ -1,6 +1,7 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100" height="100" viewBox="-11 -22 100 100">
+ <g id="base">
<g transform="translate(3, 4)">
<svg x="1" y="2" width="888" height="999">
<g>
@@ -44,4 +45,5 @@
</g>
</svg>
</g>
+ </g>
</svg>
diff --git a/dom/svg/test/test_getCTM.html b/dom/svg/test/test_getCTM.html
index 57ea79c861..332d935811 100644
--- a/dom/svg/test/test_getCTM.html
+++ b/dom/svg/test/test_getCTM.html
@@ -28,36 +28,36 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=366697
SimpleTest.waitForExplicitFinish();
function runTest() {
- var doc = $("svg").contentWindow.document;
+ let doc = $("svg").contentWindow.document;
/* Minimal */
- var buggy = doc.getElementById("buggy");
+ let buggy = doc.getElementById("buggy");
is(buggy.getCTM().e, 30, "buggy.getCTM().e");
is(buggy.getCTM().f, 40, "buggy.getCTM().f");
- var transrect1 = document.getElementById("transrect1");
+ let transrect1 = document.getElementById("transrect1");
is(transrect1.getCTM().a, 2, "transrect1.getCTM().a");
is(transrect1.getCTM().d, 3, "transrect1.getCTM().d");
- var padsvg1 = document.getElementById("padsvg1");
- var ctm = padsvg1.getScreenCTM();
- var rect = padsvg1.getBoundingClientRect();
+ let padsvg1 = document.getElementById("padsvg1");
+ let ctm = padsvg1.getScreenCTM();
+ let rect = padsvg1.getBoundingClientRect();
// Use isfuzzy to ignore some miniscule floating-point precision error on
// certain platforms:
isfuzzy(ctm.e - rect.x, 27, 0.0001, "padsvg1.getScreenCTM().e");
is(ctm.f - rect.y, 43, "padsvg1.getScreenCTM().f");
- var root = doc.documentElement;
- var inner = doc.getElementById("inner");
- var g1 = doc.getElementById("g1");
- var outer = doc.getElementById("outer");
- var outer2 = doc.getElementById("outer2");
- var g2 = doc.getElementById("g2");
- var g3 = doc.getElementById("g3");
- var g4 = doc.getElementById("g4");
- var g5 = doc.getElementById("g5");
- var symbolRect = doc.getElementById("symbolRect");
- var fO = doc.getElementById("fO");
+ let root = doc.documentElement;
+ let inner = doc.getElementById("inner");
+ let g1 = doc.getElementById("g1");
+ let outer = doc.getElementById("outer");
+ let outer2 = doc.getElementById("outer2");
+ let g2 = doc.getElementById("g2");
+ let g3 = doc.getElementById("g3");
+ let g4 = doc.getElementById("g4");
+ let g5 = doc.getElementById("g5");
+ let symbolRect = doc.getElementById("symbolRect");
+ let fO = doc.getElementById("fO");
/* Tests the consistency with nearestViewportElement
(code is from test_viewport.html) */
// root.nearestViewportElement == null
@@ -102,8 +102,8 @@ function runTest() {
is((function() { try { return outer.getScreenCTM().e; } catch (e) { return e; } })(), 46, "outer.getScreenCTM().e");
is((function() { try { return outer.getScreenCTM().f; } catch (e) { return e; } })(), 69, "outer.getScreenCTM().f");
// outer.farthestViewportElement == null (but actually == root)
- is((function() { try { return outer2.getScreenCTM().e; } catch (e) { return e; } })(), -19, "outer2.getScreenCTM().e");
- is((function() { try { return outer2.getScreenCTM().f; } catch (e) { return e; } })(), -8, "outer2.getScreenCTM().f");
+ is((function() { try { return outer2.getScreenCTM().e; } catch (e) { return e; } })(), -4, "outer2.getScreenCTM().e");
+ is((function() { try { return outer2.getScreenCTM().f; } catch (e) { return e; } })(), 19, "outer2.getScreenCTM().f");
// g2.farthestViewportElement == outer (but actually == root)
is((function() { try { return g2.getScreenCTM().e; } catch (e) { return e; } })(), 646, "g2.getScreenCTM().e");
is((function() { try { return g2.getScreenCTM().f; } catch (e) { return e; } })(), 769, "g2.getScreenCTM().f");