1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/SVGPathElement.h"
11 #include "SVGGeometryProperty.h"
12 #include "gfx2DGlue.h"
13 #include "gfxPlatform.h"
14 #include "nsGkAtoms.h"
16 #include "nsStyleConsts.h"
17 #include "nsStyleStruct.h"
18 #include "nsWindowSizes.h"
19 #include "mozilla/dom/SVGPathElementBinding.h"
20 #include "mozilla/gfx/2D.h"
21 #include "mozilla/RefPtr.h"
22 #include "SVGPathSegUtils.h"
23 #include "mozilla/SVGContentUtils.h"
25 NS_IMPL_NS_NEW_SVG_ELEMENT(Path
)
27 using namespace mozilla::gfx
;
29 namespace mozilla::dom
{
31 JSObject
* SVGPathElement::WrapNode(JSContext
* aCx
,
32 JS::Handle
<JSObject
*> aGivenProto
) {
33 return SVGPathElement_Binding::Wrap(aCx
, this, aGivenProto
);
36 //----------------------------------------------------------------------
39 SVGPathElement::SVGPathElement(
40 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
41 : SVGPathElementBase(std::move(aNodeInfo
)) {}
43 //----------------------------------------------------------------------
44 // memory reporting methods
46 void SVGPathElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
47 size_t* aNodeSize
) const {
48 SVGPathElementBase::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
49 *aNodeSize
+= mD
.SizeOfExcludingThis(aSizes
.mState
.mMallocSizeOf
);
52 //----------------------------------------------------------------------
55 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement
)
57 //----------------------------------------------------------------------
61 bool SVGPathElement::HasValidDimensions() const {
63 auto callback
= [&](const ComputedStyle
* s
) {
64 const nsStyleSVGReset
* styleSVGReset
= s
->StyleSVGReset();
66 styleSVGReset
->mD
.IsPath() && !styleSVGReset
->mD
.AsPath()._0
.IsEmpty();
69 SVGGeometryProperty::DoForComputedStyle(this, callback
);
70 // If hasPath is false, we may disable the pref of d property, so we fallback
72 return hasPath
|| !mD
.GetAnimValue().IsEmpty();
75 //----------------------------------------------------------------------
79 SVGPathElement::IsAttributeMapped(const nsAtom
* name
) const {
80 return name
== nsGkAtoms::d
|| SVGPathElementBase::IsAttributeMapped(name
);
83 already_AddRefed
<Path
> SVGPathElement::GetOrBuildPathForMeasuring() {
85 bool success
= SVGGeometryProperty::DoForComputedStyle(
86 this, [&path
](const ComputedStyle
* s
) {
87 const auto& d
= s
->StyleSVGReset()->mD
;
91 path
= SVGPathData::BuildPathForMeasuring(d
.AsPath()._0
.AsSpan(),
92 s
->EffectiveZoom().ToFloat());
94 return success
? path
.forget()
95 : mD
.GetAnimValue().BuildPathForMeasuring(1.0f
);
98 //----------------------------------------------------------------------
99 // SVGGeometryElement methods
101 bool SVGPathElement::AttributeDefinesGeometry(const nsAtom
* aName
) {
102 return aName
== nsGkAtoms::d
|| aName
== nsGkAtoms::pathLength
;
105 bool SVGPathElement::IsMarkable() { return true; }
107 void SVGPathElement::GetMarkPoints(nsTArray
<SVGMark
>* aMarks
) {
108 auto callback
= [aMarks
](const ComputedStyle
* s
) {
109 const nsStyleSVGReset
* styleSVGReset
= s
->StyleSVGReset();
110 if (styleSVGReset
->mD
.IsPath()) {
111 Span
<const StylePathCommand
> path
=
112 styleSVGReset
->mD
.AsPath()._0
.AsSpan();
113 SVGPathData::GetMarkerPositioningData(path
, s
->EffectiveZoom().ToFloat(),
118 if (SVGGeometryProperty::DoForComputedStyle(this, callback
)) {
122 mD
.GetAnimValue().GetMarkerPositioningData(1.0f
, aMarks
);
125 void SVGPathElement::GetAsSimplePath(SimplePath
* aSimplePath
) {
126 aSimplePath
->Reset();
127 auto callback
= [&](const ComputedStyle
* s
) {
128 const nsStyleSVGReset
* styleSVGReset
= s
->StyleSVGReset();
129 if (styleSVGReset
->mD
.IsPath()) {
130 auto pathData
= styleSVGReset
->mD
.AsPath()._0
.AsSpan();
131 auto maybeRect
= SVGPathToAxisAlignedRect(pathData
);
132 if (maybeRect
.isSome()) {
133 const Rect
& r
= *maybeRect
;
134 float zoom
= s
->EffectiveZoom().ToFloat();
135 aSimplePath
->SetRect(r
.x
* zoom
, r
.y
* zoom
, r
.width
* zoom
,
141 SVGGeometryProperty::DoForComputedStyle(this, callback
);
144 already_AddRefed
<Path
> SVGPathElement::BuildPath(PathBuilder
* aBuilder
) {
145 // The Moz2D PathBuilder that our SVGPathData will be using only cares about
146 // the fill rule. However, in order to fulfill the requirements of the SVG
147 // spec regarding zero length sub-paths when square line caps are in use,
148 // SVGPathData needs to know our stroke-linecap style and, if "square", then
149 // also our stroke width. See the comment for
150 // ApproximateZeroLengthSubpathSquareCaps for more info.
152 auto strokeLineCap
= StyleStrokeLinecap::Butt
;
153 Float strokeWidth
= 0;
156 auto callback
= [&](const ComputedStyle
* s
) {
157 const nsStyleSVG
* styleSVG
= s
->StyleSVG();
158 // Note: the path that we return may be used for hit-testing, and SVG
159 // exposes hit-testing of strokes that are not actually painted. For that
160 // reason we do not check for eStyleSVGPaintType_None or check the stroke
162 if (styleSVG
->mStrokeLinecap
!= StyleStrokeLinecap::Butt
) {
163 strokeLineCap
= styleSVG
->mStrokeLinecap
;
164 strokeWidth
= SVGContentUtils::GetStrokeWidth(this, s
, nullptr);
167 const auto& d
= s
->StyleSVGReset()->mD
;
169 path
= SVGPathData::BuildPath(d
.AsPath()._0
.AsSpan(), aBuilder
,
170 strokeLineCap
, strokeWidth
, {}, {},
171 s
->EffectiveZoom().ToFloat());
175 bool success
= SVGGeometryProperty::DoForComputedStyle(this, callback
);
177 return path
.forget();
180 // Fallback to use the d attribute if it exists.
181 return mD
.GetAnimValue().BuildPath(aBuilder
, strokeLineCap
, strokeWidth
,
185 bool SVGPathElement::GetDistancesFromOriginToEndsOfVisibleSegments(
186 FallibleTArray
<double>* aOutput
) {
188 auto callback
= [&ret
, aOutput
](const ComputedStyle
* s
) {
189 const auto& d
= s
->StyleSVGReset()->mD
;
191 SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(
192 d
.AsPath()._0
.AsSpan(), aOutput
);
195 if (SVGGeometryProperty::DoForComputedStyle(this, callback
)) {
199 return mD
.GetAnimValue().GetDistancesFromOriginToEndsOfVisibleSegments(
203 static bool PathIsClosed(Span
<const StylePathCommand
> aPath
) {
204 return !aPath
.IsEmpty() && aPath
.rbegin()->IsClose();
207 // Offset paths (including references to SVG Paths) are closed loops only if the
208 // final command in the path list is a closepath command ("z" or "Z"), otherwise
209 // they are unclosed intervals.
210 // https://drafts.fxtf.org/motion/#path-distance
211 bool SVGPathElement::IsClosedLoop() const {
212 bool isClosed
= false;
214 auto callback
= [&](const ComputedStyle
* s
) {
215 const nsStyleSVGReset
* styleSVGReset
= s
->StyleSVGReset();
216 if (styleSVGReset
->mD
.IsPath()) {
217 isClosed
= PathIsClosed(styleSVGReset
->mD
.AsPath()._0
.AsSpan());
221 if (SVGGeometryProperty::DoForComputedStyle(this, callback
)) {
225 return PathIsClosed(mD
.GetAnimValue().AsSpan());
229 bool SVGPathElement::IsDPropertyChangedViaCSS(const ComputedStyle
& aNewStyle
,
230 const ComputedStyle
& aOldStyle
) {
231 return aNewStyle
.StyleSVGReset()->mD
!= aOldStyle
.StyleSVGReset()->mD
;
234 } // namespace mozilla::dom