SVGAnimatedNumber.cpp (5402B)
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 "SMILFloatType.h" 10 #include "SVGAttrTearoffTable.h" 11 #include "mozilla/Attributes.h" 12 #include "mozilla/SMILValue.h" 13 #include "mozilla/SVGContentUtils.h" 14 #include "nsContentUtils.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: 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 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 constinit static SVGAttrTearoffTable<SVGAnimatedNumber, 46 SVGAnimatedNumber::DOMAnimatedNumber> 47 sSVGAnimatedNumberTearoffTable; 48 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 nsAString::const_iterator iter, end; 59 token.BeginReading(iter); 60 token.EndReading(end); 61 62 if (!SVGContentUtils::ParseNumber(iter, end, aValue)) { 63 return false; 64 } 65 66 if (aPercentagesAllowed) { 67 const nsAString& units = Substring(iter, end); 68 if (units.EqualsLiteral("%")) { 69 aValue /= 100; 70 return true; 71 } 72 } 73 74 return iter == end; 75 } 76 77 nsresult SVGAnimatedNumber::SetBaseValueString(const nsAString& aValueAsString, 78 SVGElement* aSVGElement) { 79 float val; 80 81 if (!GetValueFromString(aValueAsString, 82 aSVGElement->NumberAttrAllowsPercentage(mAttrEnum), 83 val)) { 84 return NS_ERROR_DOM_SYNTAX_ERR; 85 } 86 87 mBaseVal = val; 88 mIsBaseSet = true; 89 if (!mIsAnimated) { 90 mAnimVal = mBaseVal; 91 } else { 92 aSVGElement->AnimationNeedsResample(); 93 } 94 95 // We don't need to call DidChange* here - we're only called by 96 // SVGElement::ParseAttribute under Element::SetAttr, 97 // which takes care of notifying. 98 return NS_OK; 99 } 100 101 void SVGAnimatedNumber::GetBaseValueString(nsAString& aValueAsString) { 102 aValueAsString.Truncate(); 103 aValueAsString.AppendFloat(mBaseVal); 104 } 105 106 void SVGAnimatedNumber::SetBaseValue(float aValue, SVGElement* aSVGElement) { 107 if (mIsBaseSet && aValue == mBaseVal) { 108 return; 109 } 110 111 AutoChangeNumberNotifier notifier(this, aSVGElement); 112 113 mBaseVal = aValue; 114 mIsBaseSet = true; 115 if (!mIsAnimated) { 116 mAnimVal = mBaseVal; 117 } 118 } 119 120 void SVGAnimatedNumber::SetAnimValue(float aValue, SVGElement* aSVGElement) { 121 if (mIsAnimated && aValue == mAnimVal) { 122 return; 123 } 124 mAnimVal = aValue; 125 mIsAnimated = true; 126 aSVGElement->DidAnimateNumber(mAttrEnum); 127 } 128 129 already_AddRefed<DOMSVGAnimatedNumber> SVGAnimatedNumber::ToDOMAnimatedNumber( 130 SVGElement* aSVGElement) { 131 RefPtr<DOMAnimatedNumber> domAnimatedNumber = 132 sSVGAnimatedNumberTearoffTable.GetTearoff(this); 133 if (!domAnimatedNumber) { 134 domAnimatedNumber = new DOMAnimatedNumber(this, aSVGElement); 135 sSVGAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber); 136 } 137 138 return domAnimatedNumber.forget(); 139 } 140 141 SVGAnimatedNumber::DOMAnimatedNumber::~DOMAnimatedNumber() { 142 sSVGAnimatedNumberTearoffTable.RemoveTearoff(mVal); 143 } 144 145 UniquePtr<SMILAttr> SVGAnimatedNumber::ToSMILAttr(SVGElement* aSVGElement) { 146 return MakeUnique<SMILNumber>(this, aSVGElement); 147 } 148 149 nsresult SVGAnimatedNumber::SMILNumber::ValueFromString( 150 const nsAString& aStr, 151 const mozilla::dom::SVGAnimationElement* /*aSrcElement*/, SMILValue& aValue, 152 bool& aPreventCachingOfSandwich) const { 153 float value; 154 155 if (!GetValueFromString( 156 aStr, mSVGElement->NumberAttrAllowsPercentage(mVal->mAttrEnum), 157 value)) { 158 return NS_ERROR_DOM_SYNTAX_ERR; 159 } 160 161 SMILValue val(SMILFloatType::Singleton()); 162 val.mU.mDouble = value; 163 aValue = val; 164 165 return NS_OK; 166 } 167 168 SMILValue SVGAnimatedNumber::SMILNumber::GetBaseValue() const { 169 SMILValue val(SMILFloatType::Singleton()); 170 val.mU.mDouble = mVal->mBaseVal; 171 return val; 172 } 173 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 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