/src/mozilla-central/dom/svg/nsSVGViewBox.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsSVGViewBox.h" |
8 | | |
9 | | #include "mozilla/Move.h" |
10 | | #include "nsCharSeparatedTokenizer.h" |
11 | | #include "nsSMILValue.h" |
12 | | #include "nsTextFormatter.h" |
13 | | #include "SVGContentUtils.h" |
14 | | #include "SVGViewBoxSMILType.h" |
15 | | |
16 | 0 | #define NUM_VIEWBOX_COMPONENTS 4 |
17 | | using namespace mozilla; |
18 | | |
19 | | /* Implementation of nsSVGViewBoxRect methods */ |
20 | | |
21 | | bool |
22 | | nsSVGViewBoxRect::operator==(const nsSVGViewBoxRect& aOther) const |
23 | 0 | { |
24 | 0 | if (&aOther == this) |
25 | 0 | return true; |
26 | 0 | |
27 | 0 | return (none && aOther.none) || |
28 | 0 | (!none && !aOther.none && |
29 | 0 | x == aOther.x && |
30 | 0 | y == aOther.y && |
31 | 0 | width == aOther.width && |
32 | 0 | height == aOther.height); |
33 | 0 | } |
34 | | |
35 | | /* static */ nsresult |
36 | | nsSVGViewBoxRect::FromString(const nsAString& aStr, nsSVGViewBoxRect *aViewBox) |
37 | 0 | { |
38 | 0 | if (aStr.EqualsLiteral("none")) { |
39 | 0 | aViewBox->none = true; |
40 | 0 | return NS_OK; |
41 | 0 | } |
42 | 0 | |
43 | 0 | nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> |
44 | 0 | tokenizer(aStr, ',', nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL); |
45 | 0 | float vals[NUM_VIEWBOX_COMPONENTS]; |
46 | 0 | uint32_t i; |
47 | 0 | for (i = 0; i < NUM_VIEWBOX_COMPONENTS && tokenizer.hasMoreTokens(); ++i) { |
48 | 0 | if (!SVGContentUtils::ParseNumber(tokenizer.nextToken(), vals[i])) { |
49 | 0 | return NS_ERROR_DOM_SYNTAX_ERR; |
50 | 0 | } |
51 | 0 | } |
52 | 0 |
|
53 | 0 | if (i != NUM_VIEWBOX_COMPONENTS || // Too few values. |
54 | 0 | tokenizer.hasMoreTokens() || // Too many values. |
55 | 0 | tokenizer.separatorAfterCurrentToken()) { // Trailing comma. |
56 | 0 | return NS_ERROR_DOM_SYNTAX_ERR; |
57 | 0 | } |
58 | 0 | |
59 | 0 | aViewBox->x = vals[0]; |
60 | 0 | aViewBox->y = vals[1]; |
61 | 0 | aViewBox->width = vals[2]; |
62 | 0 | aViewBox->height = vals[3]; |
63 | 0 | aViewBox->none = false; |
64 | 0 |
|
65 | 0 | return NS_OK; |
66 | 0 | } |
67 | | |
68 | | |
69 | | /* Cycle collection macros for nsSVGViewBox */ |
70 | | |
71 | | NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMBaseVal, mSVGElement) |
72 | | NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(nsSVGViewBox::DOMAnimVal, mSVGElement) |
73 | | |
74 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMBaseVal) |
75 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMBaseVal) |
76 | | |
77 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGViewBox::DOMAnimVal) |
78 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGViewBox::DOMAnimVal) |
79 | | |
80 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMBaseVal) |
81 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
82 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
83 | 0 | NS_INTERFACE_MAP_END |
84 | | |
85 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGViewBox::DOMAnimVal) |
86 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
87 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
88 | 0 | NS_INTERFACE_MAP_END |
89 | | |
90 | | static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMBaseVal> |
91 | | sBaseSVGViewBoxTearoffTable; |
92 | | static nsSVGAttrTearoffTable<nsSVGViewBox, nsSVGViewBox::DOMAnimVal> |
93 | | sAnimSVGViewBoxTearoffTable; |
94 | | nsSVGAttrTearoffTable<nsSVGViewBox, dom::SVGAnimatedRect> |
95 | | nsSVGViewBox::sSVGAnimatedRectTearoffTable; |
96 | | |
97 | | |
98 | | /* Implementation of nsSVGViewBox methods */ |
99 | | |
100 | | void |
101 | | nsSVGViewBox::Init() |
102 | 0 | { |
103 | 0 | mHasBaseVal = false; |
104 | 0 | // We shouldn't use mBaseVal for rendering (its usages should be guarded with |
105 | 0 | // "mHasBaseVal" checks), but just in case we do by accident, this will |
106 | 0 | // ensure that we treat it as "none" and ignore its numeric values: |
107 | 0 | mBaseVal.none = true; |
108 | 0 |
|
109 | 0 | mAnimVal = nullptr; |
110 | 0 | } |
111 | | |
112 | | bool |
113 | | nsSVGViewBox::HasRect() const |
114 | 0 | { |
115 | 0 | // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one; |
116 | 0 | // otherwise, just return false (we clearly do not have a rect). |
117 | 0 | const nsSVGViewBoxRect* rect = mAnimVal; |
118 | 0 | if (!rect) { |
119 | 0 | if (!mHasBaseVal) { |
120 | 0 | // no anim val, no base val --> no viewbox rect |
121 | 0 | return false; |
122 | 0 | } |
123 | 0 | rect = &mBaseVal; |
124 | 0 | } |
125 | 0 |
|
126 | 0 | return !rect->none && rect->width >= 0 && rect->height >= 0; |
127 | 0 | } |
128 | | |
129 | | void |
130 | | nsSVGViewBox::SetAnimValue(const nsSVGViewBoxRect& aRect, |
131 | | nsSVGElement *aSVGElement) |
132 | 0 | { |
133 | 0 | if (!mAnimVal) { |
134 | 0 | // it's okay if allocation fails - and no point in reporting that |
135 | 0 | mAnimVal = new nsSVGViewBoxRect(aRect); |
136 | 0 | } else { |
137 | 0 | if (aRect == *mAnimVal) { |
138 | 0 | return; |
139 | 0 | } |
140 | 0 | *mAnimVal = aRect; |
141 | 0 | } |
142 | 0 | aSVGElement->DidAnimateViewBox(); |
143 | 0 | } |
144 | | |
145 | | void |
146 | | nsSVGViewBox::SetBaseValue(const nsSVGViewBoxRect& aRect, |
147 | | nsSVGElement *aSVGElement) |
148 | 0 | { |
149 | 0 | if (!mHasBaseVal || mBaseVal == aRect) { |
150 | 0 | // This method is used to set a single x, y, width |
151 | 0 | // or height value. It can't create a base value |
152 | 0 | // as the other components may be undefined. We record |
153 | 0 | // the new value though, so as not to lose data. |
154 | 0 | mBaseVal = aRect; |
155 | 0 | return; |
156 | 0 | } |
157 | 0 | |
158 | 0 | nsAttrValue emptyOrOldValue = aSVGElement->WillChangeViewBox(); |
159 | 0 |
|
160 | 0 | mBaseVal = aRect; |
161 | 0 | mHasBaseVal = true; |
162 | 0 |
|
163 | 0 | aSVGElement->DidChangeViewBox(emptyOrOldValue); |
164 | 0 | if (mAnimVal) { |
165 | 0 | aSVGElement->AnimationNeedsResample(); |
166 | 0 | } |
167 | 0 | } |
168 | | |
169 | | nsresult |
170 | | nsSVGViewBox::SetBaseValueString(const nsAString& aValue, |
171 | | nsSVGElement *aSVGElement, |
172 | | bool aDoSetAttr) |
173 | 0 | { |
174 | 0 | nsSVGViewBoxRect viewBox; |
175 | 0 |
|
176 | 0 | nsresult rv = nsSVGViewBoxRect::FromString(aValue, &viewBox); |
177 | 0 | if (NS_FAILED(rv)) { |
178 | 0 | return rv; |
179 | 0 | } |
180 | 0 | // Comparison against mBaseVal is only valid if we currently have a base val. |
181 | 0 | if (mHasBaseVal && viewBox == mBaseVal) { |
182 | 0 | return NS_OK; |
183 | 0 | } |
184 | 0 | |
185 | 0 | nsAttrValue emptyOrOldValue; |
186 | 0 | if (aDoSetAttr) { |
187 | 0 | emptyOrOldValue = aSVGElement->WillChangeViewBox(); |
188 | 0 | } |
189 | 0 | mHasBaseVal = true; |
190 | 0 | mBaseVal = viewBox; |
191 | 0 |
|
192 | 0 | if (aDoSetAttr) { |
193 | 0 | aSVGElement->DidChangeViewBox(emptyOrOldValue); |
194 | 0 | } |
195 | 0 | if (mAnimVal) { |
196 | 0 | aSVGElement->AnimationNeedsResample(); |
197 | 0 | } |
198 | 0 | return NS_OK; |
199 | 0 | } |
200 | | |
201 | | void |
202 | | nsSVGViewBox::GetBaseValueString(nsAString& aValue) const |
203 | 0 | { |
204 | 0 | if (mBaseVal.none) { |
205 | 0 | aValue.AssignLiteral("none"); |
206 | 0 | return; |
207 | 0 | } |
208 | 0 | nsTextFormatter::ssprintf(aValue, |
209 | 0 | u"%g %g %g %g", |
210 | 0 | (double)mBaseVal.x, (double)mBaseVal.y, |
211 | 0 | (double)mBaseVal.width, (double)mBaseVal.height); |
212 | 0 | } |
213 | | |
214 | | |
215 | | already_AddRefed<dom::SVGAnimatedRect> |
216 | | nsSVGViewBox::ToSVGAnimatedRect(nsSVGElement* aSVGElement) |
217 | 0 | { |
218 | 0 | RefPtr<dom::SVGAnimatedRect> domAnimatedRect = |
219 | 0 | sSVGAnimatedRectTearoffTable.GetTearoff(this); |
220 | 0 | if (!domAnimatedRect) { |
221 | 0 | domAnimatedRect = new dom::SVGAnimatedRect(this, aSVGElement); |
222 | 0 | sSVGAnimatedRectTearoffTable.AddTearoff(this, domAnimatedRect); |
223 | 0 | } |
224 | 0 |
|
225 | 0 | return domAnimatedRect.forget(); |
226 | 0 | } |
227 | | |
228 | | already_AddRefed<dom::SVGIRect> |
229 | | nsSVGViewBox::ToDOMBaseVal(nsSVGElement *aSVGElement) |
230 | 0 | { |
231 | 0 | if (!mHasBaseVal || mBaseVal.none) { |
232 | 0 | return nullptr; |
233 | 0 | } |
234 | 0 | |
235 | 0 | RefPtr<DOMBaseVal> domBaseVal = |
236 | 0 | sBaseSVGViewBoxTearoffTable.GetTearoff(this); |
237 | 0 | if (!domBaseVal) { |
238 | 0 | domBaseVal = new DOMBaseVal(this, aSVGElement); |
239 | 0 | sBaseSVGViewBoxTearoffTable.AddTearoff(this, domBaseVal); |
240 | 0 | } |
241 | 0 |
|
242 | 0 | return domBaseVal.forget(); |
243 | 0 | } |
244 | | |
245 | | nsSVGViewBox::DOMBaseVal::~DOMBaseVal() |
246 | 0 | { |
247 | 0 | sBaseSVGViewBoxTearoffTable.RemoveTearoff(mVal); |
248 | 0 | } |
249 | | |
250 | | already_AddRefed<dom::SVGIRect> |
251 | | nsSVGViewBox::ToDOMAnimVal(nsSVGElement *aSVGElement) |
252 | 0 | { |
253 | 0 | if ((mAnimVal && mAnimVal->none) || |
254 | 0 | (!mAnimVal && (!mHasBaseVal || mBaseVal.none))) { |
255 | 0 | return nullptr; |
256 | 0 | } |
257 | 0 | |
258 | 0 | RefPtr<DOMAnimVal> domAnimVal = |
259 | 0 | sAnimSVGViewBoxTearoffTable.GetTearoff(this); |
260 | 0 | if (!domAnimVal) { |
261 | 0 | domAnimVal = new DOMAnimVal(this, aSVGElement); |
262 | 0 | sAnimSVGViewBoxTearoffTable.AddTearoff(this, domAnimVal); |
263 | 0 | } |
264 | 0 |
|
265 | 0 | return domAnimVal.forget(); |
266 | 0 | } |
267 | | |
268 | | nsSVGViewBox::DOMAnimVal::~DOMAnimVal() |
269 | 0 | { |
270 | 0 | sAnimSVGViewBoxTearoffTable.RemoveTearoff(mVal); |
271 | 0 | } |
272 | | |
273 | | void |
274 | | nsSVGViewBox::DOMBaseVal::SetX(float aX, ErrorResult& aRv) |
275 | 0 | { |
276 | 0 | nsSVGViewBoxRect rect = mVal->GetBaseValue(); |
277 | 0 | rect.x = aX; |
278 | 0 | mVal->SetBaseValue(rect, mSVGElement); |
279 | 0 | } |
280 | | |
281 | | void |
282 | | nsSVGViewBox::DOMBaseVal::SetY(float aY, ErrorResult& aRv) |
283 | 0 | { |
284 | 0 | nsSVGViewBoxRect rect = mVal->GetBaseValue(); |
285 | 0 | rect.y = aY; |
286 | 0 | mVal->SetBaseValue(rect, mSVGElement); |
287 | 0 | } |
288 | | |
289 | | void |
290 | | nsSVGViewBox::DOMBaseVal::SetWidth(float aWidth, ErrorResult& aRv) |
291 | 0 | { |
292 | 0 | nsSVGViewBoxRect rect = mVal->GetBaseValue(); |
293 | 0 | rect.width = aWidth; |
294 | 0 | mVal->SetBaseValue(rect, mSVGElement); |
295 | 0 | } |
296 | | |
297 | | void |
298 | | nsSVGViewBox::DOMBaseVal::SetHeight(float aHeight, ErrorResult& aRv) |
299 | 0 | { |
300 | 0 | nsSVGViewBoxRect rect = mVal->GetBaseValue(); |
301 | 0 | rect.height = aHeight; |
302 | 0 | mVal->SetBaseValue(rect, mSVGElement); |
303 | 0 | } |
304 | | |
305 | | UniquePtr<nsISMILAttr> |
306 | | nsSVGViewBox::ToSMILAttr(nsSVGElement *aSVGElement) |
307 | 0 | { |
308 | 0 | return MakeUnique<SMILViewBox>(this, aSVGElement); |
309 | 0 | } |
310 | | |
311 | | nsresult |
312 | | nsSVGViewBox::SMILViewBox |
313 | | ::ValueFromString(const nsAString& aStr, |
314 | | const dom::SVGAnimationElement* /*aSrcElement*/, |
315 | | nsSMILValue& aValue, |
316 | | bool& aPreventCachingOfSandwich) const |
317 | 0 | { |
318 | 0 | nsSVGViewBoxRect viewBox; |
319 | 0 | nsresult res = nsSVGViewBoxRect::FromString(aStr, &viewBox); |
320 | 0 | if (NS_FAILED(res)) { |
321 | 0 | return res; |
322 | 0 | } |
323 | 0 | nsSMILValue val(&SVGViewBoxSMILType::sSingleton); |
324 | 0 | *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = viewBox; |
325 | 0 | aValue = std::move(val); |
326 | 0 | aPreventCachingOfSandwich = false; |
327 | 0 |
|
328 | 0 | return NS_OK; |
329 | 0 | } |
330 | | |
331 | | nsSMILValue |
332 | | nsSVGViewBox::SMILViewBox::GetBaseValue() const |
333 | 0 | { |
334 | 0 | nsSMILValue val(&SVGViewBoxSMILType::sSingleton); |
335 | 0 | *static_cast<nsSVGViewBoxRect*>(val.mU.mPtr) = mVal->mBaseVal; |
336 | 0 | return val; |
337 | 0 | } |
338 | | |
339 | | void |
340 | | nsSVGViewBox::SMILViewBox::ClearAnimValue() |
341 | 0 | { |
342 | 0 | if (mVal->mAnimVal) { |
343 | 0 | mVal->mAnimVal = nullptr; |
344 | 0 | mSVGElement->DidAnimateViewBox(); |
345 | 0 | } |
346 | 0 | } |
347 | | |
348 | | nsresult |
349 | | nsSVGViewBox::SMILViewBox::SetAnimValue(const nsSMILValue& aValue) |
350 | 0 | { |
351 | 0 | NS_ASSERTION(aValue.mType == &SVGViewBoxSMILType::sSingleton, |
352 | 0 | "Unexpected type to assign animated value"); |
353 | 0 | if (aValue.mType == &SVGViewBoxSMILType::sSingleton) { |
354 | 0 | nsSVGViewBoxRect &vb = *static_cast<nsSVGViewBoxRect*>(aValue.mU.mPtr); |
355 | 0 | mVal->SetAnimValue(vb, mSVGElement); |
356 | 0 | } |
357 | 0 | return NS_OK; |
358 | 0 | } |