Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/nsSVGLength2.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 "mozilla/ArrayUtils.h"
8
9
#include "nsSVGLength2.h"
10
#include "mozilla/dom/SVGAnimatedLength.h"
11
#include "mozilla/dom/SVGViewportElement.h"
12
#include "nsContentUtils.h" // NS_ENSURE_FINITE
13
#include "nsIFrame.h"
14
#include "nsSMILFloatType.h"
15
#include "nsSMILValue.h"
16
#include "nsSVGAttrTearoffTable.h"
17
#include "nsSVGIntegrationUtils.h"
18
#include "nsTextFormatter.h"
19
#include "DOMSVGLength.h"
20
#include "LayoutLogging.h"
21
22
using namespace mozilla;
23
using namespace mozilla::dom;
24
25
static nsStaticAtom** const unitMap[] =
26
{
27
  nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
28
  nullptr, /* SVG_LENGTHTYPE_NUMBER */
29
  &nsGkAtoms::percentage,
30
  &nsGkAtoms::em,
31
  &nsGkAtoms::ex,
32
  &nsGkAtoms::px,
33
  &nsGkAtoms::cm,
34
  &nsGkAtoms::mm,
35
  &nsGkAtoms::in,
36
  &nsGkAtoms::pt,
37
  &nsGkAtoms::pc
38
};
39
40
static nsSVGAttrTearoffTable<nsSVGLength2, SVGAnimatedLength>
41
  sSVGAnimatedLengthTearoffTable;
42
43
/* Helper functions */
44
45
static bool
46
IsValidUnitType(uint16_t unit)
47
0
{
48
0
  if (unit > SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN &&
49
0
      unit <= SVGLength_Binding::SVG_LENGTHTYPE_PC)
50
0
    return true;
51
0
52
0
  return false;
53
0
}
54
55
static void
56
GetUnitString(nsAString& unit, uint16_t unitType)
57
0
{
58
0
  if (IsValidUnitType(unitType)) {
59
0
    if (unitMap[unitType]) {
60
0
      (*unitMap[unitType])->ToString(unit);
61
0
    }
62
0
    return;
63
0
  }
64
0
65
0
  MOZ_ASSERT_UNREACHABLE("Unknown unit type");
66
0
}
67
68
static uint16_t
69
GetUnitTypeForString(const nsAString& unitStr)
70
0
{
71
0
  if (unitStr.IsEmpty())
72
0
    return SVGLength_Binding::SVG_LENGTHTYPE_NUMBER;
73
0
74
0
  nsAtom *unitAtom = NS_GetStaticAtom(unitStr);
75
0
  if (unitAtom) {
76
0
    for (uint32_t i = 0 ; i < ArrayLength(unitMap) ; i++) {
77
0
      if (unitMap[i] && *unitMap[i] == unitAtom) {
78
0
        return i;
79
0
      }
80
0
    }
81
0
  }
82
0
83
0
  return SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN;
84
0
}
85
86
static void
87
GetValueString(nsAString &aValueAsString, float aValue, uint16_t aUnitType)
88
0
{
89
0
  nsTextFormatter::ssprintf(aValueAsString, u"%g", (double)aValue);
90
0
91
0
  nsAutoString unitString;
92
0
  GetUnitString(unitString, aUnitType);
93
0
  aValueAsString.Append(unitString);
94
0
}
95
96
static bool
97
GetValueFromString(const nsAString& aString,
98
                   float& aValue,
99
                   uint16_t* aUnitType)
