SVGAnimatedNumberPair.cpp (7497B)
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 "SVGAnimatedNumberPair.h" 8 9 #include "SVGAttrTearoffTable.h" 10 #include "SVGNumberPairSMILType.h" 11 #include "mozilla/SMILValue.h" 12 #include "mozilla/SVGContentUtils.h" 13 #include "nsCharSeparatedTokenizer.h" 14 #include "nsContentUtils.h" 15 16 using namespace mozilla::dom; 17 18 namespace mozilla { 19 20 //---------------------------------------------------------------------- 21 // Helper class: AutoChangeNumberPairNotifier 22 // Stack-based helper class to pair calls to WillChangeNumberPair and 23 // DidChangeNumberPair. 24 class MOZ_RAII AutoChangeNumberPairNotifier { 25 public: 26 AutoChangeNumberPairNotifier(SVGAnimatedNumberPair* aNumberPair, 27 SVGElement* aSVGElement, bool aDoSetAttr = true) 28 : mNumberPair(aNumberPair), 29 mSVGElement(aSVGElement), 30 mDoSetAttr(aDoSetAttr) { 31 MOZ_ASSERT(mNumberPair, "Expecting non-null numberPair"); 32 MOZ_ASSERT(mSVGElement, "Expecting non-null element"); 33 34 if (mDoSetAttr) { 35 mSVGElement->WillChangeNumberPair(mNumberPair->mAttrEnum); 36 } 37 } 38 39 ~AutoChangeNumberPairNotifier() { 40 if (mDoSetAttr) { 41 mSVGElement->DidChangeNumberPair(mNumberPair->mAttrEnum); 42 } 43 if (mNumberPair->mIsAnimated) { 44 mSVGElement->AnimationNeedsResample(); 45 } 46 } 47 48 private: 49 SVGAnimatedNumberPair* const mNumberPair; 50 SVGElement* const mSVGElement; 51 bool mDoSetAttr; 52 }; 53 54 constinit static SVGAttrTearoffTable<SVGAnimatedNumberPair, 55 SVGAnimatedNumberPair::DOMAnimatedNumber> 56 sSVGFirstAnimatedNumberTearoffTable; 57 constinit static SVGAttrTearoffTable<SVGAnimatedNumberPair, 58 SVGAnimatedNumberPair::DOMAnimatedNumber> 59 sSVGSecondAnimatedNumberTearoffTable; 60 61 static nsresult ParseNumberOptionalNumber(const nsAString& aValue, 62 float aValues[2]) { 63 nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace, 64 nsTokenizerFlags::SeparatorOptional> 65 tokenizer(aValue, ','); 66 uint32_t i; 67 for (i = 0; i < 2 && tokenizer.hasMoreTokens(); ++i) { 68 if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), aValues[i])) { 69 return NS_ERROR_DOM_SYNTAX_ERR; 70 } 71 } 72 if (i == 1) { 73 aValues[1] = aValues[0]; 74 } 75 76 if (i == 0 || // Too few values. 77 tokenizer.hasMoreTokens() || // Too many values. 78 tokenizer.separatorAfterCurrentToken()) { // Trailing comma. 79 return NS_ERROR_DOM_SYNTAX_ERR; 80 } 81 82 return NS_OK; 83 } 84 85 nsresult SVGAnimatedNumberPair::SetBaseValueString( 86 const nsAString& aValueAsString, SVGElement* aSVGElement) { 87 float val[2]; 88 89 nsresult rv = ParseNumberOptionalNumber(aValueAsString, val); 90 if (NS_FAILED(rv)) { 91 return rv; 92 } 93 94 // We don't need to call Will/DidChange* here - we're only called by 95 // SVGElement::ParseAttribute under Element::SetAttr, 96 // which takes care of notifying. 97 AutoChangeNumberPairNotifier notifier(this, aSVGElement, false); 98 99 mBaseVal[0] = val[0]; 100 mBaseVal[1] = val[1]; 101 mIsBaseSet = true; 102 if (!mIsAnimated) { 103 mAnimVal[0] = mBaseVal[0]; 104 mAnimVal[1] = mBaseVal[1]; 105 } 106 107 return NS_OK; 108 } 109 110 void SVGAnimatedNumberPair::GetBaseValueString( 111 nsAString& aValueAsString) const { 112 aValueAsString.Truncate(); 113 aValueAsString.AppendFloat(mBaseVal[0]); 114 if (mBaseVal[0] != mBaseVal[1]) { 115 aValueAsString.AppendLiteral(", "); 116 aValueAsString.AppendFloat(mBaseVal[1]); 117 } 118 } 119 120 void SVGAnimatedNumberPair::SetBaseValue(float aValue, PairIndex aPairIndex, 121 SVGElement* aSVGElement) { 122 uint32_t index = (aPairIndex == eFirst ? 0 : 1); 123 if (mIsBaseSet && mBaseVal[index] == aValue) { 124 return; 125 } 126 127 AutoChangeNumberPairNotifier notifier(this, aSVGElement); 128 129 mBaseVal[index] = aValue; 130 mIsBaseSet = true; 131 if (!mIsAnimated) { 132 mAnimVal[index] = aValue; 133 } 134 } 135 136 void SVGAnimatedNumberPair::SetBaseValues(float aValue1, float aValue2, 137 SVGElement* aSVGElement) { 138 if (mIsBaseSet && mBaseVal[0] == aValue1 && mBaseVal[1] == aValue2) { 139 return; 140 } 141 142 AutoChangeNumberPairNotifier notifier(this, aSVGElement); 143 144 mBaseVal[0] = aValue1; 145 mBaseVal[1] = aValue2; 146 mIsBaseSet = true; 147 if (!mIsAnimated) { 148 mAnimVal[0] = aValue1; 149 mAnimVal[1] = aValue2; 150 } 151 } 152 153 void SVGAnimatedNumberPair::SetAnimValue(const float aValue[2], 154 SVGElement* aSVGElement) { 155 if (mIsAnimated && mAnimVal[0] == aValue[0] && mAnimVal[1] == aValue[1]) { 156 return; 157 } 158 mAnimVal[0] = aValue[0]; 159 mAnimVal[1] = aValue[1]; 160 mIsAnimated = true; 161 aSVGElement->DidAnimateNumberPair(mAttrEnum); 162 } 163 164 already_AddRefed<DOMSVGAnimatedNumber> 165 SVGAnimatedNumberPair::ToDOMAnimatedNumber(PairIndex aIndex, 166 SVGElement* aSVGElement) { 167 RefPtr<DOMAnimatedNumber> domAnimatedNumber = 168 aIndex == eFirst ? sSVGFirstAnimatedNumberTearoffTable.GetTearoff(this) 169 : sSVGSecondAnimatedNumberTearoffTable.GetTearoff(this); 170 if (!domAnimatedNumber) { 171 domAnimatedNumber = new DOMAnimatedNumber(this, aIndex, aSVGElement); 172 if (aIndex == eFirst) { 173 sSVGFirstAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber); 174 } else { 175 sSVGSecondAnimatedNumberTearoffTable.AddTearoff(this, domAnimatedNumber); 176 } 177 } 178 179 return domAnimatedNumber.forget(); 180 } 181 182 SVGAnimatedNumberPair::DOMAnimatedNumber::~DOMAnimatedNumber() { 183 if (mIndex == eFirst) { 184 sSVGFirstAnimatedNumberTearoffTable.RemoveTearoff(mVal); 185 } else { 186 sSVGSecondAnimatedNumberTearoffTable.RemoveTearoff(mVal); 187 } 188 } 189 190 UniquePtr<SMILAttr> SVGAnimatedNumberPair::ToSMILAttr(SVGElement* aSVGElement) { 191 return MakeUnique<SMILNumberPair>(this, aSVGElement); 192 } 193 194 nsresult SVGAnimatedNumberPair::SMILNumberPair::ValueFromString( 195 const nsAString& aStr, const dom::SVGAnimationElement* /*aSrcElement*/, 196 SMILValue& aValue, bool& aPreventCachingOfSandwich) const { 197 float values[2]; 198 199 nsresult rv = ParseNumberOptionalNumber(aStr, values); 200 if (NS_FAILED(rv)) { 201 return rv; 202 } 203 204 SMILValue val(&SVGNumberPairSMILType::sSingleton); 205 val.mU.mNumberPair[0] = values[0]; 206 val.mU.mNumberPair[1] = values[1]; 207 aValue = val; 208 209 return NS_OK; 210 } 211 212 SMILValue SVGAnimatedNumberPair::SMILNumberPair::GetBaseValue() const { 213 SMILValue val(&SVGNumberPairSMILType::sSingleton); 214 val.mU.mNumberPair[0] = mVal->mBaseVal[0]; 215 val.mU.mNumberPair[1] = mVal->mBaseVal[1]; 216 return val; 217 } 218 219 void SVGAnimatedNumberPair::SMILNumberPair::ClearAnimValue() { 220 if (mVal->mIsAnimated) { 221 mVal->mIsAnimated = false; 222 mVal->mAnimVal[0] = mVal->mBaseVal[0]; 223 mVal->mAnimVal[1] = mVal->mBaseVal[1]; 224 mSVGElement->DidAnimateNumberPair(mVal->mAttrEnum); 225 } 226 } 227 228 nsresult SVGAnimatedNumberPair::SMILNumberPair::SetAnimValue( 229 const SMILValue& aValue) { 230 NS_ASSERTION(aValue.mType == &SVGNumberPairSMILType::sSingleton, 231 "Unexpected type to assign animated value"); 232 if (aValue.mType == &SVGNumberPairSMILType::sSingleton) { 233 mVal->SetAnimValue(aValue.mU.mNumberPair, mSVGElement); 234 } 235 return NS_OK; 236 } 237 238 } // namespace mozilla