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