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