Backed out 2 changesets (bug 1287054) for causing Android wpt failures in SVGLength...
[gecko.git] / dom / svg / SVGLength.cpp
blob44cd05f44dd2b0dd2531a0a59799f0a9bd83eaca
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/. */
7 #include "SVGLength.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/dom/SVGElement.h"
11 #include "mozilla/dom/SVGSVGElement.h"
12 #include "nsCSSValue.h"
13 #include "nsTextFormatter.h"
14 #include "SVGContentUtils.h"
15 #include <limits>
16 #include <algorithm>
18 using namespace mozilla::dom;
19 using namespace mozilla::dom::SVGLength_Binding;
21 namespace mozilla {
23 const unsigned short SVG_LENGTHTYPE_Q = 11;
25 void SVGLength::GetValueAsString(nsAString& aValue) const {
26 nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
28 nsAutoString unitString;
29 GetUnitString(unitString, mUnit);
30 aValue.Append(unitString);
33 bool SVGLength::SetValueFromString(const nsAString& aString) {
34 bool success;
35 auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
37 if (!success) {
38 return false;
41 RangedPtr<const char16_t> iter = SVGContentUtils::GetStartRangedPtr(token);
42 const RangedPtr<const char16_t> end = SVGContentUtils::GetEndRangedPtr(token);
44 float value;
46 if (!SVGContentUtils::ParseNumber(iter, end, value)) {
47 return false;
50 const nsAString& units = Substring(iter.get(), end.get());
51 uint16_t unitType = GetUnitTypeForString(units);
52 if (unitType == SVG_LENGTHTYPE_UNKNOWN) {
53 return false;
55 mValue = value;
56 mUnit = uint8_t(unitType);
57 return true;
60 /*static*/
61 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) {
62 return aUnit == SVG_LENGTHTYPE_NUMBER ||
63 (aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_Q);
66 /*static*/
67 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit) {
68 return aUnit == SVG_LENGTHTYPE_EMS || aUnit == SVG_LENGTHTYPE_EXS;
71 /**
72 * Helper to convert between different CSS absolute units without the need for
73 * an element, which provides more flexibility at the DOM level (and without
74 * the need for an intermediary conversion to user units, which avoids
75 * unnecessary overhead and rounding error).
77 * Example usage: to find out how many centimeters there are per inch:
79 * GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
81 /*static*/
82 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
83 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
84 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
86 static const float CSSAbsoluteUnitConversionFactors[7][7] = {
87 // columns: px, cm, mm, in, pt, pc, q
88 // px per...:
89 {1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f,
90 0.94488188988f},
91 // cm per...:
92 {0.02645833333f, 1.0f, 0.1f, 2.54f, 0.035277777777777778f,
93 0.42333333333333333f, 0.025f},
94 // mm per...:
95 {0.26458333333f, 10.0f, 1.0f, 25.4f, 0.35277777777777778f,
96 4.2333333333333333f, 0.25f},
97 // in per...:
98 {0.01041666666f, 0.39370078740157481f, 0.039370078740157481f, 1.0f,
99 0.013888888888888889f, 0.16666666666666667f, 0.02204860853f},
100 // pt per...:
101 {0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f,
102 0.70866141732f},
103 // pc per...:
104 {0.0625f, 2.3622047244094489f, 0.23622047244094489f, 6.0f,
105 0.083333333333333333f, 1.0f, 16.9333333333f},
106 // q per...:
107 {1.0583333332f, 40.0f, 4.0f, 45.354336f, 1.41111111111f, 16.9333333333f,
108 1.0f}};
110 auto ToIndex = [](uint8_t aUnit) {
111 return aUnit == SVG_LENGTHTYPE_NUMBER ? 0 : aUnit - 5;
114 return CSSAbsoluteUnitConversionFactors[ToIndex(aUnits)][ToIndex(aPerUnit)];
117 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
118 const SVGElement* aElement,
119 uint8_t aAxis) const {
120 if (aUnit == mUnit) {
121 return mValue;
123 if ((aUnit == SVG_LENGTHTYPE_NUMBER && mUnit == SVG_LENGTHTYPE_PX) ||
124 (aUnit == SVG_LENGTHTYPE_PX && mUnit == SVG_LENGTHTYPE_NUMBER)) {
125 return mValue;
127 if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
128 return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
131 // Otherwise we do a two step conversion via user units. This can only
132 // succeed if aElement is non-null (although that's not sufficient to
133 // guarantee success).
135 SVGElementMetrics userSpaceMetrics(aElement);
137 float userUnitsPerCurrentUnit = GetPixelsPerUnit(userSpaceMetrics, aAxis);
138 float userUnitsPerNewUnit =
139 SVGLength(0.0f, aUnit).GetPixelsPerUnit(userSpaceMetrics, aAxis);
141 NS_ASSERTION(
142 userUnitsPerCurrentUnit >= 0 || !std::isfinite(userUnitsPerCurrentUnit),
143 "bad userUnitsPerCurrentUnit");
144 NS_ASSERTION(userUnitsPerNewUnit >= 0 || !std::isfinite(userUnitsPerNewUnit),
145 "bad userUnitsPerNewUnit");
147 float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
149 // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
150 // be zero.
151 if (std::isfinite(value)) {
152 return value;
154 return std::numeric_limits<float>::quiet_NaN();
157 // Helpers:
159 /*static*/
160 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
161 uint8_t aUnitType, uint8_t aAxis) {
162 switch (aUnitType) {
163 case SVG_LENGTHTYPE_NUMBER:
164 case SVG_LENGTHTYPE_PX:
165 return 1.0f;
166 case SVG_LENGTHTYPE_PERCENTAGE:
167 return aMetrics.GetAxisLength(aAxis) / 100.0f;
168 case SVG_LENGTHTYPE_EMS:
169 return aMetrics.GetEmLength();
170 case SVG_LENGTHTYPE_EXS:
171 return aMetrics.GetExLength();
172 default:
173 MOZ_ASSERT(IsAbsoluteUnit(aUnitType));
174 return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX, aUnitType);
178 /* static */
179 nsCSSUnit SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit) {
180 switch (aSpecifiedUnit) {
181 case SVG_LENGTHTYPE_NUMBER:
182 case SVG_LENGTHTYPE_PX:
183 return nsCSSUnit::eCSSUnit_Pixel;
185 case SVG_LENGTHTYPE_MM:
186 return nsCSSUnit::eCSSUnit_Millimeter;
188 case SVG_LENGTHTYPE_CM:
189 return nsCSSUnit::eCSSUnit_Centimeter;
191 case SVG_LENGTHTYPE_IN:
192 return nsCSSUnit::eCSSUnit_Inch;
194 case SVG_LENGTHTYPE_PT:
195 return nsCSSUnit::eCSSUnit_Point;
197 case SVG_LENGTHTYPE_PC:
198 return nsCSSUnit::eCSSUnit_Pica;
200 case SVG_LENGTHTYPE_PERCENTAGE:
201 return nsCSSUnit::eCSSUnit_Percent;
203 case SVG_LENGTHTYPE_EMS:
204 return nsCSSUnit::eCSSUnit_EM;
206 case SVG_LENGTHTYPE_EXS:
207 return nsCSSUnit::eCSSUnit_XHeight;
209 case SVG_LENGTHTYPE_Q:
210 return nsCSSUnit::eCSSUnit_Quarter;
212 default:
213 MOZ_ASSERT_UNREACHABLE("Unknown unit type");
214 return nsCSSUnit::eCSSUnit_Pixel;
218 /* static */
219 void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) {
220 switch (aUnitType) {
221 case SVG_LENGTHTYPE_NUMBER:
222 aUnit.Truncate();
223 return;
224 case SVG_LENGTHTYPE_PERCENTAGE:
225 aUnit.AssignLiteral("%");
226 return;
227 case SVG_LENGTHTYPE_EMS:
228 aUnit.AssignLiteral("em");
229 return;
230 case SVG_LENGTHTYPE_EXS:
231 aUnit.AssignLiteral("ex");
232 return;
233 case SVG_LENGTHTYPE_PX:
234 aUnit.AssignLiteral("px");
235 return;
236 case SVG_LENGTHTYPE_CM:
237 aUnit.AssignLiteral("cm");
238 return;
239 case SVG_LENGTHTYPE_MM:
240 aUnit.AssignLiteral("mm");
241 return;
242 case SVG_LENGTHTYPE_IN:
243 aUnit.AssignLiteral("in");
244 return;
245 case SVG_LENGTHTYPE_PT:
246 aUnit.AssignLiteral("pt");
247 return;
248 case SVG_LENGTHTYPE_PC:
249 aUnit.AssignLiteral("pc");
250 return;
251 case SVG_LENGTHTYPE_Q:
252 aUnit.AssignLiteral("q");
253 return;
255 MOZ_ASSERT_UNREACHABLE(
256 "Unknown unit type! Someone's using an SVGLength "
257 "with an invalid unit?");
260 /* static */
261 uint16_t SVGLength::GetUnitTypeForString(const nsAString& aUnit) {
262 if (aUnit.IsEmpty()) {
263 return SVG_LENGTHTYPE_NUMBER;
265 if (aUnit.EqualsLiteral("%")) {
266 return SVG_LENGTHTYPE_PERCENTAGE;
268 if (aUnit.LowerCaseEqualsLiteral("em")) {
269 return SVG_LENGTHTYPE_EMS;
271 if (aUnit.LowerCaseEqualsLiteral("ex")) {
272 return SVG_LENGTHTYPE_EXS;
274 if (aUnit.LowerCaseEqualsLiteral("px")) {
275 return SVG_LENGTHTYPE_PX;
277 if (aUnit.LowerCaseEqualsLiteral("cm")) {
278 return SVG_LENGTHTYPE_CM;
280 if (aUnit.LowerCaseEqualsLiteral("mm")) {
281 return SVG_LENGTHTYPE_MM;
283 if (aUnit.LowerCaseEqualsLiteral("in")) {
284 return SVG_LENGTHTYPE_IN;
286 if (aUnit.LowerCaseEqualsLiteral("pt")) {
287 return SVG_LENGTHTYPE_PT;
289 if (aUnit.LowerCaseEqualsLiteral("pc")) {
290 return SVG_LENGTHTYPE_PC;
292 if (aUnit.LowerCaseEqualsLiteral("q")) {
293 return SVG_LENGTHTYPE_Q;
295 return SVG_LENGTHTYPE_UNKNOWN;
298 } // namespace mozilla