Bumping gaia.json for 3 gaia revision(s) a=gaia-bump
[gecko.git] / dom / svg / SVGPathElement.cpp
blob25bc7e82525012beb9f869ac380add40e462bcdb
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/SVGPathElement.h"
8 #include <algorithm>
10 #include "DOMSVGPathSeg.h"
11 #include "DOMSVGPathSegList.h"
12 #include "DOMSVGPoint.h"
13 #include "gfx2DGlue.h"
14 #include "gfxPlatform.h"
15 #include "mozilla/dom/SVGPathElementBinding.h"
16 #include "mozilla/gfx/2D.h"
17 #include "mozilla/RefPtr.h"
18 #include "nsCOMPtr.h"
19 #include "nsComputedDOMStyle.h"
20 #include "nsGkAtoms.h"
21 #include "nsStyleConsts.h"
22 #include "nsStyleStruct.h"
23 #include "SVGContentUtils.h"
25 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Path)
27 using namespace mozilla::gfx;
29 namespace mozilla {
30 namespace dom {
32 JSObject*
33 SVGPathElement::WrapNode(JSContext *aCx)
35 return SVGPathElementBinding::Wrap(aCx, this);
38 nsSVGElement::NumberInfo SVGPathElement::sNumberInfo =
39 { &nsGkAtoms::pathLength, 0, false };
41 //----------------------------------------------------------------------
42 // Implementation
44 SVGPathElement::SVGPathElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
45 : SVGPathElementBase(aNodeInfo)
49 //----------------------------------------------------------------------
50 // memory reporting methods
52 size_t
53 SVGPathElement::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
55 return SVGPathElementBase::SizeOfExcludingThis(aMallocSizeOf) +
56 mD.SizeOfExcludingThis(aMallocSizeOf);
59 //----------------------------------------------------------------------
60 // nsIDOMNode methods
62 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
64 already_AddRefed<SVGAnimatedNumber>
65 SVGPathElement::PathLength()
67 return mPathLength.ToDOMAnimatedNumber(this);
70 float
71 SVGPathElement::GetTotalLength()
73 RefPtr<Path> flat = GetOrBuildPathForMeasuring();
74 return flat ? flat->ComputeLength() : 0.f;
77 already_AddRefed<nsISVGPoint>
78 SVGPathElement::GetPointAtLength(float distance, ErrorResult& rv)
80 RefPtr<Path> path = GetOrBuildPathForMeasuring();
81 if (!path) {
82 rv.Throw(NS_ERROR_FAILURE);
83 return nullptr;
86 float totalLength = path->ComputeLength();
87 if (mPathLength.IsExplicitlySet()) {
88 float pathLength = mPathLength.GetAnimValue();
89 if (pathLength <= 0) {
90 rv.Throw(NS_ERROR_FAILURE);
91 return nullptr;
93 distance *= totalLength / pathLength;
95 distance = std::max(0.f, distance);
96 distance = std::min(totalLength, distance);
98 nsCOMPtr<nsISVGPoint> point =
99 new DOMSVGPoint(path->ComputePointAtLength(distance));
100 return point.forget();
103 uint32_t
104 SVGPathElement::GetPathSegAtLength(float distance)
106 return mD.GetAnimValue().GetPathSegAtLength(distance);
109 already_AddRefed<DOMSVGPathSegClosePath>
110 SVGPathElement::CreateSVGPathSegClosePath()
112 nsRefPtr<DOMSVGPathSegClosePath> pathSeg = new DOMSVGPathSegClosePath();
113 return pathSeg.forget();
116 already_AddRefed<DOMSVGPathSegMovetoAbs>
117 SVGPathElement::CreateSVGPathSegMovetoAbs(float x, float y)
119 nsRefPtr<DOMSVGPathSegMovetoAbs> pathSeg = new DOMSVGPathSegMovetoAbs(x, y);
120 return pathSeg.forget();
123 already_AddRefed<DOMSVGPathSegMovetoRel>
124 SVGPathElement::CreateSVGPathSegMovetoRel(float x, float y)
126 nsRefPtr<DOMSVGPathSegMovetoRel> pathSeg = new DOMSVGPathSegMovetoRel(x, y);
127 return pathSeg.forget();
130 already_AddRefed<DOMSVGPathSegLinetoAbs>
131 SVGPathElement::CreateSVGPathSegLinetoAbs(float x, float y)
133 nsRefPtr<DOMSVGPathSegLinetoAbs> pathSeg = new DOMSVGPathSegLinetoAbs(x, y);
134 return pathSeg.forget();
137 already_AddRefed<DOMSVGPathSegLinetoRel>
138 SVGPathElement::CreateSVGPathSegLinetoRel(float x, float y)
140 nsRefPtr<DOMSVGPathSegLinetoRel> pathSeg = new DOMSVGPathSegLinetoRel(x, y);
141 return pathSeg.forget();
144 already_AddRefed<DOMSVGPathSegCurvetoCubicAbs>
145 SVGPathElement::CreateSVGPathSegCurvetoCubicAbs(float x, float y, float x1, float y1, float x2, float y2)
147 // Note that we swap from DOM API argument order to the argument order used
148 // in the <path> element's 'd' attribute (i.e. we put the arguments for the
149 // end point of the segment last instead of first).
150 nsRefPtr<DOMSVGPathSegCurvetoCubicAbs> pathSeg =
151 new DOMSVGPathSegCurvetoCubicAbs(x1, y1, x2, y2, x, y);
152 return pathSeg.forget();
155 already_AddRefed<DOMSVGPathSegCurvetoCubicRel>
156 SVGPathElement::CreateSVGPathSegCurvetoCubicRel(float x, float y, float x1, float y1, float x2, float y2)
158 // See comment in CreateSVGPathSegCurvetoCubicAbs
159 nsRefPtr<DOMSVGPathSegCurvetoCubicRel> pathSeg =
160 new DOMSVGPathSegCurvetoCubicRel(x1, y1, x2, y2, x, y);
161 return pathSeg.forget();
164 already_AddRefed<DOMSVGPathSegCurvetoQuadraticAbs>
165 SVGPathElement::CreateSVGPathSegCurvetoQuadraticAbs(float x, float y, float x1, float y1)
167 // See comment in CreateSVGPathSegCurvetoCubicAbs
168 nsRefPtr<DOMSVGPathSegCurvetoQuadraticAbs> pathSeg =
169 new DOMSVGPathSegCurvetoQuadraticAbs(x1, y1, x, y);
170 return pathSeg.forget();
173 already_AddRefed<DOMSVGPathSegCurvetoQuadraticRel>
174 SVGPathElement::CreateSVGPathSegCurvetoQuadraticRel(float x, float y, float x1, float y1)
176 // See comment in CreateSVGPathSegCurvetoCubicAbs
177 nsRefPtr<DOMSVGPathSegCurvetoQuadraticRel> pathSeg =
178 new DOMSVGPathSegCurvetoQuadraticRel(x1, y1, x, y);
179 return pathSeg.forget();
182 already_AddRefed<DOMSVGPathSegArcAbs>
183 SVGPathElement::CreateSVGPathSegArcAbs(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
185 // See comment in CreateSVGPathSegCurvetoCubicAbs
186 nsRefPtr<DOMSVGPathSegArcAbs> pathSeg =
187 new DOMSVGPathSegArcAbs(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
188 return pathSeg.forget();
191 already_AddRefed<DOMSVGPathSegArcRel>
192 SVGPathElement::CreateSVGPathSegArcRel(float x, float y, float r1, float r2, float angle, bool largeArcFlag, bool sweepFlag)
194 // See comment in CreateSVGPathSegCurvetoCubicAbs
195 nsRefPtr<DOMSVGPathSegArcRel> pathSeg =
196 new DOMSVGPathSegArcRel(r1, r2, angle, largeArcFlag, sweepFlag, x, y);
197 return pathSeg.forget();
200 already_AddRefed<DOMSVGPathSegLinetoHorizontalAbs>
201 SVGPathElement::CreateSVGPathSegLinetoHorizontalAbs(float x)
203 nsRefPtr<DOMSVGPathSegLinetoHorizontalAbs> pathSeg =
204 new DOMSVGPathSegLinetoHorizontalAbs(x);
205 return pathSeg.forget();
208 already_AddRefed<DOMSVGPathSegLinetoHorizontalRel>
209 SVGPathElement::CreateSVGPathSegLinetoHorizontalRel(float x)
211 nsRefPtr<DOMSVGPathSegLinetoHorizontalRel> pathSeg =
212 new DOMSVGPathSegLinetoHorizontalRel(x);
213 return pathSeg.forget();
216 already_AddRefed<DOMSVGPathSegLinetoVerticalAbs>
217 SVGPathElement::CreateSVGPathSegLinetoVerticalAbs(float y)
219 nsRefPtr<DOMSVGPathSegLinetoVerticalAbs> pathSeg =
220 new DOMSVGPathSegLinetoVerticalAbs(y);
221 return pathSeg.forget();
224 already_AddRefed<DOMSVGPathSegLinetoVerticalRel>
225 SVGPathElement::CreateSVGPathSegLinetoVerticalRel(float y)
227 nsRefPtr<DOMSVGPathSegLinetoVerticalRel> pathSeg =
228 new DOMSVGPathSegLinetoVerticalRel(y);
229 return pathSeg.forget();
232 already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothAbs>
233 SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothAbs(float x, float y, float x2, float y2)
235 // See comment in CreateSVGPathSegCurvetoCubicAbs
236 nsRefPtr<DOMSVGPathSegCurvetoCubicSmoothAbs> pathSeg =
237 new DOMSVGPathSegCurvetoCubicSmoothAbs(x2, y2, x, y);
238 return pathSeg.forget();
241 already_AddRefed<DOMSVGPathSegCurvetoCubicSmoothRel>
242 SVGPathElement::CreateSVGPathSegCurvetoCubicSmoothRel(float x, float y, float x2, float y2)
244 // See comment in CreateSVGPathSegCurvetoCubicAbs
245 nsRefPtr<DOMSVGPathSegCurvetoCubicSmoothRel> pathSeg =
246 new DOMSVGPathSegCurvetoCubicSmoothRel(x2, y2, x, y);
247 return pathSeg.forget();
250 already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothAbs>
251 SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothAbs(float x, float y)
253 nsRefPtr<DOMSVGPathSegCurvetoQuadraticSmoothAbs> pathSeg =
254 new DOMSVGPathSegCurvetoQuadraticSmoothAbs(x, y);
255 return pathSeg.forget();
258 already_AddRefed<DOMSVGPathSegCurvetoQuadraticSmoothRel>
259 SVGPathElement::CreateSVGPathSegCurvetoQuadraticSmoothRel(float x, float y)
261 nsRefPtr<DOMSVGPathSegCurvetoQuadraticSmoothRel> pathSeg =
262 new DOMSVGPathSegCurvetoQuadraticSmoothRel(x, y);
263 return pathSeg.forget();
266 already_AddRefed<DOMSVGPathSegList>
267 SVGPathElement::PathSegList()
269 return DOMSVGPathSegList::GetDOMWrapper(mD.GetBaseValKey(), this, false);
272 already_AddRefed<DOMSVGPathSegList>
273 SVGPathElement::AnimatedPathSegList()
275 return DOMSVGPathSegList::GetDOMWrapper(mD.GetAnimValKey(), this, true);
278 //----------------------------------------------------------------------
279 // nsSVGElement methods
281 /* virtual */ bool
282 SVGPathElement::HasValidDimensions() const
284 return !mD.GetAnimValue().IsEmpty();
287 nsSVGElement::NumberAttributesInfo
288 SVGPathElement::GetNumberInfo()
290 return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1);
293 //----------------------------------------------------------------------
294 // nsIContent methods
296 NS_IMETHODIMP_(bool)
297 SVGPathElement::IsAttributeMapped(const nsIAtom* name) const
299 static const MappedAttributeEntry* const map[] = {
300 sMarkersMap
303 return FindAttributeDependence(name, map) ||
304 SVGPathElementBase::IsAttributeMapped(name);
307 TemporaryRef<Path>
308 SVGPathElement::GetOrBuildPathForMeasuring()
310 return mD.GetAnimValue().BuildPathForMeasuring();
313 //----------------------------------------------------------------------
314 // nsSVGPathGeometryElement methods
316 bool
317 SVGPathElement::AttributeDefinesGeometry(const nsIAtom *aName)
319 return aName == nsGkAtoms::d ||
320 aName == nsGkAtoms::pathLength;
323 bool
324 SVGPathElement::IsMarkable()
326 return true;
329 void
330 SVGPathElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
332 mD.GetAnimValue().GetMarkerPositioningData(aMarks);
335 float
336 SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
338 NS_ABORT_IF_FALSE(aFor == eForTextPath || aFor == eForStroking,
339 "Unknown enum");
340 if (mPathLength.IsExplicitlySet()) {
341 float authorsPathLengthEstimate = mPathLength.GetAnimValue();
342 if (authorsPathLengthEstimate > 0) {
343 RefPtr<Path> path = GetOrBuildPathForMeasuring();
344 if (!path) {
345 // The path is empty or invalid so its length must be zero and
346 // we know that 0 / authorsPathLengthEstimate = 0.
347 return 0.0;
349 if (aFor == eForTextPath) {
350 // For textPath, a transform on the referenced path affects the
351 // textPath layout, so when calculating the actual path length
352 // we need to take that into account.
353 gfxMatrix matrix = PrependLocalTransformsTo(gfxMatrix());
354 if (!matrix.IsIdentity()) {
355 RefPtr<PathBuilder> builder =
356 path->TransformedCopyToBuilder(ToMatrix(matrix));
357 path = builder->Finish();
360 return path->ComputeLength() / authorsPathLengthEstimate;
363 return 1.0;
366 TemporaryRef<Path>
367 SVGPathElement::BuildPath(PathBuilder* aBuilder)
369 // The Moz2D PathBuilder that our SVGPathData will be using only cares about
370 // the fill rule. However, in order to fulfill the requirements of the SVG
371 // spec regarding zero length sub-paths when square line caps are in use,
372 // SVGPathData needs to know our stroke-linecap style and, if "square", then
373 // also our stroke width. See the comment for
374 // ApproximateZeroLengthSubpathSquareCaps for more info.
376 uint8_t strokeLineCap = NS_STYLE_STROKE_LINECAP_BUTT;
377 Float strokeWidth = 0;
379 nsRefPtr<nsStyleContext> styleContext =
380 nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, nullptr);
381 if (styleContext) {
382 const nsStyleSVG* style = styleContext->StyleSVG();
383 // Note: the path that we return may be used for hit-testing, and SVG
384 // exposes hit-testing of strokes that are not actually painted. For that
385 // reason we do not check for eStyleSVGPaintType_None or check the stroke
386 // opacity here.
387 if (style->mStrokeLinecap != NS_STYLE_STROKE_LINECAP_BUTT) {
388 strokeLineCap = style->mStrokeLinecap;
389 strokeWidth = SVGContentUtils::GetStrokeWidth(this, styleContext, nullptr);
393 return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth);
396 } // namespace dom
397 } // namespace mozilla