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