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