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/. */
6
7 #include "SVGAnimatedNumber.h"
8
9 #include "mozilla/Attributes.h"
10 #include "mozilla/SMILValue.h"
11 #include "mozilla/SVGContentUtils.h"
12 #include "nsContentUtils.h"
13 #include "SMILFloatType.h"
14 #include "SVGAttrTearoffTable.h"
15
16 using namespace mozilla::dom;
17
18 namespace mozilla {
19
20 /* Implementation */
21
22 //----------------------------------------------------------------------
23 // Helper class: AutoChangeNumberNotifier
24 // Stack-based helper class to ensure DidChangeNumber is called.
25 class MOZ_RAII AutoChangeNumberNotifier {
26 public:
AutoChangeNumberNotifier(SVGAnimatedNumber * aNumber,SVGElement * aSVGElement)27 AutoChangeNumberNotifier(SVGAnimatedNumber* aNumber, SVGElement* aSVGElement)
28 : mNumber(aNumber), mSVGElement(aSVGElement) {
29 MOZ_ASSERT(mNumber, "Expecting non-null number");
30 MOZ_ASSERT(mSVGElement, "Expecting non-null element");
31 }
32
~AutoChangeNumberNotifier()33 ~AutoChangeNumberNotifier() {
34 mSVGElement->DidChangeNumber(mNumber->mAttrEnum);
35 if (mNumber->mIsAnimated) {
36 mSVGElement->AnimationNeedsResample();
37 }
38 }
39
40 private:
41 SVGAnimatedNumber* const mNumber;
42 SVGElement* const mSVGElement;
43 };
44
45 static SVGAttrTearoffTable<SVGAnimatedNumber,
46 SVGAnimatedNumber::DOMAnimatedNumber>
47 sSVGAnimatedNumberTearoffTable;
48
GetValueFromString(const nsAString & aString,bool aPercentagesAllowed,float & aValue)49 static bool GetValueFromString(const nsAString& aString,
50 bool aPercentagesAllowed, float& aValue) {
51 bool success;
52 auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
53
54 if (!success) {
55 return false;
56 }
57
58 RangedPtr<const char16_t> iter = SVGContentUtils::GetStartRangedPtr(token);
59 const RangedPtr<const char16_t> end = SVGContentUtils::GetEndRangedPtr(token);
60
61 if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
62 return false;
63 }
64
65 if (aPercentagesAllowed) {
66 const nsAString& units = Substring(iter.get(), end.get());
67 if (units.EqualsLiteral("%")) {
68 aValue /= 100;
69 return true;
70 }
71 }
72
73 return iter == end;
74 }
75
SetBaseValueString(const nsAString & aValueAsString,SVGElement * aSVGElement)76 nsresult SVGAnimatedNumber::SetBaseValueString(const nsAString& aValueAsString,
77 SVGElement* aSVGElement) {
78 float val;
79
80 if (!GetValueFromString(aValueAsString,
81 aSVGElement->NumberAttrAllowsPercentage(mAttrEnum),
82 val)) {
83 return NS_ERROR_DOM_SYNTAX_ERR;
84 }
85
86 mBaseVal = val;
87 mIsBaseSet = true;
88 if (!mIsAnimated) {
89 mAnimVal = mBaseVal;
90 } else {
91 aSVGElement->AnimationNeedsResample();
92 }
93
94 // We don't need to call DidChange* here - we're only called by
95 // SVGElement::ParseAttribute under Element::SetAttr,
96 // which takes care of notifying.
97 return NS_OK;
98 }
99
GetBaseValueString(nsAString & aValueAsString)100 void SVGAnimatedNumber::GetBaseValueString(nsAString& aValueAsString) {
101 aValueAsString.Truncate();
102 aValueAsString.AppendFloat(mBaseVal);
103 }
104
SetBaseValue(float aValue,SVGElement * aSVGElement)105 void SVGAnimatedNumber::SetBaseValue(float aValue, SVGElement* aSVGElement) {
106 if (mIsBaseSet && aValue == mBaseVal) {
107 return;
108 }
109
110 AutoChangeNumberNotifier notifier(this, aSVGElement);
111
112 mBaseVal = aValue;
113 mIsBaseSet = true;
114 if (!mIsAnimated) {
115 mAnimVal = mBaseVal;
116 }
117 }
118
SetAnimValue(float aValue,SVGElement * aSVGElement)119 void SVGAnimatedNumber::SetAnimValue(float aValue, SVGElement* aSVGElement) {
120 if (mIsAnimated && aValue == mAnimVal) {
121 return;
122 }
123 mAnimVal = aValue;
124 mIsAnimated = true;
125 aSVGElement->DidAnimateNumber(mAttrEnum);
126 }
127
ToDOMAnimatedNumber(SVGElement * aSVGElement)128 already_AddRefed<DOMSVGAnimatedNumber> SVGAnimatedNumber::ToDOMAnimatedNumber(
129 SVGElement* aSVGElement) {
130 RefPtr<DOMAnimatedNumber> domAnimatedNumber =
131 sSVGAnimatedNumberTearoffTable.GetTearoff(this);
132 if (!domAnimatedNumber) {
133 domAnimatedNumber = new DOMAnimatedNumber(this, aSVGElement);
134 sSVGAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber);
135 }
136
137 return domAnimatedNumber.forget();
138 }
139
~DOMAnimatedNumber()140 SVGAnimatedNumber::DOMAnimatedNumber::~DOMAnimatedNumber() {
141 sSVGAnimatedNumberTearoffTable.RemoveTearoff(mVal);
142 }
143
ToSMILAttr(SVGElement * aSVGElement)144 UniquePtr<SMILAttr> SVGAnimatedNumber::ToSMILAttr(SVGElement* aSVGElement) {
145 return MakeUnique<SMILNumber>(this, aSVGElement);
146 }
147
ValueFromString(const nsAString & aStr,const mozilla::dom::SVGAnimationElement *,SMILValue & aValue,bool & aPreventCachingOfSandwich) const148 nsresult SVGAnimatedNumber::SMILNumber::ValueFromString(
149 const nsAString& aStr,
150 const mozilla::dom::SVGAnimationElement* /*aSrcElement*/, SMILValue& aValue,
151 bool& aPreventCachingOfSandwich) const {
152 float value;
153
154 if (!GetValueFromString(
155 aStr, mSVGElement->NumberAttrAllowsPercentage(mVal->mAttrEnum),
156 value)) {
157 return NS_ERROR_DOM_SYNTAX_ERR;
158 }
159
160 SMILValue val(SMILFloatType::Singleton());
161 val.mU.mDouble = value;
162 aValue = val;
163 aPreventCachingOfSandwich = false;
164
165 return NS_OK;
166 }
167
GetBaseValue() const168 SMILValue SVGAnimatedNumber::SMILNumber::GetBaseValue() const {
169 SMILValue val(SMILFloatType::Singleton());
170 val.mU.mDouble = mVal->mBaseVal;
171 return val;
172 }
173
ClearAnimValue()174 void SVGAnimatedNumber::SMILNumber::ClearAnimValue() {
175 if (mVal->mIsAnimated) {
176 mVal->mIsAnimated = false;
177 mVal->mAnimVal = mVal->mBaseVal;
178 mSVGElement->DidAnimateNumber(mVal->mAttrEnum);
179 }
180 }
181
SetAnimValue(const SMILValue & aValue)182 nsresult SVGAnimatedNumber::SMILNumber::SetAnimValue(const SMILValue& aValue) {
183 NS_ASSERTION(aValue.mType == SMILFloatType::Singleton(),
184 "Unexpected type to assign animated value");
185 if (aValue.mType == SMILFloatType::Singleton()) {
186 mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement);
187 }
188 return NS_OK;
189 }
190
191 } // namespace mozilla
192