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 "mozilla/ArrayUtils.h"
8 
9 #include "SVGAnimatedPreserveAspectRatio.h"
10 #include "mozilla/dom/SVGAnimatedPreserveAspectRatioBinding.h"
11 #include "nsSMILValue.h"
12 #include "nsSVGAttrTearoffTable.h"
13 #include "nsWhitespaceTokenizer.h"
14 #include "SMILEnumType.h"
15 #include "SVGContentUtils.h"
16 
17 using namespace mozilla;
18 using namespace mozilla::dom;
19 
20 ////////////////////////////////////////////////////////////////////////
21 // SVGAnimatedPreserveAspectRatio class
NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedPreserveAspectRatio,mSVGElement)22 NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMSVGAnimatedPreserveAspectRatio, mSVGElement)
23 
24 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGAnimatedPreserveAspectRatio)
25 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGAnimatedPreserveAspectRatio)
26 
27 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGAnimatedPreserveAspectRatio)
28   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
29   NS_INTERFACE_MAP_ENTRY(nsISupports)
30 NS_INTERFACE_MAP_END
31 
32 JSObject*
33 DOMSVGAnimatedPreserveAspectRatio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
34 {
35   return SVGAnimatedPreserveAspectRatioBinding::Wrap(aCx, this, aGivenProto);
36 }
37 
38 /* Implementation */
39 
40 static const char *sAlignStrings[] =
41   { "none", "xMinYMin", "xMidYMin", "xMaxYMin", "xMinYMid", "xMidYMid",
42     "xMaxYMid", "xMinYMax", "xMidYMax", "xMaxYMax" };
43 
44 static const char *sMeetOrSliceStrings[] = { "meet", "slice" };
45 
46 static nsSVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio, DOMSVGAnimatedPreserveAspectRatio>
47   sSVGAnimatedPAspectRatioTearoffTable;
48 static nsSVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio, DOMSVGPreserveAspectRatio>
49   sBaseSVGPAspectRatioTearoffTable;
50 static nsSVGAttrTearoffTable<SVGAnimatedPreserveAspectRatio, DOMSVGPreserveAspectRatio>
51   sAnimSVGPAspectRatioTearoffTable;
52 
53 static uint16_t
GetAlignForString(const nsAString & aAlignString)54 GetAlignForString(const nsAString &aAlignString)
55 {
56   for (uint32_t i = 0 ; i < ArrayLength(sAlignStrings) ; i++) {
57     if (aAlignString.EqualsASCII(sAlignStrings[i])) {
58       return (i + SVG_ALIGN_MIN_VALID);
59     }
60   }
61 
62   return SVG_PRESERVEASPECTRATIO_UNKNOWN;
63 }
64 
65 static void
GetAlignString(nsAString & aAlignString,uint16_t aAlign)66 GetAlignString(nsAString& aAlignString, uint16_t aAlign)
67 {
68   NS_ASSERTION(
69     aAlign >= SVG_ALIGN_MIN_VALID && aAlign <= SVG_ALIGN_MAX_VALID,
70     "Unknown align");
71 
72   aAlignString.AssignASCII(
73     sAlignStrings[aAlign - SVG_ALIGN_MIN_VALID]);
74 }
75 
76 static uint16_t
GetMeetOrSliceForString(const nsAString & aMeetOrSlice)77 GetMeetOrSliceForString(const nsAString &aMeetOrSlice)
78 {
79   for (uint32_t i = 0 ; i < ArrayLength(sMeetOrSliceStrings) ; i++) {
80     if (aMeetOrSlice.EqualsASCII(sMeetOrSliceStrings[i])) {
81       return (i + SVG_MEETORSLICE_MIN_VALID);
82     }
83   }
84 
85   return SVG_MEETORSLICE_UNKNOWN;
86 }
87 
88 static void
GetMeetOrSliceString(nsAString & aMeetOrSliceString,uint16_t aMeetOrSlice)89 GetMeetOrSliceString(nsAString& aMeetOrSliceString, uint16_t aMeetOrSlice)
90 {
91   NS_ASSERTION(
92     aMeetOrSlice >= SVG_MEETORSLICE_MIN_VALID &&
93     aMeetOrSlice <= SVG_MEETORSLICE_MAX_VALID,
94     "Unknown meetOrSlice");
95 
96   aMeetOrSliceString.AssignASCII(
97     sMeetOrSliceStrings[aMeetOrSlice - SVG_MEETORSLICE_MIN_VALID]);
98 }
99 
100 already_AddRefed<DOMSVGPreserveAspectRatio>
BaseVal()101 DOMSVGAnimatedPreserveAspectRatio::BaseVal()
102 {
103   RefPtr<DOMSVGPreserveAspectRatio> domBaseVal =
104     sBaseSVGPAspectRatioTearoffTable.GetTearoff(mVal);
105   if (!domBaseVal) {
106     domBaseVal = new DOMSVGPreserveAspectRatio(mVal, mSVGElement, true);
107     sBaseSVGPAspectRatioTearoffTable.AddTearoff(mVal, domBaseVal);
108   }
109 
110   return domBaseVal.forget();
111 }
112 
~DOMSVGPreserveAspectRatio()113 DOMSVGPreserveAspectRatio::~DOMSVGPreserveAspectRatio()
114 {
115   if (mIsBaseValue) {
116     sBaseSVGPAspectRatioTearoffTable.RemoveTearoff(mVal);
117   } else {
118     sAnimSVGPAspectRatioTearoffTable.RemoveTearoff(mVal);
119   }
120 }
121 
122 already_AddRefed<DOMSVGPreserveAspectRatio>
AnimVal()123 DOMSVGAnimatedPreserveAspectRatio::AnimVal()
124 {
125   RefPtr<DOMSVGPreserveAspectRatio> domAnimVal =
126     sAnimSVGPAspectRatioTearoffTable.GetTearoff(mVal);
127   if (!domAnimVal) {
128     domAnimVal = new DOMSVGPreserveAspectRatio(mVal, mSVGElement, false);
129     sAnimSVGPAspectRatioTearoffTable.AddTearoff(mVal, domAnimVal);
130   }
131 
132   return domAnimVal.forget();
133 }
134 
135 static nsresult
ToPreserveAspectRatio(const nsAString & aString,SVGPreserveAspectRatio * aValue)136 ToPreserveAspectRatio(const nsAString &aString,
137                       SVGPreserveAspectRatio *aValue)
138 {
139   nsWhitespaceTokenizerTemplate<IsSVGWhitespace> tokenizer(aString);
140   if (tokenizer.whitespaceBeforeFirstToken() ||
141       !tokenizer.hasMoreTokens()) {
142     return NS_ERROR_DOM_SYNTAX_ERR;
143   }
144   const nsAString &token = tokenizer.nextToken();
145 
146   nsresult rv;
147   SVGPreserveAspectRatio val;
148 
149   rv = val.SetAlign(GetAlignForString(token));
150 
151   if (NS_FAILED(rv)) {
152     return NS_ERROR_DOM_SYNTAX_ERR;
153   }
154 
155   if (tokenizer.hasMoreTokens()) {
156     rv = val.SetMeetOrSlice(GetMeetOrSliceForString(tokenizer.nextToken()));
157     if (NS_FAILED(rv)) {
158       return NS_ERROR_DOM_SYNTAX_ERR;
159     }
160   } else {
161     val.SetMeetOrSlice(SVG_MEETORSLICE_MEET);
162   }
163 
164   if (tokenizer.whitespaceAfterCurrentToken()) {
165     return NS_ERROR_DOM_SYNTAX_ERR;
166   }
167 
168   *aValue = val;
169   return NS_OK;
170 }
171 
172 nsresult
SetBaseValueString(const nsAString & aValueAsString,nsSVGElement * aSVGElement,bool aDoSetAttr)173 SVGAnimatedPreserveAspectRatio::SetBaseValueString(
174   const nsAString &aValueAsString, nsSVGElement *aSVGElement, bool aDoSetAttr)
175 {
176   SVGPreserveAspectRatio val;
177   nsresult res = ToPreserveAspectRatio(aValueAsString, &val);
178   if (NS_FAILED(res)) {
179     return res;
180   }
181 
182   nsAttrValue emptyOrOldValue;
183   if (aDoSetAttr) {
184     emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
185   }
186 
187   mBaseVal = val;
188   mIsBaseSet = true;
189 
190   if (!mIsAnimated) {
191     mAnimVal = mBaseVal;
192   }
193   if (aDoSetAttr) {
194     aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
195   }
196   if (mIsAnimated) {
197     aSVGElement->AnimationNeedsResample();
198   }
199   return NS_OK;
200 }
201 
202 void
GetBaseValueString(nsAString & aValueAsString) const203 SVGAnimatedPreserveAspectRatio::GetBaseValueString(
204   nsAString& aValueAsString) const
205 {
206   nsAutoString tmpString;
207 
208   aValueAsString.Truncate();
209 
210   GetAlignString(tmpString, mBaseVal.mAlign);
211   aValueAsString.Append(tmpString);
212 
213   if (mBaseVal.mAlign != uint8_t(SVG_PRESERVEASPECTRATIO_NONE)) {
214 
215     aValueAsString.Append(' ');
216     GetMeetOrSliceString(tmpString, mBaseVal.mMeetOrSlice);
217     aValueAsString.Append(tmpString);
218   }
219 }
220 
221 void
SetBaseValue(const SVGPreserveAspectRatio & aValue,nsSVGElement * aSVGElement)222 SVGAnimatedPreserveAspectRatio::SetBaseValue(const SVGPreserveAspectRatio &aValue,
223                                              nsSVGElement *aSVGElement)
224 {
225   if (mIsBaseSet && mBaseVal == aValue) {
226     return;
227   }
228 
229   nsAttrValue emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
230   mBaseVal = aValue;
231   mIsBaseSet = true;
232 
233   if (!mIsAnimated) {
234     mAnimVal = mBaseVal;
235   }
236   aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
237   if (mIsAnimated) {
238     aSVGElement->AnimationNeedsResample();
239   }
240 }
241 
242 static uint64_t
PackPreserveAspectRatio(const SVGPreserveAspectRatio & par)243 PackPreserveAspectRatio(const SVGPreserveAspectRatio& par)
244 {
245   // All preserveAspectRatio values are enum values (do not interpolate), so we
246   // can safely collate them and treat them as a single enum as for SMIL.
247   uint64_t packed = 0;
248   packed |= uint64_t(par.GetAlign()) << 8;
249   packed |= uint64_t(par.GetMeetOrSlice());
250   return packed;
251 }
252 
253 void
SetAnimValue(uint64_t aPackedValue,nsSVGElement * aSVGElement)254 SVGAnimatedPreserveAspectRatio::SetAnimValue(uint64_t aPackedValue,
255                                              nsSVGElement *aSVGElement)
256 {
257   if (mIsAnimated && PackPreserveAspectRatio(mAnimVal) == aPackedValue) {
258     return;
259   }
260   mAnimVal.SetAlign(uint16_t((aPackedValue & 0xff00) >> 8));
261   mAnimVal.SetMeetOrSlice(uint16_t(aPackedValue & 0xff));
262   mIsAnimated = true;
263   aSVGElement->DidAnimatePreserveAspectRatio();
264 }
265 
266 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
ToDOMAnimatedPreserveAspectRatio(nsSVGElement * aSVGElement)267 SVGAnimatedPreserveAspectRatio::ToDOMAnimatedPreserveAspectRatio(
268   nsSVGElement *aSVGElement)
269 {
270   RefPtr<DOMSVGAnimatedPreserveAspectRatio> domAnimatedPAspectRatio =
271     sSVGAnimatedPAspectRatioTearoffTable.GetTearoff(this);
272   if (!domAnimatedPAspectRatio) {
273     domAnimatedPAspectRatio = new DOMSVGAnimatedPreserveAspectRatio(this, aSVGElement);
274     sSVGAnimatedPAspectRatioTearoffTable.AddTearoff(this, domAnimatedPAspectRatio);
275   }
276   return domAnimatedPAspectRatio.forget();
277 }
278 
~DOMSVGAnimatedPreserveAspectRatio()279 DOMSVGAnimatedPreserveAspectRatio::~DOMSVGAnimatedPreserveAspectRatio()
280 {
281   sSVGAnimatedPAspectRatioTearoffTable.RemoveTearoff(mVal);
282 }
283 
284 nsISMILAttr*
ToSMILAttr(nsSVGElement * aSVGElement)285 SVGAnimatedPreserveAspectRatio::ToSMILAttr(nsSVGElement *aSVGElement)
286 {
287   return new SMILPreserveAspectRatio(this, aSVGElement);
288 }
289 
290 // typedef for inner class, to make function signatures shorter below:
291 typedef SVGAnimatedPreserveAspectRatio::SMILPreserveAspectRatio
292   SMILPreserveAspectRatio;
293 
294 nsresult
ValueFromString(const nsAString & aStr,const SVGAnimationElement *,nsSMILValue & aValue,bool & aPreventCachingOfSandwich) const295 SMILPreserveAspectRatio::ValueFromString(const nsAString& aStr,
296                                          const SVGAnimationElement* /*aSrcElement*/,
297                                          nsSMILValue& aValue,
298                                          bool& aPreventCachingOfSandwich) const
299 {
300   SVGPreserveAspectRatio par;
301   nsresult res = ToPreserveAspectRatio(aStr, &par);
302   NS_ENSURE_SUCCESS(res, res);
303 
304   nsSMILValue val(SMILEnumType::Singleton());
305   val.mU.mUint = PackPreserveAspectRatio(par);
306   aValue = val;
307   aPreventCachingOfSandwich = false;
308   return NS_OK;
309 }
310 
311 nsSMILValue
GetBaseValue() const312 SMILPreserveAspectRatio::GetBaseValue() const
313 {
314   nsSMILValue val(SMILEnumType::Singleton());
315   val.mU.mUint = PackPreserveAspectRatio(mVal->GetBaseValue());
316   return val;
317 }
318 
319 void
ClearAnimValue()320 SMILPreserveAspectRatio::ClearAnimValue()
321 {
322   if (mVal->mIsAnimated) {
323     mVal->mIsAnimated = false;
324     mVal->mAnimVal = mVal->mBaseVal;
325     mSVGElement->DidAnimatePreserveAspectRatio();
326   }
327 }
328 
329 nsresult
SetAnimValue(const nsSMILValue & aValue)330 SMILPreserveAspectRatio::SetAnimValue(const nsSMILValue& aValue)
331 {
332   NS_ASSERTION(aValue.mType == SMILEnumType::Singleton(),
333                "Unexpected type to assign animated value");
334   if (aValue.mType == SMILEnumType::Singleton()) {
335     mVal->SetAnimValue(aValue.mU.mUint, mSVGElement);
336   }
337   return NS_OK;
338 }
339