Bumping manifests a=b2g-bump
[gecko.git] / dom / svg / nsSVGAngle.cpp
blobebee0c6032115f2363842e34bedb099d1cc94400
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"
14 #include "SVGAngle.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 */
25 &nsGkAtoms::deg,
26 &nsGkAtoms::rad,
27 &nsGkAtoms::grad
30 static nsSVGAttrTearoffTable<nsSVGAngle, SVGAnimatedAngle>
31 sSVGAnimatedAngleTearoffTable;
32 static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle>
33 sBaseSVGAngleTearoffTable;
34 static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle>
35 sAnimSVGAngleTearoffTable;
37 /* Helper functions */
39 static bool
40 IsValidUnitType(uint16_t unit)
42 if (unit > SVG_ANGLETYPE_UNKNOWN &&
43 unit <= SVG_ANGLETYPE_GRAD)
44 return true;
46 return false;
49 static void
50 GetUnitString(nsAString& unit, uint16_t unitType)
52 if (IsValidUnitType(unitType)) {
53 if (unitMap[unitType]) {
54 (*unitMap[unitType])->ToString(unit);
56 return;
59 NS_NOTREACHED("Unknown unit type");
60 return;
63 static uint16_t
64 GetUnitTypeForString(const nsAString& unitStr)
66 if (unitStr.IsEmpty())
67 return SVG_ANGLETYPE_UNSPECIFIED;
69 nsIAtom *unitAtom = NS_GetStaticAtom(unitStr);
71 if (unitAtom) {
72 for (uint32_t i = 0 ; i < ArrayLength(unitMap) ; i++) {
73 if (unitMap[i] && *unitMap[i] == unitAtom) {
74 return i;
79 return SVG_ANGLETYPE_UNKNOWN;
82 static void
83 GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
85 char16_t buf[24];
86 nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
87 MOZ_UTF16("%g"),
88 (double)aValue);
89 aValueAsString.Assign(buf);
91 nsAutoString unitString;
92 GetUnitString(unitString, aUnitType);
93 aValueAsString.Append(unitString);
96 static bool
97 GetValueFromString(const nsAString& aString,
98 float& aValue,
99 uint16_t* aUnitType)
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)) {
107 return false;
110 const nsAString& units = Substring(iter.get(), end.get());
111 *aUnitType = GetUnitTypeForString(units);
112 return IsValidUnitType(*aUnitType);
115 /* static */ float
116 nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit)
118 switch (aUnit) {
119 case SVG_ANGLETYPE_UNSPECIFIED:
120 case SVG_ANGLETYPE_DEG:
121 return 1;
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;
126 default:
127 NS_NOTREACHED("Unknown unit type");
128 return 0;
132 void
133 nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
134 nsSVGElement *aSVGElement)
136 if (mBaseVal == aValue) {
137 return;
140 nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
141 mBaseVal = aValue;
142 if (!mIsAnimated) {
143 mAnimVal = mBaseVal;
145 else {
146 aSVGElement->AnimationNeedsResample();
148 aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
151 nsresult
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))
159 return NS_OK;
161 nsAttrValue emptyOrOldValue;
162 if (aSVGElement) {
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);
172 if (aSVGElement) {
173 aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
176 return NS_OK;
179 nsresult
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))
190 return NS_OK;
192 nsAttrValue emptyOrOldValue;
193 if (aSVGElement) {
194 emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
196 mBaseVal = valueInSpecifiedUnits;
197 mBaseValUnit = uint8_t(unitType);
198 if (!mIsAnimated) {
199 mAnimVal = mBaseVal;
200 mAnimValUnit = mBaseValUnit;
202 else {
203 aSVGElement->AnimationNeedsResample();
205 if (aSVGElement) {
206 aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
208 return NS_OK;
211 already_AddRefed<SVGAngle>
212 nsSVGAngle::ToDOMBaseVal(nsSVGElement *aSVGElement)
214 nsRefPtr<SVGAngle> domBaseVal =
215 sBaseSVGAngleTearoffTable.GetTearoff(this);
216 if (!domBaseVal) {
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);
229 if (!domAnimVal) {
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);
243 } else {
244 delete mVal;
248 /* Implementation */
250 nsresult
251 nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
252 nsSVGElement *aSVGElement,
253 bool aDoSetAttr)
255 float value;
256 uint16_t unitType;
258 if (!GetValueFromString(aValueAsString, value, &unitType)) {
259 return NS_ERROR_DOM_SYNTAX_ERR;
261 if (mBaseVal == value && mBaseValUnit == uint8_t(unitType)) {
262 return NS_OK;
265 nsAttrValue emptyOrOldValue;
266 if (aDoSetAttr) {
267 emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
269 mBaseVal = value;
270 mBaseValUnit = uint8_t(unitType);
271 if (!mIsAnimated) {
272 mAnimVal = mBaseVal;
273 mAnimValUnit = mBaseValUnit;
275 else {
276 aSVGElement->AnimationNeedsResample();
279 if (aDoSetAttr) {
280 aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
282 return NS_OK;
285 void
286 nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) const
288 GetValueString(aValueAsString, mBaseVal, mBaseValUnit);
291 void
292 nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) const
294 GetValueString(aValueAsString, mAnimVal, mAnimValUnit);
297 void
298 nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
299 bool aDoSetAttr)
301 if (mBaseVal == aValue * GetDegreesPerUnit(mBaseValUnit)) {
302 return;
304 nsAttrValue emptyOrOldValue;
305 if (aSVGElement && aDoSetAttr) {
306 emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
309 mBaseVal = aValue / GetDegreesPerUnit(mBaseValUnit);
310 if (!mIsAnimated) {
311 mAnimVal = mBaseVal;
313 else {
314 aSVGElement->AnimationNeedsResample();
316 if (aSVGElement && aDoSetAttr) {
317 aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
321 void
322 nsSVGAngle::SetAnimValue(float aValue, uint8_t aUnit, nsSVGElement *aSVGElement)
324 if (mIsAnimated && mAnimVal == aValue && mAnimValUnit == aUnit) {
325 return;
327 mAnimVal = aValue;
328 mAnimValUnit = aUnit;
329 mIsAnimated = true;
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);
351 nsISMILAttr*
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.");
361 return nullptr;
364 nsresult
365 nsSVGAngle::SMILOrient::ValueFromString(const nsAString& aStr,
366 const SVGAnimationElement* /*aSrcElement*/,
367 nsSMILValue& aValue,
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;
375 } else {
376 float value;
377 uint16_t unitType;
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;
385 aValue.Swap(val);
386 aPreventCachingOfSandwich = false;
388 return NS_OK;
391 nsSMILValue
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();
398 return val;
401 void
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);
413 nsresult
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);
424 } else {
425 mAngle->SetAnimValue(aValue.mU.mOrient.mAngle, aValue.mU.mOrient.mUnit, mSVGElement);
428 return NS_OK;