Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/svg/SVGLength.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 "SVGLength.h"
10
#include "nsSVGElement.h"
11
#include "mozilla/dom/SVGSVGElement.h"
12
#include "nsTextFormatter.h"
13
#include "SVGContentUtils.h"
14
#include <limits>
15
#include <algorithm>
16
17
namespace mozilla {
18
19
using namespace mozilla;
20
21
// Declare some helpers defined below:
22
static void GetUnitString(nsAString& unit, uint16_t unitType);
23
static uint16_t GetUnitTypeForString(const nsAString& unitStr);
24
25
void
26
SVGLength::GetValueAsString(nsAString &aValue) const
27
0
{
28
0
  nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
29
0
30
0
  nsAutoString unitString;
31
0
  GetUnitString(unitString, mUnit);
32
0
  aValue.Append(unitString);
33
0
}
34
35
bool
36
SVGLength::SetValueFromString(const nsAString &aString)
37
0
{
38
0
  RangedPtr<const char16_t> iter =
39
0
    SVGContentUtils::GetStartRangedPtr(aString);
40
0
  const RangedPtr<const char16_t> end =
41
0
    SVGContentUtils::GetEndRangedPtr(aString);
42
0
43
0
  float value;
44
0
45
0
  if (!SVGContentUtils::ParseNumber(iter, end, value)) {
46
0
    return false;
47
0
  }
48
0
49
0
  const nsAString& units = Substring(iter.get(), end.get());
50
0
  uint16_t unitType = GetUnitTypeForString(units);
51
0
  if (!IsValidUnitType(unitType)) {
52
0
    return false;
53
0
  }
54
0
  mValue = value;
55
0
  mUnit = uint8_t(unitType);
56
0
  return true;
57
0
}
58
59
inline static bool
60
IsAbsoluteUnit(uint8_t aUnit)
61
0
{
62
0
  return aUnit >= SVGLength_Binding::SVG_LENGTHTYPE_CM &&
63
0
         aUnit <= SVGLength_Binding::SVG_LENGTHTYPE_PC;
64
0
}
65
66
/**
67
 * Helper to convert between different CSS absolute units without the need for
68
 * an element, which provides more flexibility at the DOM level (and without
69
 * the need for an intermediary conversion to user units, which avoids
70
 * unnecessary overhead and rounding error).
71
 *
72
 * Example usage: to find out how many centimeters there are per inch:
73
 *
74
 *   GetAbsUnitsPerAbsUnit(SVGLength_Binding::SVG_LENGTHTYPE_CM,
75
 *                         SVGLength_Binding::SVG_LENGTHTYPE_IN)
76
 */
77
inline static float GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit)
78
0
{
79
0
  MOZ_ASSERT(IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
80
0
  MOZ_ASSERT(IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
81
0
82
0
  float CSSAbsoluteUnitConversionFactors[5][5] = { // columns: cm, mm, in, pt, pc
83
0
    // cm per...:
84
0
    { 1.0f, 0.1f, 2.54f, 0.035277777777777778f, 0.42333333333333333f },
85
0
    // mm per...:
86
0
    { 10.0f, 1.0f, 25.4f, 0.35277777777777778f, 4.2333333333333333f },
87
0
    // in per...:
88
0
    { 0.39370078740157481f, 0.039370078740157481f, 1.0f, 0.013888888888888889f, 0.16666666666666667f },
89
0
    // pt per...:
90
0
    { 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f },
91
0
    // pc per...:
92
0
    { 2.3622047244094489f, 0.23622047244094489f, 6.0f, 0.083333333333333333f, 1.0f }
93
0
  };
94
0
95
0
  // First absolute unit is SVG_LENGTHTYPE_CM = 6
96
0
  return CSSAbsoluteUnitConversionFactors[aUnits - 6][aPerUnit - 6];
97
0
}
98
99
float
100
SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
101
                                   const nsSVGElement *aElement,
102
                                   uint8_t aAxis) const
103
0
{
104
0
  if (aUnit == mUnit) {
105
0
    return mValue;
106
0
  }
107
0
  if ((aUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER &&
108
0
       mUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX) ||
109
0
      (aUnit == SVGLength_Binding::SVG_LENGTHTYPE_PX &&
110
0
       mUnit == SVGLength_Binding::SVG_LENGTHTYPE_NUMBER)) {
111
0
    return mValue;
112
0
  }
113
0
  if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
114
0
    return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
115
0
  }
116
0
117
0
  // Otherwise we do a two step conversion via user units. This can only
118
0
  // succeed if aElement is non-null (although that's not sufficient to
119
0
  // guarantee success).
120
0
121
0
  float userUnitsPerCurrentUnit = GetUserUnitsPerUnit(aElement, aAxis);
122
0
  float userUnitsPerNewUnit =
123
0
    SVGLength(0.0f, aUnit).GetUserUnitsPerUnit(aElement, aAxis);
124
0
125
0
  NS_ASSERTION(userUnitsPerCurrentUnit >= 0 ||
126
0
               !IsFinite(userUnitsPerCurrentUnit),
127
0
               "bad userUnitsPerCurrentUnit");
128
0
  NS_ASSERTION(userUnitsPerNewUnit >= 0 ||
129
0
               !IsFinite(userUnitsPerNewUnit),
130
0
               "bad userUnitsPerNewUnit");
131
0
132
0
  float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
133
0
134
0
  // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
135
0
  // be zero.
136
0
  if (IsFinite(value)) {
137
0
    return value;
138
0
  }
139
0
  return std::numeric_limits<float>::quiet_NaN();
140
0
}
141
142
0
#define INCHES_PER_MM_FLOAT float(0.0393700787)
143
0
#define INCHES_PER_CM_FLOAT float(0.393700787)
144
145
float
146
SVGLength::GetUserUnitsPerUnit(const nsSVGElement *aElement, uint8_t aAxis) const
147
0
{
148
0
  switch (mUnit) {
149
0
    case SVGLength_Binding::SVG_LENGTHTYPE_NUMBER:
150
0
    case SVGLength_Binding::SVG_LENGTHTYPE_PX:
151
0
      return 1.0f;
152
0
    case SVGLength_Binding::SVG_LENGTHTYPE_MM:
153
0
      return INCHES_PER_MM_FLOAT * GetUserUnitsPerInch();
154
0
    case SVGLength_Binding::SVG_LENGTHTYPE_CM:
155
0
      return INCHES_PER_CM_FLOAT * GetUserUnitsPerInch();
156
0
    case SVGLength_Binding::SVG_LENGTHTYPE_IN:
157
0
      return GetUserUnitsPerInch();
158
0
    case SVGLength_Binding::SVG_LENGTHTYPE_PT:
159
0
      return (1.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
160
0
    case SVGLength_Binding::SVG_LENGTHTYPE_PC:
161
0
      return (12.0f/POINTS_PER_INCH_FLOAT) * GetUserUnitsPerInch();
162
0
    case SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE:
163
0
      return GetUserUnitsPerPercent(aElement, aAxis);
164
0
    case SVGLength_Binding::SVG_LENGTHTYPE_EMS:
165
0
      return SVGContentUtils::GetFontSize(const_cast<nsSVGElement*>(aElement));
166
0
    case SVGLength_Binding::SVG_LENGTHTYPE_EXS:
167
0
      return SVGContentUtils::GetFontXHeight(const_cast<nsSVGElement*>(aElement));
168
0
    default:
169
0
      MOZ_ASSERT_UNREACHABLE("Unknown unit type");
170
0
      return std::numeric_limits<float>::quiet_NaN();
171
0
  }
172
0
}
173
174
/* static */ float
175
SVGLength::GetUserUnitsPerPercent(const nsSVGElement *aElement, uint8_t aAxis)
176
0
{
177
0
  if (aElement) {
178
0
    dom::SVGViewportElement *viewportElement = aElement->GetCtx();
179
0
    if (viewportElement) {
180
0
      return std::max(viewportElement->GetLength(aAxis) / 100.0f, 0.0f);
181
0
    }
182
0
  }
183
0
  return std::numeric_limits<float>::quiet_NaN();
184
0
}
185
186
// Helpers:
187
188
// These items must be at the same index as the SVGLength constants!
189
static nsStaticAtom** const unitMap[] =
190
{
191
  nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
192
  nullptr, /* SVG_LENGTHTYPE_NUMBER */
193
  &nsGkAtoms::percentage,
194
  &nsGkAtoms::em,
195
  &nsGkAtoms::ex,
196
  &nsGkAtoms::px,
197
  &nsGkAtoms::cm,
198
  &nsGkAtoms::mm,
199
  &nsGkAtoms::in,
200
  &nsGkAtoms::pt,
201
  &nsGkAtoms::pc
202
};
203
204
static void
205
GetUnitString(nsAString& unit, uint16_t unitType)
206
0
{
207
0
  if (SVGLength::IsValidUnitType(unitType)) {
208
0
    if (unitMap[unitType]) {
209
0
      (*unitMap[unitType])->ToString(unit);
210
0
    }
211
0
    return;
212
0
  }
213
0
  MOZ_ASSERT_UNREACHABLE("Unknown unit type! Someone's using an SVGLength "
214
0
                         "with an invalid unit?");
215
0
}
216
217
static uint16_t
218
GetUnitTypeForString(const nsAString& unitStr)
219
0
{
220
0
  if (unitStr.IsEmpty())
221
0
    return SVGLength_Binding::SVG_LENGTHTYPE_NUMBER;
222
0
223
0
  nsAtom* unitAtom = NS_GetStaticAtom(unitStr);
224
0
225
0
  if (unitAtom) {
226
0
    for (uint32_t i = 1 ; i < ArrayLength(unitMap) ; i++) {
227
0
      if (unitMap[i] && *unitMap[i] == unitAtom) {
228
0
        return i;
229
0
      }
230
0
    }
231
0
  }
232
0
  return SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN;
233
0
}
234
235
} // namespace mozilla