100
0
{
101
0
  RangedPtr<const char16_t> iter =
102
0
    SVGContentUtils::GetStartRangedPtr(aString);
103
0
  const RangedPtr<const char16_t> end =
104
0
    SVGContentUtils::GetEndRangedPtr(aString);
105
0
106
0
  if (!SVGContentUtils::ParseNumber(iter, end, aValue)) {
107
0
    return false;
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
FixAxisLength(float aLength)
116
0
{
117
0
  if (aLength == 0.0f) {
118
0
    LAYOUT_WARNING("zero axis length");
119
0
    return 1e-20f;
120
0
  }
121
0
  return aLength;
122
0
}
123
124
SVGElementMetrics::SVGElementMetrics(nsSVGElement* aSVGElement,
125
                                     SVGViewportElement* aCtx)
126
  : mSVGElement(aSVGElement)
127
  , mCtx(aCtx)
128
0
{
129
0
}
130
131
float
132
SVGElementMetrics::GetEmLength() const
133
0
{
134
0
  return SVGContentUtils::GetFontSize(mSVGElement);
135
0
}
136
137
float
138
SVGElementMetrics::GetExLength() const
139
0
{
140
0
  return SVGContentUtils::GetFontXHeight(mSVGElement);
141
0
}
142
143
float
144
SVGElementMetrics::GetAxisLength(uint8_t aCtxType) const
145
0
{
146
0
  if (!EnsureCtx()) {
147
0
    return 1;
148
0
  }
149
0
150
0
  return FixAxisLength(mCtx->GetLength(aCtxType));
151
0
}
152
153
bool
154
SVGElementMetrics::EnsureCtx() const
155
0
{
156
0
  if (!mCtx && mSVGElement) {
157
0
    mCtx = mSVGElement->GetCtx();
158
0
    if (!mCtx && mSVGElement->IsSVGElement(nsGkAtoms::svg)) {
159
0
      // mSVGElement must be the outer svg element
160
0
      mCtx = static_cast<SVGViewportElement*>(mSVGElement);
161
0
    }
162
0
  }
163
0
  return mCtx != nullptr;
164
0
}
165
166
NonSVGFrameUserSpaceMetrics::NonSVGFrameUserSpaceMetrics(nsIFrame* aFrame)
167
  : mFrame(aFrame)
168
0
{
169
0
}
170
171
float
172
NonSVGFrameUserSpaceMetrics::GetEmLength() const
173
0
{
174
0
  return SVGContentUtils::GetFontSize(mFrame);
175
0
}
176
177
float
178
NonSVGFrameUserSpaceMetrics::GetExLength() const
179
0
{
180
0
  return SVGContentUtils::GetFontXHeight(mFrame);
181
0
}
182
183
gfx::Size
184
NonSVGFrameUserSpaceMetrics::GetSize() const
185
0
{
186
0
  return nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(mFrame);
187
0
}
188
189
float
190
UserSpaceMetricsWithSize::GetAxisLength(uint8_t aCtxType) const
191
0
{
192
0
  gfx::Size size = GetSize();
193
0
  float length;
194
0
  switch (aCtxType) {
195
0
  case SVGContentUtils::X:
196
0
    length = size.width;
197
0
    break;
198
0
  case SVGContentUtils::Y:
199
0
    length = size.height;
200
0
    break;
201
0
  case SVGContentUtils::XY:
202
0
    length = SVGContentUtils::ComputeNormalizedHypotenuse(size.width, size.height);
203
0
    break;
204
0
  default:
205
0
    MOZ_ASSERT_UNREACHABLE("Unknown axis type");
206
0
    length = 1;
207
0
    break;
208
0
  }
209
0
  return FixAxisLength(length);
210
0
}
211
212
float
213
nsSVGLength2::GetPixelsPerUnit(nsSVGElement* aSVGElement,
214
                               uint8_t aUnitType) const
215
0
{
216
0
  return GetPixelsPerUnit(SVGElementMetrics(aSVGElement), aUnitType);
217
0
}
218
219
float
220
nsSVGLength2::GetPixelsPerUnit(SVGViewportElement* aCtx,
221
                               uint8_t aUnitType) const
222
0
{
223
0
  return GetPixelsPerUnit(SVGElementMetrics(aCtx, aCtx), aUnitType);
224
0
}
225
226
float
227
nsSVGLength2::GetPixelsPerUnit(nsIFrame* aFrame,
228
                               uint8_t aUnitType) const
229
0
{
230
0
  nsIContent* content = aFrame->GetContent();
231
0
  if (content->IsSVGElement()) {
232
0
    return GetPixelsPerUnit(
233
0
             SVGElementMetrics(static_cast<nsSVGElement*>(content)), aUnitType);
234
0
  }
235
0
  return GetPixelsPerUnit(NonSVGFrameUserSpaceMetrics(aFrame), aUnitType);
236
0
}
237
238
// See https://www.w3.org/TR/css-values-3/#absolute-lengths
239
static const float DPI = 96.0f;
240
241
float
242
nsSVGLength2::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
243
                               uint8_t aUnitType) const
244
0
{
245
0
  switch (aUnitType) {
246
0
  case SVGLength_Binding::SVG_LENGTHTYPE_NUMBER:
247
0
  case SVGLength_Binding::SVG_LENGTHTYPE_PX:
248
0
    return 1;
249
0
  case SVGLength_Binding::SVG_LENGTHTYPE_MM:
250
0
    return DPI / MM_PER_INCH_FLOAT;
251
0
  case SVGLength_Binding::SVG_LENGTHTYPE_CM:
252
0
    return 10.0f * DPI / MM_PER_INCH_FLOAT;
253
0
  case SVGLength_Binding::SVG_LENGTHTYPE_IN:
254
0
    return DPI;
255
0
  case SVGLength_Binding::SVG_LENGTHTYPE_PT:
256
0
    return DPI / POINTS_PER_INCH_FLOAT;
257
0
  case SVGLength_Binding::SVG_LENGTHTYPE_PC:
258
0
    return 12.0f * DPI / POINTS_PER_INCH_FLOAT;
259
0
  case SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE:
260
0
    return aMetrics.GetAxisLength(mCtxType) / 100.0f;
261
0
  case SVGLength_Binding::SVG_LENGTHTYPE_EMS:
262
0
    return aMetrics.GetEmLength();
263
0
  case SVGLength_Binding::SVG_LENGTHTYPE_EXS:
264
0
    return aMetrics.GetExLength();
265
0
  default:
266
0
    MOZ_ASSERT_UNREACHABLE("Unknown unit type");
267
0
    return 0;
268
0
  }
269
0
}
270
271
void
272
nsSVGLength2::SetBaseValueInSpecifiedUnits(float aValue,
273
                                           nsSVGElement *aSVGElement,
274
                                           bool aDoSetAttr)
275
0
{
276
0
  if (mIsBaseSet && mBaseVal == aValue) {
277
0
    return;
278
0
  }
279
0
280
0
  nsAttrValue emptyOrOldValue;
281
0
  if (aDoSetAttr) {
282
0
    emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
283
0
  }
284
0
  mBaseVal = aValue;
285
0
  mIsBaseSet = true;
286
0
  if (!mIsAnimated) {
287
0
    mAnimVal = mBaseVal;
288
0
  }
289
0
  else {
290
0
    aSVGElement->AnimationNeedsResample();
291
0
  }
292
0
  if (aDoSetAttr) {
293
0
    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
294
0
  }
295
0
}
296
297
nsresult
298
nsSVGLength2::ConvertToSpecifiedUnits(uint16_t unitType,
299
                                      nsSVGElement *aSVGElement)
300
0
{
301
0
  if (!IsValidUnitType(unitType))
302
0
    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
303
0
304
0
  if (mIsBaseSet && mSpecifiedUnitType == uint8_t(unitType))
305
0
    return NS_OK;
306
0
307
0
  float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, unitType);
308
0
  if (pixelsPerUnit == 0.0f) {
309
0
    return NS_ERROR_ILLEGAL_VALUE;
310
0
  }
311
0
312
0
  float valueInUserUnits =
313
0
    mBaseVal * GetPixelsPerUnit(aSVGElement, mSpecifiedUnitType);
314
0
  float valueInSpecifiedUnits = valueInUserUnits / pixelsPerUnit;
315
0
316
0
  if (!IsFinite(valueInSpecifiedUnits)) {
317
0
    return NS_ERROR_ILLEGAL_VALUE;
318
0
  }
319
0
320
0
  // Even though we're not changing the visual effect this length will have
321
0
  // on the document, we still need to send out notifications in case we have
322
0
  // mutation listeners, since the actual string value of the attribute will
323
0
  // change.
324
0
  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
325
0
326
0
  mSpecifiedUnitType = uint8_t(unitType);
327
0
  // Setting aDoSetAttr to false here will ensure we don't call
328
0
  // Will/DidChangeAngle a second time (and dispatch duplicate notifications).
329
0
  SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement, false);
