From da4c7e7ed675c3bf405668739c3012d140856109 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:42 +0200 Subject: Adding upstream version 126.0. Signed-off-by: Daniel Baumann --- dom/svg/SVGAElement.cpp | 10 ++ dom/svg/SVGAElement.h | 4 +- dom/svg/SVGContentUtils.cpp | 85 +++++++--- dom/svg/SVGElement.cpp | 2 +- dom/svg/SVGElement.h | 4 +- dom/svg/SVGFEImageElement.cpp | 17 ++ dom/svg/SVGFEImageElement.h | 1 + dom/svg/SVGGeometryElement.cpp | 2 +- dom/svg/SVGGraphicsElement.cpp | 6 +- dom/svg/SVGImageElement.cpp | 17 ++ dom/svg/SVGImageElement.h | 2 + dom/svg/SVGPathData.cpp | 337 +++++++++++++++++++------------------ dom/svg/SVGPathData.h | 14 +- dom/svg/SVGPathElement.cpp | 2 +- dom/svg/SVGPathSegUtils.cpp | 144 +++++++--------- dom/svg/SVGPathSegUtils.h | 12 +- dom/svg/SVGSVGElement.cpp | 39 +++-- dom/svg/SVGUseElement.cpp | 5 + dom/svg/SVGUseElement.h | 1 + dom/svg/crashtests/1858792.html | 15 ++ dom/svg/crashtests/crashtests.list | 1 + dom/svg/test/getCTM-helper.svg | 2 + dom/svg/test/test_getCTM.html | 38 ++--- 23 files changed, 437 insertions(+), 323 deletions(-) create mode 100644 dom/svg/crashtests/1858792.html (limited to 'dom/svg') 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(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 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 SVGGraphicsElement::GetBBox( } already_AddRefed 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 SVGGraphicsElement::GetCTM() { } already_AddRefed 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 SVGPathData::BuildPath(PathBuilder* aBuilder, return aBuilder->Finish(); } +#undef MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS_TO_DT + already_AddRefed 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 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 SVGPathData::BuildPath( - Span 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 +static already_AddRefed BuildPathInternal( + Span> aPath, + PathBuilder* aBuilder, StyleStrokeLinecap aStrokeLineCap, + Float aStrokeWidth, const CSSSize& aPercentageBasis, const Point& aOffset, float aZoomFactor) { - if (aPath.IsEmpty() || !aPath[0].IsMoveTo()) { + using Command = StyleGenericShapeCommand; + + if (aPath.IsEmpty() || !aPath[0].IsMove()) { return nullptr; // paths without an initial moveto are invalid } @@ -592,14 +590,24 @@ already_AddRefed 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 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 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 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 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 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 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 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 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 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 SVGPathData::BuildPath( + Span 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 SVGPathData::BuildPath( + Span 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 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 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 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 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 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 aPath, static_cast(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 namespace mozilla { -struct StylePathCommand; struct SVGMark; enum class StyleStrokeLinecap : uint8_t; @@ -190,14 +190,22 @@ class SVGPathData { Span 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 BuildPath( Span 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 BuildPath( + Span 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 SVGPathToAxisAlignedRect(Span 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 SVGPathToAxisAlignedRect(Span 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 SVGPathToAxisAlignedRect(Span aPath) { break; } - case StylePathCommand::Tag::ClosePath: { + case StylePathCommand::Tag::Close: { if (!helper.Edge(segStart, pathStart)) { return Nothing(); } @@ -754,9 +740,9 @@ Maybe SVGPathToAxisAlignedRect(Span 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 SVGPathToAxisAlignedRect(Span 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 SVGPathToAxisAlignedRect(Span 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 +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& 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 SVGPathToAxisAlignedRect(Span aPath); +Maybe SVGPathToAxisAlignedRect( + Span> 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 or not bound to a tree, so silently fail + return; } - // else we're not the outermost 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 @@ + + + + + + + + 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 @@ + @@ -44,4 +45,5 @@ + 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"); -- cgit v1.2.3