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