330
0
331
0
  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
332
0
333
0
  return NS_OK;
334
0
}
335
336
nsresult
337
nsSVGLength2::NewValueSpecifiedUnits(uint16_t unitType,
338
                                     float valueInSpecifiedUnits,
339
                                     nsSVGElement *aSVGElement)
340
0
{
341
0
  NS_ENSURE_FINITE(valueInSpecifiedUnits, NS_ERROR_ILLEGAL_VALUE);
342
0
343
0
  if (!IsValidUnitType(unitType))
344
0
    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
345
0
346
0
  if (mIsBaseSet && mBaseVal == valueInSpecifiedUnits &&
347
0
      mSpecifiedUnitType == uint8_t(unitType)) {
348
0
    return NS_OK;
349
0
  }
350
0
351
0
  nsAttrValue emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
352
0
  mBaseVal = valueInSpecifiedUnits;
353
0
  mIsBaseSet = true;
354
0
  mSpecifiedUnitType = uint8_t(unitType);
355
0
  if (!mIsAnimated) {
356
0
    mAnimVal = mBaseVal;
357
0
  }
358
0
  else {
359
0
    aSVGElement->AnimationNeedsResample();
360
0
  }
361
0
  aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
362
0
  return NS_OK;
363
0
}
364
365
nsresult
366
nsSVGLength2::ToDOMBaseVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement)
367
0
{
368
0
  RefPtr<DOMSVGLength> domBaseVal =
369
0
    DOMSVGLength::GetTearOff(this, aSVGElement, false);
370
0
371
0
  domBaseVal.forget(aResult);
372
0
  return NS_OK;
373
0
}
374
375
nsresult
376
nsSVGLength2::ToDOMAnimVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement)
377
0
{
378
0
  RefPtr<DOMSVGLength> domAnimVal =
379
0
    DOMSVGLength::GetTearOff(this, aSVGElement, true);
380
0
381
0
  domAnimVal.forget(aResult);
382
0
  return NS_OK;
383
0
}
384
385
/* Implementation */
386
387
nsresult
388
nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
389
                                 nsSVGElement *aSVGElement,
