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 "nsSVGAngle.h"
8 :
9 : #include "mozilla/ArrayUtils.h"
10 : #include "mozilla/dom/SVGMarkerElement.h"
11 : #include "mozilla/Move.h"
12 : #include "nsContentUtils.h" // NS_ENSURE_FINITE
13 : #include "nsSMILValue.h"
14 : #include "nsSVGAttrTearoffTable.h"
15 : #include "nsTextFormatter.h"
16 : #include "SVGAngle.h"
17 : #include "SVGAnimatedAngle.h"
18 : #include "SVGOrientSMILType.h"
19 :
20 : using namespace mozilla;
21 : using namespace mozilla::dom;
22 :
23 : static nsIAtom** const unitMap[] =
24 : {
25 : nullptr, /* SVG_ANGLETYPE_UNKNOWN */
26 : nullptr, /* SVG_ANGLETYPE_UNSPECIFIED */
27 : &nsGkAtoms::deg,
28 : &nsGkAtoms::rad,
29 : &nsGkAtoms::grad
30 : };
31 :
32 : static nsSVGAttrTearoffTable<nsSVGAngle, SVGAnimatedAngle>
33 3 : sSVGAnimatedAngleTearoffTable;
34 : static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle>
35 3 : sBaseSVGAngleTearoffTable;
36 : static nsSVGAttrTearoffTable<nsSVGAngle, SVGAngle>
37 3 : sAnimSVGAngleTearoffTable;
38 :
39 : /* Helper functions */
40 :
41 : static bool
42 0 : IsValidUnitType(uint16_t unit)
43 : {
44 0 : if (unit > SVG_ANGLETYPE_UNKNOWN &&
45 0 : unit <= SVG_ANGLETYPE_GRAD)
46 0 : return true;
47 :
48 0 : return false;
49 : }
50 :
51 : static void
52 0 : GetUnitString(nsAString& unit, uint16_t unitType)
53 : {
54 0 : if (IsValidUnitType(unitType)) {
55 0 : if (unitMap[unitType]) {
56 0 : (*unitMap[unitType])->ToString(unit);
57 : }
58 0 : return;
59 : }
60 :
61 0 : NS_NOTREACHED("Unknown unit type");
62 0 : return;
63 : }
64 :
65 : static uint16_t
66 0 : GetUnitTypeForString(const nsAString& unitStr)
67 : {
68 0 : if (unitStr.IsEmpty())
69 0 : return SVG_ANGLETYPE_UNSPECIFIED;
70 :
71 0 : nsIAtom *unitAtom = NS_GetStaticAtom(unitStr);
72 :
73 0 : if (unitAtom) {
74 0 : for (uint32_t i = 0 ; i < ArrayLength(unitMap) ; i++) {
75 0 : if (unitMap[i] && *unitMap[i] == unitAtom) {
76 0 : return i;
77 : }
78 : }
79 : }
80 :
81 0 : return SVG_ANGLETYPE_UNKNOWN;
82 : }
83 :
84 : static void
85 0 : GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
86 : {
87 : char16_t buf[24];
88 0 : nsTextFormatter::snprintf(buf, sizeof(buf)/sizeof(char16_t),
89 : u"%g",
90 0 : (double)aValue);
91 0 : aValueAsString.Assign(buf);
92 :
93 0 : nsAutoString unitString;
94 0 : GetUnitString(unitString, aUnitType);
95 0 : aValueAsString.Append(unitString);
96 0 : }
97 :
98 : static bool
99 0 : GetValueFromString(const nsAString& aString,
100 : float& aValue,
101 : uint16_t* aUnitType)
102 : {
103 : RangedPtr<const char16_t> iter =
104 0 : SVGContentUtils::GetStartRangedPtr(aString);
105 : const RangedPtr<const char16_t> end =
106 0 : SVGContentUtils::GetEndRangedPtr(aString);
107 :
108 0 : if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
109 0 : return false;
110 : }
111 :
112 0 : const nsAString& units = Substring(iter.get(), end.get());
113 0 : *aUnitType = GetUnitTypeForString(units);
114 0 : return IsValidUnitType(*aUnitType);
115 : }
116 :
117 : /* static */ float
118 0 : nsSVGAngle::GetDegreesPerUnit(uint8_t aUnit)
119 : {
120 0 : switch (aUnit) {
121 : case SVG_ANGLETYPE_UNSPECIFIED:
122 : case SVG_ANGLETYPE_DEG:
123 0 : return 1;
124 : case SVG_ANGLETYPE_RAD:
125 0 : return static_cast<float>(180.0 / M_PI);
126 : case SVG_ANGLETYPE_GRAD:
127 0 : return 90.0f / 100.0f;
128 : default:
129 0 : NS_NOTREACHED("Unknown unit type");
130 0 : return 0;
131 : }
132 : }
133 :
134 : void
135 0 : nsSVGAngle::SetBaseValueInSpecifiedUnits(float aValue,
136 : nsSVGElement *aSVGElement)
137 : {
138 0 : if (mBaseVal == aValue) {
139 0 : return;
140 : }
141 :
142 0 : nsAttrValue emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
143 0 : mBaseVal = aValue;
144 0 : if (!mIsAnimated) {
145 0 : mAnimVal = mBaseVal;
146 : }
147 : else {
148 0 : aSVGElement->AnimationNeedsResample();
149 : }
150 0 : aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
151 : }
152 :
153 : nsresult
154 0 : nsSVGAngle::ConvertToSpecifiedUnits(uint16_t unitType,
155 : nsSVGElement *aSVGElement)
156 : {
157 0 : if (!IsValidUnitType(unitType))
158 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
159 :
160 0 : if (mBaseValUnit == uint8_t(unitType))
161 0 : return NS_OK;
162 :
163 0 : nsAttrValue emptyOrOldValue;
164 0 : if (aSVGElement) {
165 0 : emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
166 : }
167 :
168 0 : float valueInUserUnits = mBaseVal * GetDegreesPerUnit(mBaseValUnit);
169 0 : mBaseValUnit = uint8_t(unitType);
170 : // Setting aDoSetAttr to false here will ensure we don't call
171 : // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
172 0 : SetBaseValue(valueInUserUnits, aSVGElement, false);
173 :
174 0 : if (aSVGElement) {
175 0 : aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
176 : }
177 :
178 0 : return NS_OK;
179 : }
180 :
181 : nsresult
182 0 : nsSVGAngle::NewValueSpecifiedUnits(uint16_t unitType,
183 : float valueInSpecifiedUnits,
184 : nsSVGElement *aSVGElement)
185 : {
186 0 : NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
187 :
188 0 : if (!IsValidUnitType(unitType))
189 0 : return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
190 :
191 0 : if (mBaseVal == valueInSpecifiedUnits && mBaseValUnit == uint8_t(unitType))
192 0 : return NS_OK;
193 :
194 0 : nsAttrValue emptyOrOldValue;
195 0 : if (aSVGElement) {
196 0 : emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
197 : }
198 0 : mBaseVal = valueInSpecifiedUnits;
199 0 : mBaseValUnit = uint8_t(unitType);
200 0 : if (!mIsAnimated) {
201 0 : mAnimVal = mBaseVal;
202 0 : mAnimValUnit = mBaseValUnit;
203 : }
204 : else {
205 0 : aSVGElement->AnimationNeedsResample();
206 : }
207 0 : if (aSVGElement) {
208 0 : aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
209 : }
210 0 : return NS_OK;
211 : }
212 :
213 : already_AddRefed<SVGAngle>
214 0 : nsSVGAngle::ToDOMBaseVal(nsSVGElement *aSVGElement)
215 : {
216 : RefPtr<SVGAngle> domBaseVal =
217 0 : sBaseSVGAngleTearoffTable.GetTearoff(this);
218 0 : if (!domBaseVal) {
219 0 : domBaseVal = new SVGAngle(this, aSVGElement, SVGAngle::BaseValue);
220 0 : sBaseSVGAngleTearoffTable.AddTearoff(this, domBaseVal);
221 : }
222 :
223 0 : return domBaseVal.forget();
224 : }
225 :
226 : already_AddRefed<SVGAngle>
227 0 : nsSVGAngle::ToDOMAnimVal(nsSVGElement *aSVGElement)
228 : {
229 : RefPtr<SVGAngle> domAnimVal =
230 0 : sAnimSVGAngleTearoffTable.GetTearoff(this);
231 0 : if (!domAnimVal) {
232 0 : domAnimVal = new SVGAngle(this, aSVGElement, SVGAngle::AnimValue);
233 0 : sAnimSVGAngleTearoffTable.AddTearoff(this, domAnimVal);
234 : }
235 :
236 0 : return domAnimVal.forget();
237 : }
238 :
239 0 : SVGAngle::~SVGAngle()
240 : {
241 0 : if (mType == BaseValue) {
242 0 : sBaseSVGAngleTearoffTable.RemoveTearoff(mVal);
243 0 : } else if (mType == AnimValue) {
244 0 : sAnimSVGAngleTearoffTable.RemoveTearoff(mVal);
245 : } else {
246 0 : delete mVal;
247 : }
248 0 : }
249 :
250 : /* Implementation */
251 :
252 : nsresult
253 0 : nsSVGAngle::SetBaseValueString(const nsAString &aValueAsString,
254 : nsSVGElement *aSVGElement,
255 : bool aDoSetAttr)
256 : {
257 : float value;
258 : uint16_t unitType;
259 :
260 0 : if (!GetValueFromString(aValueAsString, value, &unitType)) {
261 0 : return NS_ERROR_DOM_SYNTAX_ERR;
262 : }
263 0 : if (mBaseVal == value && mBaseValUnit == uint8_t(unitType)) {
264 0 : return NS_OK;
265 : }
266 :
267 0 : nsAttrValue emptyOrOldValue;
268 0 : if (aDoSetAttr) {
269 0 : emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
270 : }
271 0 : mBaseVal = value;
272 0 : mBaseValUnit = uint8_t(unitType);
273 0 : if (!mIsAnimated) {
274 0 : mAnimVal = mBaseVal;
275 0 : mAnimValUnit = mBaseValUnit;
276 : }
277 : else {
278 0 : aSVGElement->AnimationNeedsResample();
279 : }
280 :
281 0 : if (aDoSetAttr) {
282 0 : aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
283 : }
284 0 : return NS_OK;
285 : }
286 :
287 : void
288 0 : nsSVGAngle::GetBaseValueString(nsAString & aValueAsString) const
289 : {
290 0 : GetValueString(aValueAsString, mBaseVal, mBaseValUnit);
291 0 : }
292 :
293 : void
294 0 : nsSVGAngle::GetAnimValueString(nsAString & aValueAsString) const
295 : {
296 0 : GetValueString(aValueAsString, mAnimVal, mAnimValUnit);
297 0 : }
298 :
299 : void
300 0 : nsSVGAngle::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
301 : bool aDoSetAttr)
302 : {
303 0 : if (mBaseVal == aValue * GetDegreesPerUnit(mBaseValUnit)) {
304 0 : return;
305 : }
306 0 : nsAttrValue emptyOrOldValue;
307 0 : if (aSVGElement && aDoSetAttr) {
308 0 : emptyOrOldValue = aSVGElement->WillChangeAngle(mAttrEnum);
309 : }
310 :
311 0 : mBaseVal = aValue / GetDegreesPerUnit(mBaseValUnit);
312 0 : if (!mIsAnimated) {
313 0 : mAnimVal = mBaseVal;
314 : }
315 : else {
316 0 : aSVGElement->AnimationNeedsResample();
317 : }
318 0 : if (aSVGElement && aDoSetAttr) {
319 0 : aSVGElement->DidChangeAngle(mAttrEnum, emptyOrOldValue);
320 : }
321 : }
322 :
323 : void
324 0 : nsSVGAngle::SetAnimValue(float aValue, uint8_t aUnit, nsSVGElement *aSVGElement)
325 : {
326 0 : if (mIsAnimated && mAnimVal == aValue && mAnimValUnit == aUnit) {
327 0 : return;
328 : }
329 0 : mAnimVal = aValue;
330 0 : mAnimValUnit = aUnit;
331 0 : mIsAnimated = true;
332 0 : aSVGElement->DidAnimateAngle(mAttrEnum);
333 : }
334 :
335 : already_AddRefed<SVGAnimatedAngle>
336 0 : nsSVGAngle::ToDOMAnimatedAngle(nsSVGElement *aSVGElement)
337 : {
338 : RefPtr<SVGAnimatedAngle> domAnimatedAngle =
339 0 : sSVGAnimatedAngleTearoffTable.GetTearoff(this);
340 0 : if (!domAnimatedAngle) {
341 0 : domAnimatedAngle = new SVGAnimatedAngle(this, aSVGElement);
342 0 : sSVGAnimatedAngleTearoffTable.AddTearoff(this, domAnimatedAngle);
343 : }
344 :
345 0 : return domAnimatedAngle.forget();
346 : }
347 :
348 0 : SVGAnimatedAngle::~SVGAnimatedAngle()
349 : {
350 0 : sSVGAnimatedAngleTearoffTable.RemoveTearoff(mVal);
351 0 : }
352 :
353 : UniquePtr<nsISMILAttr>
354 0 : nsSVGAngle::ToSMILAttr(nsSVGElement *aSVGElement)
355 : {
356 0 : if (aSVGElement->NodeInfo()->Equals(nsGkAtoms::marker, kNameSpaceID_SVG)) {
357 0 : SVGMarkerElement *marker = static_cast<SVGMarkerElement*>(aSVGElement);
358 0 : return MakeUnique<SMILOrient>(marker->GetOrientType(), this, aSVGElement);
359 : }
360 : // SMILOrient would not be useful for general angle attributes (also,
361 : // "orient" is the only animatable <angle>-valued attribute in SVG 1.1).
362 0 : NS_NOTREACHED("Trying to animate unknown angle attribute.");
363 0 : return nullptr;
364 : }
365 :
366 : nsresult
367 0 : nsSVGAngle::SMILOrient::ValueFromString(const nsAString& aStr,
368 : const SVGAnimationElement* /*aSrcElement*/,
369 : nsSMILValue& aValue,
370 : bool& aPreventCachingOfSandwich) const
371 : {
372 0 : nsSMILValue val(&SVGOrientSMILType::sSingleton);
373 0 : if (aStr.EqualsLiteral("auto")) {
374 0 : val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO;
375 0 : } else if (aStr.EqualsLiteral("auto-start-reverse")) {
376 0 : val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_AUTO_START_REVERSE;
377 : } else {
378 : float value;
379 : uint16_t unitType;
380 0 : if (!GetValueFromString(aStr, value, &unitType)) {
381 0 : return NS_ERROR_DOM_SYNTAX_ERR;
382 : }
383 0 : val.mU.mOrient.mAngle = value;
384 0 : val.mU.mOrient.mUnit = unitType;
385 0 : val.mU.mOrient.mOrientType = SVG_MARKER_ORIENT_ANGLE;
386 : }
387 0 : aValue = Move(val);
388 0 : aPreventCachingOfSandwich = false;
389 :
390 0 : return NS_OK;
391 : }
392 :
393 : nsSMILValue
394 0 : nsSVGAngle::SMILOrient::GetBaseValue() const
395 : {
396 0 : nsSMILValue val(&SVGOrientSMILType::sSingleton);
397 0 : val.mU.mOrient.mAngle = mAngle->GetBaseValInSpecifiedUnits();
398 0 : val.mU.mOrient.mUnit = mAngle->GetBaseValueUnit();
399 0 : val.mU.mOrient.mOrientType = mOrientType->GetBaseValue();
400 0 : return val;
401 : }
402 :
403 : void
404 0 : nsSVGAngle::SMILOrient::ClearAnimValue()
405 : {
406 0 : if (mAngle->mIsAnimated) {
407 0 : mOrientType->SetAnimValue(mOrientType->GetBaseValue());
408 0 : mAngle->mIsAnimated = false;
409 0 : mAngle->mAnimVal = mAngle->mBaseVal;
410 0 : mAngle->mAnimValUnit = mAngle->mBaseValUnit;
411 0 : mSVGElement->DidAnimateAngle(mAngle->mAttrEnum);
412 : }
413 0 : }
414 :
415 : nsresult
416 0 : nsSVGAngle::SMILOrient::SetAnimValue(const nsSMILValue& aValue)
417 : {
418 0 : NS_ASSERTION(aValue.mType == &SVGOrientSMILType::sSingleton,
419 : "Unexpected type to assign animated value");
420 :
421 0 : if (aValue.mType == &SVGOrientSMILType::sSingleton) {
422 0 : mOrientType->SetAnimValue(aValue.mU.mOrient.mOrientType);
423 0 : if (aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_AUTO ||
424 0 : aValue.mU.mOrient.mOrientType == SVG_MARKER_ORIENT_AUTO_START_REVERSE) {
425 0 : mAngle->SetAnimValue(0.0f, SVG_ANGLETYPE_UNSPECIFIED, mSVGElement);
426 : } else {
427 0 : mAngle->SetAnimValue(aValue.mU.mOrient.mAngle, aValue.mU.mOrient.mUnit, mSVGElement);
428 : }
429 : }
430 0 : return NS_OK;
431 : }
|