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 static SVGAttrTearoffTable<SVGAnimatedNumber,
23                            SVGAnimatedNumber::DOMAnimatedNumber>
24     sSVGAnimatedNumberTearoffTable;
25 
GetValueFromString(const nsAString & aString,bool aPercentagesAllowed,float & aValue)26 static bool GetValueFromString(const nsAString& aString,
27                                bool aPercentagesAllowed, float& aValue) {
28   bool success;
29   auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
30 
31   if (!success) {
32     return false;
33   }
34 
35   RangedPtr<const char16_t> iter = SVGContentUtils::GetStartRangedPtr(token);
36   const RangedPtr<const char16_t> end = SVGContentUtils::GetEndRangedPtr(token);
37 
38   if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
39     return false;
40   }
41 
42   if (aPercentagesAllowed) {
43     const nsAString& units = Substring(iter.get(), end.get());
44     if (units.EqualsLiteral("%")) {
45       aValue /= 100;
46       return true;
47     }
48   }
49 
50   return iter == end;
51 }
52 
SetBaseValueString(const nsAString & aValueAsString,SVGElement * aSVGElement)53 nsresult SVGAnimatedNumber::SetBaseValueString(const nsAString& aValueAsString,
54                                                SVGElement* aSVGElement) {
55   float val;
56 
57   if (!GetValueFromString(aValueAsString,
58                           aSVGElement->NumberAttrAllowsPercentage(mAttrEnum),
59                           val)) {
60     return NS_ERROR_DOM_SYNTAX_ERR;
61   }
62 
63   mBaseVal = val;
64   mIsBaseSet = true;
65   if (!mIsAnimated) {
66     mAnimVal = mBaseVal;
67   } else {
68     aSVGElement->AnimationNeedsResample();
69   }
70 
71   // We don't need to call DidChange* here - we're only called by
72   // SVGElement::ParseAttribute under Element::SetAttr,
73   // which takes care of notifying.
74   return NS_OK;
75 }
76 
GetBaseValueString(nsAString & aValueAsString)77 void SVGAnimatedNumber::GetBaseValueString(nsAString& aValueAsString) {
78   aValueAsString.Truncate();
79   aValueAsString.AppendFloat(mBaseVal);
80 }
81 
SetBaseValue(float aValue,SVGElement * aSVGElement)82 void SVGAnimatedNumber::SetBaseValue(float aValue, SVGElement* aSVGElement) {
83   if (mIsBaseSet && aValue == mBaseVal) {
84     return;
85   }
86 
87   mBaseVal = aValue;
88   mIsBaseSet = true;
89   if (!mIsAnimated) {
90     mAnimVal = mBaseVal;
91   } else {
92     aSVGElement->AnimationNeedsResample();
93   }
94   aSVGElement->DidChangeNumber(mAttrEnum);
95 }
96 
SetAnimValue(float aValue,SVGElement * aSVGElement)97 void SVGAnimatedNumber::SetAnimValue(float aValue, SVGElement* aSVGElement) {
98   if (mIsAnimated && aValue == mAnimVal) {
99     return;
100   }
101   mAnimVal = aValue;
102   mIsAnimated = true;
103   aSVGElement->DidAnimateNumber(mAttrEnum);
104 }
105 
ToDOMAnimatedNumber(SVGElement * aSVGElement)106 already_AddRefed<DOMSVGAnimatedNumber> SVGAnimatedNumber::ToDOMAnimatedNumber(
107     SVGElement* aSVGElement) {
108   RefPtr<DOMAnimatedNumber> domAnimatedNumber =
109       sSVGAnimatedNumberTearoffTable.GetTearoff(this);
110   if (!domAnimatedNumber) {
111     domAnimatedNumber = new DOMAnimatedNumber(this, aSVGElement);
112     sSVGAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber);
113   }
114 
115   return domAnimatedNumber.forget();
116 }
117 
~DOMAnimatedNumber()118 SVGAnimatedNumber::DOMAnimatedNumber::~DOMAnimatedNumber() {
119   sSVGAnimatedNumberTearoffTable.RemoveTearoff(mVal);
120 }
121 
ToSMILAttr(SVGElement * aSVGElement)122 UniquePtr<SMILAttr> SVGAnimatedNumber::ToSMILAttr(SVGElement* aSVGElement) {
123   return MakeUnique<SMILNumber>(this, aSVGElement);
124 }
125 
ValueFromString(const nsAString & aStr,const mozilla::dom::SVGAnimationElement *,SMILValue & aValue,bool & aPreventCachingOfSandwich) const126 nsresult SVGAnimatedNumber::SMILNumber::ValueFromString(
127     const nsAString& aStr,
128     const mozilla::dom::SVGAnimationElement* /*aSrcElement*/, SMILValue& aValue,
129     bool& aPreventCachingOfSandwich) const {
130   float value;
131 
132   if (!GetValueFromString(
133           aStr, mSVGElement->NumberAttrAllowsPercentage(mVal->mAttrEnum),
134           value)) {
135     return NS_ERROR_DOM_SYNTAX_ERR;
136   }
137 
138   SMILValue val(SMILFloatType::Singleton());
139   val.mU.mDouble = value;
140   aValue = val;
141   aPreventCachingOfSandwich = false;
142 
143   return NS_OK;
144 }
145 
GetBaseValue() const146 SMILValue SVGAnimatedNumber::SMILNumber::GetBaseValue() const {
147   SMILValue val(SMILFloatType::Singleton());
148   val.mU.mDouble = mVal->mBaseVal;
149   return val;
150 }
151 
ClearAnimValue()152 void SVGAnimatedNumber::SMILNumber::ClearAnimValue() {
153   if (mVal->mIsAnimated) {
154     mVal->mIsAnimated = false;
155     mVal->mAnimVal = mVal->mBaseVal;
156     mSVGElement->DidAnimateNumber(mVal->mAttrEnum);
157   }
158 }
159 
SetAnimValue(const SMILValue & aValue)160 nsresult SVGAnimatedNumber::SMILNumber::SetAnimValue(const SMILValue& aValue) {
161   NS_ASSERTION(aValue.mType == SMILFloatType::Singleton(),
162                "Unexpected type to assign animated value");
163   if (aValue.mType == SMILFloatType::Singleton()) {
164     mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement);
165   }
166   return NS_OK;
167 }
168 
169 }  // namespace mozilla
170