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