Coverage Report

Created: 2018-09-25 14:53

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