Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / dom / svg / SVGPathElement.cpp
blobc49c5b9f72bb30d8c32792cbe789c0b2eaacfc78
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"
9 #include <algorithm>
11 #include "DOMSVGPathSeg.h"
12 #include "DOMSVGPathSegList.h"
13 #include "SVGGeometryProperty.h"
14 #include "gfx2DGlue.h"
15 #include "gfxPlatform.h"
16 #include "nsGkAtoms.h"
17 #include "nsIFrame.h"
18 #include "nsStyleConsts.h"
19 #include "nsStyleStruct.h"
20 #include "nsWindowSizes.h"
21 #include "mozilla/dom/SVGPathElementBinding.h"
22 #include "mozilla/gfx/2D.h"
23 #include "mozilla/RefPtr.h"
24 #include "mozilla/StaticPrefs_layout.h"
25 #include "mozilla/SVGContentUtils.h"
27 NS_IMPL_NS_NEW_SVG_ELEMENT(Path)
29 using namespace mozilla::gfx;
31 namespace mozilla::dom {
33 JSObject* SVGPathElement::WrapNode(JSContext* aCx,
34 JS::Handle<JSObject*> aGivenProto) {
35 return SVGPathElement_Binding::Wrap(aCx, this, aGivenProto);
38 //----------------------------------------------------------------------
39 // Implementation
41 SVGPathElement::SVGPathElement(
42 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
43 : SVGPathElementBase(std::move(aNodeInfo)) {}
45 //----------------------------------------------------------------------
46 // memory reporting methods
48 void SVGPathElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
49 size_t* aNodeSize) const {
50 SVGPathElementBase::AddSizeOfExcludingThis(aSizes, aNodeSize);
51 *aNodeSize += mD.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
54 //----------------------------------------------------------------------
55 // nsINode methods
57 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
59 uint32_t SVGPathElement::GetPathSegAtLength(float distance) {
60 uint32_t seg = 0;
61 auto callback = [&](const ComputedStyle* s) {
62 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
63 if (styleSVGReset->mD.IsPath()) {
64 seg = SVGPathData::GetPathSegAtLength(
65 styleSVGReset->mD.AsPath()._0.AsSpan(), distance);
69 FlushStyleIfNeeded();
70 if (SVGGeometryProperty::DoForComputedStyle(this, callback)) {
71 return seg;
73 return mD.GetAnimValue().GetPathSegAtLength(distance);
76 already_AddRefed<DOMSVGPathSegClosePath>
77 SVGPathElement::CreateSVGPathSegClosePath() {
78 RefPtr<DOMSVGPathSegClosePath> pathSeg = new DOMSVGPathSegClosePath();
79 return pathSeg.forget();
82 already_AddRefed<DOMSVGPathSegMovetoAbs>
83 SVGPathElement::CreateSVGPathSegMovetoAbs(float x, float y) {
84 RefPtr<DOMSVGPathSegMovetoAbs> pathSeg = new DOMSVGPathSegMovetoAbs(x, y);
85 return pathSeg.forget();
88 already_AddRefed<DOMSVGPathSegMovetoRel>
89 SVGPathElement::CreateSVGPathSegMovetoRel(float x, float y) {
90 RefPtr<DOMSVGPathSegMovetoRel> pathSeg = new DOMSVGPathSegMovetoRel(x, y);
91 return pathSeg.forget();
94 already_AddRefed<DOMSVGPathSegLinetoAbs>
95 SVGPathElement::CreateSVGPathSegLinetoAbs(float x, float y) {
96 RefPtr<DOMSVGPathSegLinetoAbs> pathSeg = new DOMSVGPathSegLinetoAbs(x, y);
97 return pathSeg.forget();
100 already_AddRefed<DOMSVGPathSegLinetoRel>
101 SVGPathElement::CreateSVGPathSegLinetoRel(float x, float y) {
102 RefPtr<DOMSVGPathSegLinetoRel> pathSeg = new DOMSVGPathSegLinetoRel(x, y);
103 return pathSeg.forget();
106 already_AddRefed<DOMSVGPathSegCurvetoCubicAbs>
107 SVGPathElement::CreateSVGPathSegCurvetoCubicAbs(float x, float y, float x1,
108 float y1, float x2, float y2) {
109 // Note that we swap from DOM API argument order to the argument order used
110 // in the <path> element's 'd' attribute (i.e. we put the arguments for the
111 // end point of the segment last instead of first).
112 RefPtr<DOMSVGPathSegCurvetoCubicAbs> pathSeg =
113 new DOMSVGPathSegCurvetoCubicAbs(x1, y1, x2, y2, x, y);
114 return pathSeg.forget();
117 already_AddRefed<DOMSVGPathSegCurvetoCubicRel>
118 SVGPathElement::CreateSVGPathSegCurvetoCubicRel(float x, float y, float x1,
119 float y1, float x2, float y2) {
120 // See comment in CreateSVGPathSegCurvetoCubicAbs
121 RefPtr<DOMSVGPathSegCurvetoCubicRel> pathSeg =
122 new DOMSVGPathSegCurvetoCubicRel(x1, y1, x2, y2, x, y);
123 return pathSeg.forget();
126 already_AddRefed<DOMSVGPathSegCurvetoQuadraticAbs>
127 SVGPathElement::CreateSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1,
128 float y1) {
129 // See comment in CreateSVGPathSegCurvetoCubicAbs
130 RefPtr<DOMSVGPathSegCurvetoQuadraticAbs> pathSeg =
131 new DOMSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y);
132 return pathSeg.forget();
135 already_AddRefed<DOMSVGPathSegCurvetoQuadraticRel>
136 SVGPathElement::CreateSVGPathSegCurvetoQuadraticRel(float x, float y, float x1,
137 float y1) {
138 // See comment in CreateSVGPathSegCurvetoCubicAbs
139 RefPtr<DOMSVGPathSegCurvetoQuadraticRel> pathSeg =
140 new DOMSVGPathSegCurvetoQuadraticRel(x1, y1, x, y);
141 return pathSeg.forget();
144 already_AddRefed<DOMSVGPathSegArcAbs> SVGPathElement::CreateSVGPathSegArcAbs(
145 float x, float y, float r1, float r2, float angle, bool largeArcFlag,
146 bool sweepFlag) {
147 // See comment in CreateSVGPathSegCurvetoCubicAbs
148 RefPtr<DOMSVGPathSegArcAbs> pathSeg =
149 new DOMSVGPathSegArcAbs(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
150 return pathSeg.forget();
153 already_AddRefed<DOMSVGPathSegArcRel> SVGPathElement::CreateSVGPathSegArcRel(
154 float x, float y, float r1, float r2, float angle, bool largeArcFlag,
155 bool sweepFlag) {
156 // See comment in CreateSVGPathSegCurvetoCubicAbs
157 RefPtr<DOMSVGPathSegArcRel> pathSeg =
158 new DOMSVGPathSegArcRel(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
159 return pathSeg.forget();
162 already_AddRefed<DOMSVGPathSegLinetoHorizontalAbs>
163 SVGPathElement::CreateSVGPathSegLinetoHorizontalAbs(float x) {
164 RefPtr<DOMSVGPathSegLinetoHorizontalAbs> pathSeg =
165 new DOMSVGPathSegLinetoHorizontalAbs(x);
166 return pathSeg.forget();
169 already_AddRefed<DOMSVGPathSegLinetoHorizontalRel>
170 SVGPathElement::CreateSVGPathSegLinetoHorizontalRel(float x) {
171 RefPtr<DOMSVGPathSegLinetoHorizontalRel> pathSeg =
172 new DOMSVGPathSegLinetoHorizontalRel(x);
173 return pathSeg.forget();
176 already_AddRefed<DOMSVGPathSegLinetoVerticalAbs>
177 SVGPathElement::CreateSVGPathSegLinetoVerticalAbs(float y) {
178 RefPtr<DOMSVGPathSegLinetoVerticalAbs> pathSeg =
179 new DOMSVGPathSegLinetoVerticalAbs(y);
180 return pathSeg.forget();
183 already_AddRefed<DOMSVGPathSegLinetoVerticalRel>
184 SVGPathElement::CreateSVGPathSegLinetoVerticalRel(float y) {
185 RefPtr<DOMSVGPathSegLinetoVerticalRel> pathSeg =
186 new DOMSVGPathSegLinetoVerticalRel(y);
187 return pathSeg.forget();
190 already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothAbs>
191 SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothAbs(float x, float y,
192 float x2, float y2) {
193 // See comment in CreateSVGPathSegCurvetoCubicAbs
194 RefPtr<DOMSVGPathSegCurvetoCubicSmoothAbs> pathSeg =
195 new DOMSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y);
196 return pathSeg.forget();
199 already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothRel>
200 SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothRel(float x, float y,
201 float x2, float y2) {
202 // See comment in CreateSVGPathSegCurvetoCubicAbs
203 RefPtr<DOMSVGPathSegCurvetoCubicSmoothRel> pathSeg =
204 new DOMSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y);
205 return pathSeg.forget();
208 already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothAbs>
209 SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y) {
210 RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothAbs> pathSeg =
211 new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x, y);
212 return pathSeg.forget();
215 already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothRel>
216 SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y) {
217 RefPtr<DOMSVGPathSegCurvetoQuadraticSmoothRel> pathSeg =
218 new DOMSVGPathSegCurvetoQuadraticSmoothRel(x, y);
219 return pathSeg.forget();
222 // FIXME: This API is enabled only if dom.svg.pathSeg.enabled is true. This
223 // preference is off by default in Bug 1388931, and will be dropped later.
224 // So we are not planning to map d property for this API.
225 already_AddRefed<DOMSVGPathSegList> SVGPathElement::PathSegList() {
226 return DOMSVGPathSegList::GetDOMWrapper(mD.GetBaseValKey(), this, false);
229 // FIXME: This API is enabled only if dom.svg.pathSeg.enabled is true. This
230 // preference is off by default in Bug 1388931, and will be dropped later.
231 // So we are not planning to map d property for this API.
232 already_AddRefed<DOMSVGPathSegList> SVGPathElement::AnimatedPathSegList() {
233 return DOMSVGPathSegList::GetDOMWrapper(mD.GetAnimValKey(), this, true);
236 //----------------------------------------------------------------------
237 // SVGElement methods
239 /* virtual */
240 bool SVGPathElement::HasValidDimensions() const {
241 bool hasPath = false;
242 auto callback = [&](const ComputedStyle* s) {
243 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
244 hasPath =
245 styleSVGReset->mD.IsPath() && !styleSVGReset->mD.AsPath()._0.IsEmpty();
248 SVGGeometryProperty::DoForComputedStyle(this, callback);
249 // If hasPath is false, we may disable the pref of d property, so we fallback
250 // to check mD.
251 return hasPath || !mD.GetAnimValue().IsEmpty();
254 //----------------------------------------------------------------------
255 // nsIContent methods
257 NS_IMETHODIMP_(bool)
258 SVGPathElement::IsAttributeMapped(const nsAtom* name) const {
259 return name == nsGkAtoms::d || SVGPathElementBase::IsAttributeMapped(name);
262 already_AddRefed<Path> SVGPathElement::GetOrBuildPathForMeasuring() {
263 RefPtr<Path> path;
264 bool success = SVGGeometryProperty::DoForComputedStyle(
265 this, [&path](const ComputedStyle* s) {
266 const auto& d = s->StyleSVGReset()->mD;
267 if (d.IsNone()) {
268 return;
270 path = SVGPathData::BuildPathForMeasuring(d.AsPath()._0.AsSpan());
272 return success ? path.forget() : mD.GetAnimValue().BuildPathForMeasuring();
275 //----------------------------------------------------------------------
276 // SVGGeometryElement methods
278 bool SVGPathElement::AttributeDefinesGeometry(const nsAtom* aName) {
279 return aName == nsGkAtoms::d || aName == nsGkAtoms::pathLength;
282 bool SVGPathElement::IsMarkable() { return true; }
284 void SVGPathElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) {
285 auto callback = [aMarks](const ComputedStyle* s) {
286 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
287 if (styleSVGReset->mD.IsPath()) {
288 Span<const StylePathCommand> path =
289 styleSVGReset->mD.AsPath()._0.AsSpan();
290 SVGPathData::GetMarkerPositioningData(path, aMarks);
294 if (SVGGeometryProperty::DoForComputedStyle(this, callback)) {
295 return;
298 mD.GetAnimValue().GetMarkerPositioningData(aMarks);
301 void SVGPathElement::GetAsSimplePath(SimplePath* aSimplePath) {
302 aSimplePath->Reset();
303 auto callback = [&](const ComputedStyle* s) {
304 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
305 if (styleSVGReset->mD.IsPath()) {
306 auto pathData = styleSVGReset->mD.AsPath()._0.AsSpan();
307 auto maybeRect = SVGPathToAxisAlignedRect(pathData);
308 if (maybeRect.isSome()) {
309 Rect r = maybeRect.value();
310 aSimplePath->SetRect(r.x, r.y, r.width, r.height);
315 SVGGeometryProperty::DoForComputedStyle(this, callback);
318 already_AddRefed<Path> SVGPathElement::BuildPath(PathBuilder* aBuilder) {
319 // The Moz2D PathBuilder that our SVGPathData will be using only cares about
320 // the fill rule. However, in order to fulfill the requirements of the SVG
321 // spec regarding zero length sub-paths when square line caps are in use,
322 // SVGPathData needs to know our stroke-linecap style and, if "square", then
323 // also our stroke width. See the comment for
324 // ApproximateZeroLengthSubpathSquareCaps for more info.
326 auto strokeLineCap = StyleStrokeLinecap::Butt;
327 Float strokeWidth = 0;
328 RefPtr<Path> path;
330 auto callback = [&](const ComputedStyle* s) {
331 const nsStyleSVG* styleSVG = s->StyleSVG();
332 // Note: the path that we return may be used for hit-testing, and SVG
333 // exposes hit-testing of strokes that are not actually painted. For that
334 // reason we do not check for eStyleSVGPaintType_None or check the stroke
335 // opacity here.
336 if (styleSVG->mStrokeLinecap != StyleStrokeLinecap::Butt) {
337 strokeLineCap = styleSVG->mStrokeLinecap;
338 strokeWidth = SVGContentUtils::GetStrokeWidth(this, s, nullptr);
341 const auto& d = s->StyleSVGReset()->mD;
342 if (d.IsPath()) {
343 path = SVGPathData::BuildPath(d.AsPath()._0.AsSpan(), aBuilder,
344 strokeLineCap, strokeWidth);
348 bool success = SVGGeometryProperty::DoForComputedStyle(this, callback);
349 if (success) {
350 return path.forget();
353 // Fallback to use the d attribute if it exists.
354 return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth);
357 bool SVGPathElement::GetDistancesFromOriginToEndsOfVisibleSegments(
358 FallibleTArray<double>* aOutput) {
359 bool ret = false;
360 auto callback = [&ret, aOutput](const ComputedStyle* s) {
361 const auto& d = s->StyleSVGReset()->mD;
362 ret = d.IsNone() ||
363 SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(
364 d.AsPath()._0.AsSpan(), aOutput);
367 if (SVGGeometryProperty::DoForComputedStyle(this, callback)) {
368 return ret;
371 return mD.GetAnimValue().GetDistancesFromOriginToEndsOfVisibleSegments(
372 aOutput);
375 /* static */
376 bool SVGPathElement::IsDPropertyChangedViaCSS(const ComputedStyle& aNewStyle,
377 const ComputedStyle& aOldStyle) {
378 return aNewStyle.StyleSVGReset()->mD != aOldStyle.StyleSVGReset()->mD;
381 } // namespace mozilla::dom