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 "SVGAnimatedEnumeration.h"
8
9 #include "mozilla/dom/SVGElement.h"
10 #include "mozilla/SMILValue.h"
11 #include "nsAtom.h"
12 #include "nsError.h"
13 #include "SMILEnumType.h"
14 #include "SVGAttrTearoffTable.h"
15
16 using namespace mozilla::dom;
17
18 namespace mozilla {
19
20 //----------------------------------------------------------------------
21 // Helper class: AutoChangeEnumNotifier
22 // Stack-based helper class to ensure DidChangeEnum is called.
23 class MOZ_RAII AutoChangeEnumNotifier {
24 public:
AutoChangeEnumNotifier(SVGAnimatedEnumeration * aEnum,SVGElement * aSVGElement,bool aDoSetAttr=true)25 AutoChangeEnumNotifier(SVGAnimatedEnumeration* aEnum, SVGElement* aSVGElement,
26 bool aDoSetAttr = true)
27 : mEnum(aEnum), mSVGElement(aSVGElement), mDoSetAttr(aDoSetAttr) {
28 MOZ_ASSERT(mEnum, "Expecting non-null enum");
29 MOZ_ASSERT(mSVGElement, "Expecting non-null element");
30 }
31
~AutoChangeEnumNotifier()32 ~AutoChangeEnumNotifier() {
33 if (mDoSetAttr) {
34 mSVGElement->DidChangeEnum(mEnum->mAttrEnum);
35 }
36 if (mEnum->mIsAnimated) {
37 mSVGElement->AnimationNeedsResample();
38 }
39 }
40
41 private:
42 SVGAnimatedEnumeration* const mEnum;
43 SVGElement* const mSVGElement;
44 bool mDoSetAttr;
45 };
46
47 static SVGAttrTearoffTable<SVGAnimatedEnumeration,
48 SVGAnimatedEnumeration::DOMAnimatedEnum>
49 sSVGAnimatedEnumTearoffTable;
50
GetMapping(SVGElement * aSVGElement)51 const SVGEnumMapping* SVGAnimatedEnumeration::GetMapping(
52 SVGElement* aSVGElement) {
53 SVGElement::EnumAttributesInfo info = aSVGElement->GetEnumInfo();
54
55 NS_ASSERTION(info.mCount > 0 && mAttrEnum < info.mCount,
56 "mapping request for a non-attrib enum");
57
58 return info.mInfos[mAttrEnum].mMapping;
59 }
60
SetBaseValueAtom(const nsAtom * aValue,SVGElement * aSVGElement)61 bool SVGAnimatedEnumeration::SetBaseValueAtom(const nsAtom* aValue,
62 SVGElement* aSVGElement) {
63 const SVGEnumMapping* mapping = GetMapping(aSVGElement);
64
65 while (mapping && mapping->mKey) {
66 if (aValue == mapping->mKey) {
67 if (!mIsBaseSet || mBaseVal != mapping->mVal) {
68 mIsBaseSet = true;
69 // We don't need to call DidChange* here - we're only called by
70 // SVGElement::ParseAttribute under Element::SetAttr,
71 // which takes care of notifying.
72 AutoChangeEnumNotifier notifier(this, aSVGElement, false);
73
74 mBaseVal = mapping->mVal;
75 if (!mIsAnimated) {
76 mAnimVal = mBaseVal;
77 }
78 }
79 return true;
80 }
81 mapping++;
82 }
83
84 return false;
85 }
86
GetBaseValueAtom(SVGElement * aSVGElement)87 nsAtom* SVGAnimatedEnumeration::GetBaseValueAtom(SVGElement* aSVGElement) {
88 const SVGEnumMapping* mapping = GetMapping(aSVGElement);
89
90 while (mapping && mapping->mKey) {
91 if (mBaseVal == mapping->mVal) {
92 return mapping->mKey;
93 }
94 mapping++;
95 }
96 NS_ERROR("unknown enumeration value");
97 return nsGkAtoms::_empty;
98 }
99
SetBaseValue(uint16_t aValue,SVGElement * aSVGElement,ErrorResult & aRv)100 void SVGAnimatedEnumeration::SetBaseValue(uint16_t aValue,
101 SVGElement* aSVGElement,
102 ErrorResult& aRv) {
103 const SVGEnumMapping* mapping = GetMapping(aSVGElement);
104
105 while (mapping && mapping->mKey) {
106 if (mapping->mVal == aValue) {
107 if (!mIsBaseSet || mBaseVal != uint8_t(aValue)) {
108 mIsBaseSet = true;
109 AutoChangeEnumNotifier notifier(this, aSVGElement);
110
111 mBaseVal = uint8_t(aValue);
112 if (!mIsAnimated) {
113 mAnimVal = mBaseVal;
114 }
115 }
116 return;
117 }
118 mapping++;
119 }
120 return aRv.ThrowTypeError("Invalid SVGAnimatedEnumeration base value");
121 }
122
SetAnimValue(uint16_t aValue,SVGElement * aSVGElement)123 void SVGAnimatedEnumeration::SetAnimValue(uint16_t aValue,
124 SVGElement* aSVGElement) {
125 if (mIsAnimated && aValue == mAnimVal) {
126 return;
127 }
128 mAnimVal = aValue;
129 mIsAnimated = true;
130 aSVGElement->DidAnimateEnum(mAttrEnum);
131 }
132
133 already_AddRefed<DOMSVGAnimatedEnumeration>
ToDOMAnimatedEnum(SVGElement * aSVGElement)134 SVGAnimatedEnumeration::ToDOMAnimatedEnum(SVGElement* aSVGElement) {
135 RefPtr<DOMAnimatedEnum> domAnimatedEnum =
136 sSVGAnimatedEnumTearoffTable.GetTearoff(this);
137 if (!domAnimatedEnum) {
138 domAnimatedEnum = new DOMAnimatedEnum(this, aSVGElement);
139 sSVGAnimatedEnumTearoffTable.AddTearoff(this, domAnimatedEnum);
140 }
141
142 return domAnimatedEnum.forget();
143 }
144
~DOMAnimatedEnum()145 SVGAnimatedEnumeration::DOMAnimatedEnum::~DOMAnimatedEnum() {
146 sSVGAnimatedEnumTearoffTable.RemoveTearoff(mVal);
147 }
148
ToSMILAttr(SVGElement * aSVGElement)149 UniquePtr<SMILAttr> SVGAnimatedEnumeration::ToSMILAttr(
150 SVGElement* aSVGElement) {
151 return MakeUnique<SMILEnum>(this, aSVGElement);
152 }
153
ValueFromString(const nsAString & aStr,const SVGAnimationElement *,SMILValue & aValue,bool & aPreventCachingOfSandwich) const154 nsresult SVGAnimatedEnumeration::SMILEnum::ValueFromString(
155 const nsAString& aStr, const SVGAnimationElement* /*aSrcElement*/,
156 SMILValue& aValue, bool& aPreventCachingOfSandwich) const {
157 nsAtom* valAtom = NS_GetStaticAtom(aStr);
158 if (valAtom) {
159 const SVGEnumMapping* mapping = mVal->GetMapping(mSVGElement);
160
161 while (mapping && mapping->mKey) {
162 if (valAtom == mapping->mKey) {
163 SMILValue val(SMILEnumType::Singleton());
164 val.mU.mUint = mapping->mVal;
165 aValue = val;
166 aPreventCachingOfSandwich = false;
167 return NS_OK;
168 }
169 mapping++;
170 }
171 }
172
173 // only a warning since authors may mistype attribute values
174 NS_WARNING("unknown enumeration key");
175 return NS_ERROR_FAILURE;
176 }
177
GetBaseValue() const178 SMILValue SVGAnimatedEnumeration::SMILEnum::GetBaseValue() const {
179 SMILValue val(SMILEnumType::Singleton());
180 val.mU.mUint = mVal->mBaseVal;
181 return val;
182 }
183
ClearAnimValue()184 void SVGAnimatedEnumeration::SMILEnum::ClearAnimValue() {
185 if (mVal->mIsAnimated) {
186 mVal->mIsAnimated = false;
187 mVal->mAnimVal = mVal->mBaseVal;
188 mSVGElement->DidAnimateEnum(mVal->mAttrEnum);
189 }
190 }
191
SetAnimValue(const SMILValue & aValue)192 nsresult SVGAnimatedEnumeration::SMILEnum::SetAnimValue(
193 const SMILValue& aValue) {
194 NS_ASSERTION(aValue.mType == SMILEnumType::Singleton(),
195 "Unexpected type to assign animated value");
196 if (aValue.mType == SMILEnumType::Singleton()) {
197 MOZ_ASSERT(aValue.mU.mUint <= USHRT_MAX,
198 "Very large enumerated value - too big for uint16_t");
199 mVal->SetAnimValue(uint16_t(aValue.mU.mUint), mSVGElement);
200 }
201 return NS_OK;
202 }
203
204 } // namespace mozilla
205