390
                                 bool aDoSetAttr)
391
0
{
392
0
  float value;
393
0
  uint16_t unitType;
394
0
395
0
  if (!GetValueFromString(aValueAsString, value, &unitType)) {
396
0
    return NS_ERROR_DOM_SYNTAX_ERR;
397
0
  }
398
0
399
0
  if (mIsBaseSet && mBaseVal == float(value) &&
400
0
      mSpecifiedUnitType == uint8_t(unitType)) {
401
0
    return NS_OK;
402
0
  }
403
0
404
0
  nsAttrValue emptyOrOldValue;
405
0
  if (aDoSetAttr) {
406
0
    emptyOrOldValue = aSVGElement->WillChangeLength(mAttrEnum);
407
0
  }
408
0
  mBaseVal = value;
409
0
  mIsBaseSet = true;
410
0
  mSpecifiedUnitType = uint8_t(unitType);
411
0
  if (!mIsAnimated) {
412
0
    mAnimVal = mBaseVal;
413
0
  }
414
0
  else {
415
0
    aSVGElement->AnimationNeedsResample();
416
0
  }
417
0
418
0
  if (aDoSetAttr) {
419
0
    aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
420
0
  }
421
0
  return NS_OK;
422
0
}
423
424
void
425
nsSVGLength2::GetBaseValueString(nsAString & aValueAsString) const
426
0
{
427
0
  GetValueString(aValueAsString, mBaseVal, mSpecifiedUnitType);
428
0
}
429
430
void
431
nsSVGLength2::GetAnimValueString(nsAString & aValueAsString) const
432
0
{
433
0
  GetValueString(aValueAsString, mAnimVal, mSpecifiedUnitType);
434
0
}
435
436
nsresult
437
nsSVGLength2::SetBaseValue(float aValue, nsSVGElement *aSVGElement,
438
                           bool aDoSetAttr)
439
0
{
440
0
  float pixelsPerUnit = GetPixelsPerUnit(aSVGElement, mSpecifiedUnitType);
441
0
  if (pixelsPerUnit == 0.0f) {
442
0
    return NS_ERROR_ILLEGAL_VALUE;
443
0
  }
444
0
445
0
  float valueInSpecifiedUnits = aValue / pixelsPerUnit;
446
0
  if (!IsFinite(valueInSpecifiedUnits)) {
447
0
    return NS_ERROR_ILLEGAL_VALUE;
448
0
  }
449
0
450
0
  SetBaseValueInSpecifiedUnits(valueInSpecifiedUnits,
451
0
                               aSVGElement, aDoSetAttr);
452
0
  return NS_OK;
453
0
}
454
455
void
456
nsSVGLength2::SetAnimValueInSpecifiedUnits(float aValue,
457
                                           nsSVGElement* aSVGElement)
