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/ArrayUtils.h"
8 #include "nsSVGAngle.h"
9 #include "mozilla/dom/SVGMarkerElement.h"
10 #include "nsContentUtils.h" // NS_ENSURE_FINITE
11 #include "nsSMILValue.h"
12 #include "nsSVGAttrTearoffTable.h"
13 #include "nsTextFormatter.h"
15 #include "SVGAnimatedAngle.h"
16 #include "SVGOrientSMILType.h"
18 using namespace mozilla
;
19 using namespace mozilla::dom
;
21 static nsIAtom
** const unitMap
[] =
23 nullptr, /* SVG_ANGLETYPE_UNKNOWN */
24 nullptr, /* SVG_ANGLETYPE_UNSPECIFIED */
30 static nsSVGAttrTearoffTable
<nsSVGAngle
, SVGAnimatedAngle
>
31 sSVGAnimatedAngleTearoffTable
;
32 static nsSVGAttrTearoffTable
<nsSVGAngle
, SVGAngle
>
33 sBaseSVGAngleTearoffTable
;
34 static nsSVGAttrTearoffTable
<nsSVGAngle
, SVGAngle
>
35 sAnimSVGAngleTearoffTable
;
37 /* Helper functions */
40 IsValidUnitType(uint16_t unit
)
42 if (unit
> SVG_ANGLETYPE_UNKNOWN
&&
43 unit
<= SVG_ANGLETYPE_GRAD
)
50 GetUnitString(nsAString
& unit
, uint16_t unitType
)
52 if (IsValidUnitType(unitType
)) {
53 if (unitMap
[unitType
]) {
54 (*unitMap
[unitType
])->ToString(unit
);
59 NS_NOTREACHED("Unknown unit type");
64 GetUnitTypeForString(const nsAString
& unitStr
)
66 if (unitStr
.IsEmpty())
67 return SVG_ANGLETYPE_UNSPECIFIED
;
69 nsIAtom
*unitAtom
= NS_GetStaticAtom(unitStr
);
72 for (uint32_t i
= 0 ; i
< ArrayLength(unitMap
) ; i
++) {
73 if (unitMap
[i
] && *unitMap
[i
] == unitAtom
) {
79 return SVG_ANGLETYPE_UNKNOWN
;
83 GetValueString(nsAString
&aValueAsString
, float aValue
, uint16_t aUnitType
)
86 nsTextFormatter::snprintf(buf
, sizeof(buf
)/sizeof(char16_t
),
89 aValueAsString
.Assign(buf
);
91 nsAutoString unitString
;
92 GetUnitString(unitString
, aUnitType
);
93 aValueAsString
.Append(unitString
);
97 GetValueFromString(const nsAString
& aString
,
101 RangedPtr
<const char16_t
> iter
=
102 SVGContentUtils::GetStartRangedPtr(aString
);
103 const RangedPtr
<const char16_t
> end
=
104 SVGContentUtils::GetEndRangedPtr(aString
);
106 if (!SVGContentUtils::ParseNumber(iter
, end
, aValue
)) {
110 const nsAString
& units
= Substring(iter
.get(), end
.get());
111 *aUnitType
= GetUnitTypeForString(units
);
112 return IsValidUnitType(*aUnitType
);
116 nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit
)
119 case SVG_ANGLETYPE_UNSPECIFIED
:
120 case SVG_ANGLETYPE_DEG
:
122 case SVG_ANGLETYPE_RAD
:
123 return static_cast<float>(180.0 / M_PI
);
124 case SVG_ANGLETYPE_GRAD
:
125 return 90.0f
/ 100.0f
;
127 NS_NOTREACHED("Unknown unit type");
133 nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue
,
134 nsSVGElement
*aSVGElement
)
136 if (mBaseVal
== aValue
) {
140 nsAttrValue emptyOrOldValue
= aSVGElement
->WillChangeAngle(mAttrEnum
);
146 aSVGElement
->AnimationNeedsResample();
148 aSVGElement
->DidChangeAngle(mAttrEnum
, emptyOrOldValue
);
152 nsSVGAngle::ConvertToSpecifiedUnits(uint16_t unitType
,
153 nsSVGElement
*aSVGElement
)
155 if (!IsValidUnitType(unitType
))
156 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
158 if (mBaseValUnit
== uint8_t(unitType
))
161 nsAttrValue emptyOrOldValue
;
163 emptyOrOldValue
= aSVGElement
->WillChangeAngle(mAttrEnum
);
166 float valueInUserUnits
= mBaseVal
* GetDegreesPerUnit(mBaseValUnit
);
167 mBaseValUnit
= uint8_t(unitType
);
168 // Setting aDoSetAttr to false here will ensure we don't call
169 // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
170 SetBaseValue(valueInUserUnits
, aSVGElement
, false);
173 aSVGElement
->DidChangeAngle(mAttrEnum
, emptyOrOldValue
);
180 nsSVGAngle::NewValueSpecifiedUnits(uint16_t unitType
,
181 float valueInSpecifiedUnits
,
182 nsSVGElement
*aSVGElement
)
184 NS_ENSURE_FINITE(valueInSpecifiedUnits
, NS_ERROR_ILLEGAL_VALUE
);
186 if (!IsValidUnitType(unitType
))
187 return NS_ERROR_DOM_NOT_SUPPORTED_ERR
;
189 if (mBaseVal
== valueInSpecifiedUnits
&& mBaseValUnit
== uint8_t(unitType
))
192 nsAttrValue emptyOrOldValue
;
194 emptyOrOldValue
= aSVGElement
->WillChangeAngle(mAttrEnum
);
196 mBaseVal
= valueInSpecifiedUnits
;
197 mBaseValUnit
= uint8_t(unitType
);
200 mAnimValUnit
= mBaseValUnit
;
203 aSVGElement
->AnimationNeedsResample();
206 aSVGElement
->DidChangeAngle(mAttrEnum
, emptyOrOldValue
);
211 already_AddRefed
<SVGAngle
>
212 nsSVGAngle::ToDOMBaseVal(nsSVGElement
*aSVGElement
)
214 nsRefPtr
<SVGAngle
> domBaseVal
=
215 sBaseSVGAngleTearoffTable
.GetTearoff(this);
217 domBaseVal
= new SVGAngle(this, aSVGElement
, SVGAngle::BaseValue
);
218 sBaseSVGAngleTearoffTable
.AddTearoff(this, domBaseVal
);
221 return domBaseVal
.forget();
224 already_AddRefed
<SVGAngle
>
225 nsSVGAngle::ToDOMAnimVal(nsSVGElement
*aSVGElement
)
227 nsRefPtr
<SVGAngle
> domAnimVal
=
228 sAnimSVGAngleTearoffTable
.GetTearoff(this);
230 domAnimVal
= new SVGAngle(this, aSVGElement
, SVGAngle::AnimValue
);
231 sAnimSVGAngleTearoffTable
.AddTearoff(this, domAnimVal
);
234 return domAnimVal
.forget();
237 SVGAngle::~SVGAngle()
239 if (mType
== BaseValue
) {
240 sBaseSVGAngleTearoffTable
.RemoveTearoff(mVal
);
241 } else if (mType
== AnimValue
) {
242 sAnimSVGAngleTearoffTable
.RemoveTearoff(mVal
);
251 nsSVGAngle::SetBaseValueString(const nsAString
&aValueAsString
,
252 nsSVGElement
*aSVGElement
,
258 if (!GetValueFromString(aValueAsString
, value
, &unitType
)) {
259 return NS_ERROR_DOM_SYNTAX_ERR
;
261 if (mBaseVal
== value
&& mBaseValUnit
== uint8_t(unitType
)) {
265 nsAttrValue emptyOrOldValue
;
267 emptyOrOldValue
= aSVGElement
->WillChangeAngle(mAttrEnum
);
270 mBaseValUnit
= uint8_t(unitType
);
273 mAnimValUnit
= mBaseValUnit
;
276 aSVGElement
->AnimationNeedsResample();
280 aSVGElement
->DidChangeAngle(mAttrEnum
, emptyOrOldValue
);
286 nsSVGAngle::GetBaseValueString(nsAString
& aValueAsString
) const
288 GetValueString(aValueAsString
, mBaseVal
, mBaseValUnit
);
292 nsSVGAngle::GetAnimValueString(nsAString
& aValueAsString
) const
294 GetValueString(aValueAsString
, mAnimVal
, mAnimValUnit
);
298 nsSVGAngle::SetBaseValue(float aValue
, nsSVGElement
*aSVGElement
,
301 if (mBaseVal
== aValue
* GetDegreesPerUnit(mBaseValUnit
)) {
304 nsAttrValue emptyOrOldValue
;
305 if (aSVGElement
&& aDoSetAttr
) {
306 emptyOrOldValue
= aSVGElement
->WillChangeAngle(mAttrEnum
);
309 mBaseVal
= aValue
/ GetDegreesPerUnit(mBaseValUnit
);
314 aSVGElement
->AnimationNeedsResample();
316 if (aSVGElement
&& aDoSetAttr
) {
317 aSVGElement
->DidChangeAngle(mAttrEnum
, emptyOrOldValue
);
322 nsSVGAngle::SetAnimValue(float aValue
, uint8_t aUnit
, nsSVGElement
*aSVGElement
)
324 if (mIsAnimated
&& mAnimVal
== aValue
&& mAnimValUnit
== aUnit
) {
328 mAnimValUnit
= aUnit
;
330 aSVGElement
->DidAnimateAngle(mAttrEnum
);
333 already_AddRefed
<SVGAnimatedAngle
>
334 nsSVGAngle::ToDOMAnimatedAngle(nsSVGElement
*aSVGElement
)
336 nsRefPtr
<SVGAnimatedAngle
> domAnimatedAngle
=
337 sSVGAnimatedAngleTearoffTable
.GetTearoff(this);
338 if (!domAnimatedAngle
) {
339 domAnimatedAngle
= new SVGAnimatedAngle(this, aSVGElement
);
340 sSVGAnimatedAngleTearoffTable
.AddTearoff(this, domAnimatedAngle
);
343 return domAnimatedAngle
.forget();
346 SVGAnimatedAngle::~SVGAnimatedAngle()
348 sSVGAnimatedAngleTearoffTable
.RemoveTearoff(mVal
);
352 nsSVGAngle::ToSMILAttr(nsSVGElement
*aSVGElement
)
354 if (aSVGElement
->NodeInfo()->Equals(nsGkAtoms::marker
, kNameSpaceID_SVG
)) {
355 SVGMarkerElement
*marker
= static_cast<SVGMarkerElement
*>(aSVGElement
);
356 return new SMILOrient(marker
->GetOrientType(), this, aSVGElement
);
358 // SMILOrient would not be useful for general angle attributes (also,
359 // "orient" is the only animatable <angle>-valued attribute in SVG 1.1).
360 NS_NOTREACHED("Trying to animate unknown angle attribute.");
365 nsSVGAngle::SMILOrient::ValueFromString(const nsAString
& aStr
,
366 const SVGAnimationElement
* /*aSrcElement*/,
368 bool& aPreventCachingOfSandwich
) const
370 nsSMILValue
val(&SVGOrientSMILType::sSingleton
);
371 if (aStr
.EqualsLiteral("auto")) {
372 val
.mU
.mOrient
.mOrientType
= SVG_MARKER_ORIENT_AUTO
;
373 } else if (aStr
.EqualsLiteral("auto-start-reverse")) {
374 val
.mU
.mOrient
.mOrientType
= SVG_MARKER_ORIENT_AUTO_START_REVERSE
;
378 if (!GetValueFromString(aStr
, value
, &unitType
)) {
379 return NS_ERROR_DOM_SYNTAX_ERR
;
381 val
.mU
.mOrient
.mAngle
= value
;
382 val
.mU
.mOrient
.mUnit
= unitType
;
383 val
.mU
.mOrient
.mOrientType
= SVG_MARKER_ORIENT_ANGLE
;
386 aPreventCachingOfSandwich
= false;
392 nsSVGAngle::SMILOrient::GetBaseValue() const
394 nsSMILValue
val(&SVGOrientSMILType::sSingleton
);
395 val
.mU
.mOrient
.mAngle
= mAngle
->GetBaseValInSpecifiedUnits();
396 val
.mU
.mOrient
.mUnit
= mAngle
->GetBaseValueUnit();
397 val
.mU
.mOrient
.mOrientType
= mOrientType
->GetBaseValue();
402 nsSVGAngle::SMILOrient::ClearAnimValue()
404 if (mAngle
->mIsAnimated
) {
405 mOrientType
->SetAnimValue(mOrientType
->GetBaseValue());
406 mAngle
->mIsAnimated
= false;
407 mAngle
->mAnimVal
= mAngle
->mBaseVal
;
408 mAngle
->mAnimValUnit
= mAngle
->mBaseValUnit
;
409 mSVGElement
->DidAnimateAngle(mAngle
->mAttrEnum
);
414 nsSVGAngle::SMILOrient::SetAnimValue(const nsSMILValue
& aValue
)
416 NS_ASSERTION(aValue
.mType
== &SVGOrientSMILType::sSingleton
,
417 "Unexpected type to assign animated value");
419 if (aValue
.mType
== &SVGOrientSMILType::sSingleton
) {
420 mOrientType
->SetAnimValue(aValue
.mU
.mOrient
.mOrientType
);
421 if (aValue
.mU
.mOrient
.mOrientType
== SVG_MARKER_ORIENT_AUTO
||
422 aValue
.mU
.mOrient
.mOrientType
== SVG_MARKER_ORIENT_AUTO_START_REVERSE
) {
423 mAngle
->SetAnimValue(0.0f
, SVG_ANGLETYPE_UNSPECIFIED
, mSVGElement
);
425 mAngle
->SetAnimValue(aValue
.mU
.mOrient
.mAngle
, aValue
.mU
.mOrient
.mUnit
, mSVGElement
);