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