458
0
{
459
0
  if (mAnimVal == aValue && mIsAnimated) {
460
0
    return;
461
0
  }
462
0
  mAnimVal = aValue;
463
0
  mIsAnimated = true;
464
0
  aSVGElement->DidAnimateLength(mAttrEnum);
465
0
}
466
467
nsresult
468
nsSVGLength2::SetAnimValue(float aValue, nsSVGElement *aSVGElement)
469
0
{
470
0
  float valueInSpecifiedUnits = aValue /
471
0
          GetPixelsPerUnit(aSVGElement, mSpecifiedUnitType);
472
0
473
0
  if (IsFinite(valueInSpecifiedUnits)) {
474
0
    SetAnimValueInSpecifiedUnits(valueInSpecifiedUnits, aSVGElement);
475
0
    return NS_OK;
476
0
  }
477
0
  return NS_ERROR_ILLEGAL_VALUE;
478
0
}
479
480
already_AddRefed<SVGAnimatedLength>
481
nsSVGLength2::ToDOMAnimatedLength(nsSVGElement* aSVGElement)
482
0
{
483
0
  RefPtr<SVGAnimatedLength> svgAnimatedLength =
484
0
    sSVGAnimatedLengthTearoffTable.GetTearoff(this);
485
0
  if (!svgAnimatedLength) {
486
0
    svgAnimatedLength = new SVGAnimatedLength(this, aSVGElement);
487
0
    sSVGAnimatedLengthTearoffTable.AddTearoff(this, svgAnimatedLength);
488
0
  }
489
0
490
0
  return svgAnimatedLength.forget();
491
0
}
492
493
SVGAnimatedLength::~SVGAnimatedLength()
494
0
{
495
0
  sSVGAnimatedLengthTearoffTable.RemoveTearoff(mVal);
496
0
}
497
498
UniquePtr<nsISMILAttr>
499
nsSVGLength2::ToSMILAttr(nsSVGElement *aSVGElement)
500
0
{
501
0
  return MakeUnique<SMILLength>(this, aSVGElement);
502
0
}
503
504
nsresult
505
nsSVGLength2::SMILLength::ValueFromString(const nsAString& aStr,
506
                                 const SVGAnimationElement* /*aSrcElement*/,
507
                                 nsSMILValue& aValue,
508
                                 bool& aPreventCachingOfSandwich) const
509
0
{
510
0
  float value;
511
0
  uint16_t unitType;
512
0
513
0
  if (!GetValueFromString(aStr, value, &unitType)) {
514
0
    return NS_ERROR_DOM_SYNTAX_ERR;
515
0
  }
516
0
517
0
  nsSMILValue val(nsSMILFloatType::Singleton());
518
0
  val.mU.mDouble = value * mVal->GetPixelsPerUnit(mSVGElement, unitType);
519
0
  aValue = val;
520
0
  aPreventCachingOfSandwich =
521
0
              (unitType == SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE ||
522
0
               unitType == SVGLength_Binding::SVG_LENGTHTYPE_EMS ||
523
0
               unitType == SVGLength_Binding::SVG_LENGTHTYPE_EXS);
524
0
525
0
  return NS_OK;
526
0
}
527
528
nsSMILValue
529
nsSVGLength2::SMILLength::GetBaseValue() const
530
0
{
531
0
  nsSMILValue val(nsSMILFloatType::Singleton());
532
0
  val.mU.mDouble = mVal->GetBaseValue(mSVGElement);
533
0
  return val;
534
0
}
535
536
void
537
nsSVGLength2::SMILLength::ClearAnimValue()
538
0
{
539
0
  if (mVal->mIsAnimated) {
540
0
    mVal->mIsAnimated = false;
541
0
    mVal->mAnimVal = mVal->mBaseVal;
542
0
    mSVGElement->DidAnimateLength(mVal->mAttrEnum);
543
0
  }
544
0
}
545
546
nsresult
547
nsSVGLength2::SMILLength::SetAnimValue(const nsSMILValue& aValue)
548
0
{
549
0
  NS_ASSERTION(aValue.mType == nsSMILFloatType::Singleton(),
550
0
    "Unexpected type to assign animated value");
551
0
  if (aValue.mType == nsSMILFloatType::Singleton()) {
552
0
    return mVal->SetAnimValue(float(aValue.mU.mDouble), mSVGElement);
553
0
  }
554
0
  return NS_OK;
555
0
}