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/. */
7 #include "SVGAnimatedNumberPair.h"
9 #include "nsCharSeparatedTokenizer.h"
10 #include "SVGAttrTearoffTable.h"
11 #include "SVGNumberPairSMILType.h"
12 #include "mozilla/SMILValue.h"
13 #include "mozilla/SVGContentUtils.h"
14 #include "nsContentUtils.h"
16 using namespace mozilla::dom
;
20 //----------------------------------------------------------------------
21 // Helper class: AutoChangeNumberPairNotifier
22 // Stack-based helper class to pair calls to WillChangeNumberPair and
23 // DidChangeNumberPair.
24 class MOZ_RAII AutoChangeNumberPairNotifier
{
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");
36 mSVGElement
->WillChangeNumberPair(mNumberPair
->mAttrEnum
);
40 ~AutoChangeNumberPairNotifier() {
42 mSVGElement
->DidChangeNumberPair(mNumberPair
->mAttrEnum
,
45 if (mNumberPair
->mIsAnimated
) {
46 mSVGElement
->AnimationNeedsResample();
51 SVGAnimatedNumberPair
* const mNumberPair
;
52 SVGElement
* const mSVGElement
;
53 nsAttrValue mEmptyOrOldValue
;
57 MOZ_CONSTINIT
static SVGAttrTearoffTable
<
58 SVGAnimatedNumberPair
, SVGAnimatedNumberPair::DOMAnimatedNumber
>
59 sSVGFirstAnimatedNumberTearoffTable
;
60 MOZ_CONSTINIT
static SVGAttrTearoffTable
<
61 SVGAnimatedNumberPair
, SVGAnimatedNumberPair::DOMAnimatedNumber
>
62 sSVGSecondAnimatedNumberTearoffTable
;
64 static nsresult
ParseNumberOptionalNumber(const nsAString
& aValue
,
66 nsCharSeparatedTokenizerTemplate
<nsContentUtils::IsHTMLWhitespace
,
67 nsTokenizerFlags::SeparatorOptional
>
68 tokenizer(aValue
, ',');
70 for (i
= 0; i
< 2 && tokenizer
.hasMoreTokens(); ++i
) {
71 if (!SVGContentUtils::ParseNumber(tokenizer
.nextToken(), aValues
[i
])) {
72 return NS_ERROR_DOM_SYNTAX_ERR
;
76 aValues
[1] = aValues
[0];
79 if (i
== 0 || // Too few values.
80 tokenizer
.hasMoreTokens() || // Too many values.
81 tokenizer
.separatorAfterCurrentToken()) { // Trailing comma.
82 return NS_ERROR_DOM_SYNTAX_ERR
;
88 nsresult
SVGAnimatedNumberPair::SetBaseValueString(
89 const nsAString
& aValueAsString
, SVGElement
* aSVGElement
) {
92 nsresult rv
= ParseNumberOptionalNumber(aValueAsString
, val
);
97 // We don't need to call Will/DidChange* here - we're only called by
98 // SVGElement::ParseAttribute under Element::SetAttr,
99 // which takes care of notifying.
100 AutoChangeNumberPairNotifier
notifier(this, aSVGElement
, false);
102 mBaseVal
[0] = val
[0];
103 mBaseVal
[1] = val
[1];
106 mAnimVal
[0] = mBaseVal
[0];
107 mAnimVal
[1] = mBaseVal
[1];
113 void SVGAnimatedNumberPair::GetBaseValueString(
114 nsAString
& aValueAsString
) const {
115 aValueAsString
.Truncate();
116 aValueAsString
.AppendFloat(mBaseVal
[0]);
117 if (mBaseVal
[0] != mBaseVal
[1]) {
118 aValueAsString
.AppendLiteral(", ");
119 aValueAsString
.AppendFloat(mBaseVal
[1]);
123 void SVGAnimatedNumberPair::SetBaseValue(float aValue
, PairIndex aPairIndex
,
124 SVGElement
* aSVGElement
) {
125 uint32_t index
= (aPairIndex
== eFirst
? 0 : 1);
126 if (mIsBaseSet
&& mBaseVal
[index
] == aValue
) {
130 AutoChangeNumberPairNotifier
notifier(this, aSVGElement
);
132 mBaseVal
[index
] = aValue
;
135 mAnimVal
[index
] = aValue
;
139 void SVGAnimatedNumberPair::SetBaseValues(float aValue1
, float aValue2
,
140 SVGElement
* aSVGElement
) {
141 if (mIsBaseSet
&& mBaseVal
[0] == aValue1
&& mBaseVal
[1] == aValue2
) {
145 AutoChangeNumberPairNotifier
notifier(this, aSVGElement
);
147 mBaseVal
[0] = aValue1
;
148 mBaseVal
[1] = aValue2
;
151 mAnimVal
[0] = aValue1
;
152 mAnimVal
[1] = aValue2
;
156 void SVGAnimatedNumberPair::SetAnimValue(const float aValue
[2],
157 SVGElement
* aSVGElement
) {
158 if (mIsAnimated
&& mAnimVal
[0] == aValue
[0] && mAnimVal
[1] == aValue
[1]) {
161 mAnimVal
[0] = aValue
[0];
162 mAnimVal
[1] = aValue
[1];
164 aSVGElement
->DidAnimateNumberPair(mAttrEnum
);
167 already_AddRefed
<DOMSVGAnimatedNumber
>
168 SVGAnimatedNumberPair::ToDOMAnimatedNumber(PairIndex aIndex
,
169 SVGElement
* aSVGElement
) {
170 RefPtr
<DOMAnimatedNumber
> domAnimatedNumber
=
171 aIndex
== eFirst
? sSVGFirstAnimatedNumberTearoffTable
.GetTearoff(this)
172 : sSVGSecondAnimatedNumberTearoffTable
.GetTearoff(this);
173 if (!domAnimatedNumber
) {
174 domAnimatedNumber
= new DOMAnimatedNumber(this, aIndex
, aSVGElement
);
175 if (aIndex
== eFirst
) {
176 sSVGFirstAnimatedNumberTearoffTable
.AddTearoff(this, domAnimatedNumber
);
178 sSVGSecondAnimatedNumberTearoffTable
.AddTearoff(this, domAnimatedNumber
);
182 return domAnimatedNumber
.forget();
185 SVGAnimatedNumberPair::DOMAnimatedNumber::~DOMAnimatedNumber() {
186 if (mIndex
== eFirst
) {
187 sSVGFirstAnimatedNumberTearoffTable
.RemoveTearoff(mVal
);
189 sSVGSecondAnimatedNumberTearoffTable
.RemoveTearoff(mVal
);
193 UniquePtr
<SMILAttr
> SVGAnimatedNumberPair::ToSMILAttr(SVGElement
* aSVGElement
) {
194 return MakeUnique
<SMILNumberPair
>(this, aSVGElement
);
197 nsresult
SVGAnimatedNumberPair::SMILNumberPair::ValueFromString(
198 const nsAString
& aStr
, const dom::SVGAnimationElement
* /*aSrcElement*/,
199 SMILValue
& aValue
, bool& aPreventCachingOfSandwich
) const {
202 nsresult rv
= ParseNumberOptionalNumber(aStr
, values
);
207 SMILValue
val(&SVGNumberPairSMILType::sSingleton
);
208 val
.mU
.mNumberPair
[0] = values
[0];
209 val
.mU
.mNumberPair
[1] = values
[1];
215 SMILValue
SVGAnimatedNumberPair::SMILNumberPair::GetBaseValue() const {
216 SMILValue
val(&SVGNumberPairSMILType::sSingleton
);
217 val
.mU
.mNumberPair
[0] = mVal
->mBaseVal
[0];
218 val
.mU
.mNumberPair
[1] = mVal
->mBaseVal
[1];
222 void SVGAnimatedNumberPair::SMILNumberPair::ClearAnimValue() {
223 if (mVal
->mIsAnimated
) {
224 mVal
->mIsAnimated
= false;
225 mVal
->mAnimVal
[0] = mVal
->mBaseVal
[0];
226 mVal
->mAnimVal
[1] = mVal
->mBaseVal
[1];
227 mSVGElement
->DidAnimateNumberPair(mVal
->mAttrEnum
);
231 nsresult
SVGAnimatedNumberPair::SMILNumberPair::SetAnimValue(
232 const SMILValue
& aValue
) {
233 NS_ASSERTION(aValue
.mType
== &SVGNumberPairSMILType::sSingleton
,
234 "Unexpected type to assign animated value");
235 if (aValue
.mType
== &SVGNumberPairSMILType::sSingleton
) {
236 mVal
->SetAnimValue(aValue
.mU
.mNumberPair
, mSVGElement
);
241 } // namespace mozilla