Bug 1890793: Assert CallArgs::newTarget is not gray. r=spidermonkey-reviewers,sfink...
[gecko.git] / dom / svg / SVGLength.cpp
blobf7c383a16fa5c411e89fa8ec719bfd60878417ac
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;
24 const unsigned short SVG_LENGTHTYPE_CH = 12;
25 const unsigned short SVG_LENGTHTYPE_REM = 13;
26 const unsigned short SVG_LENGTHTYPE_IC = 14;
27 const unsigned short SVG_LENGTHTYPE_CAP = 15;
28 const unsigned short SVG_LENGTHTYPE_VW = 16;
29 const unsigned short SVG_LENGTHTYPE_VH = 17;
30 const unsigned short SVG_LENGTHTYPE_VMIN = 18;
31 const unsigned short SVG_LENGTHTYPE_VMAX = 19;
33 void SVGLength::GetValueAsString(nsAString& aValue) const {
34 nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
36 nsAutoString unitString;
37 GetUnitString(unitString, mUnit);
38 aValue.Append(unitString);
41 bool SVGLength::SetValueFromString(const nsAString& aString) {
42 bool success;
43 auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
45 if (!success) {
46 return false;
49 RangedPtr<const char16_t> iter = SVGContentUtils::GetStartRangedPtr(token);
50 const RangedPtr<const char16_t> end = SVGContentUtils::GetEndRangedPtr(token);
52 float value;
54 if (!SVGContentUtils::ParseNumber(iter, end, value)) {
55 return false;
58 const nsAString& units = Substring(iter.get(), end.get());
59 uint16_t unitType = GetUnitTypeForString(units);
60 if (unitType == SVG_LENGTHTYPE_UNKNOWN) {
61 return false;
63 mValue = value;
64 mUnit = uint8_t(unitType);
65 return true;
68 /*static*/
69 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) {
70 return aUnit == SVG_LENGTHTYPE_NUMBER ||
71 (aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_Q);
74 /*static*/
75 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit) {
76 return aUnit == SVG_LENGTHTYPE_EMS || aUnit == SVG_LENGTHTYPE_EXS ||
77 (aUnit >= SVG_LENGTHTYPE_CH && aUnit <= SVG_LENGTHTYPE_CAP);
80 /**
81 * Helper to convert between different CSS absolute units without the need for
82 * an element, which provides more flexibility at the DOM level (and without
83 * the need for an intermediary conversion to user units, which avoids
84 * unnecessary overhead and rounding error).
86 * Example usage: to find out how many centimeters there are per inch:
88 * GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
90 /*static*/
91 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
92 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
93 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
95 static const float CSSAbsoluteUnitConversionFactors[7][7] = {
96 // columns: px, cm, mm, in, pt, pc, q
97 // px per...:
98 {1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f,
99 0.94488188988f},
100 // cm per...:
101 {0.02645833333f, 1.0f, 0.1f, 2.54f, 0.035277777777777778f,
102 0.42333333333333333f, 0.025f},
103 // mm per...:
104 {0.26458333333f, 10.0f, 1.0f, 25.4f, 0.35277777777777778f,
105 4.2333333333333333f, 0.25f},
106 // in per...:
107 {0.01041666666f, 0.39370078740157481f, 0.039370078740157481f, 1.0f,
108 0.013888888888888889f, 0.16666666666666667f, 0.02204860853f},
109 // pt per...:
110 {0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f,
111 0.70866141732f},
112 // pc per...:
113 {0.0625f, 2.3622047244094489f, 0.23622047244094489f, 6.0f,
114 0.083333333333333333f, 1.0f, 16.9333333333f},
115 // q per...:
116 {1.0583333332f, 40.0f, 4.0f, 45.354336f, 1.41111111111f, 16.9333333333f,
117 1.0f}};
119 auto ToIndex = [](uint8_t aUnit) {
120 return aUnit == SVG_LENGTHTYPE_NUMBER ? 0 : aUnit - 5;
123 return CSSAbsoluteUnitConversionFactors[ToIndex(aUnits)][ToIndex(aPerUnit)];
126 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
127 const SVGElement* aElement,
128 uint8_t aAxis) const {
129 if (aUnit == mUnit) {
130 return mValue;
132 if ((aUnit == SVG_LENGTHTYPE_NUMBER && mUnit == SVG_LENGTHTYPE_PX) ||
133 (aUnit == SVG_LENGTHTYPE_PX && mUnit == SVG_LENGTHTYPE_NUMBER)) {
134 return mValue;
136 if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
137 return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
140 // Otherwise we do a two step conversion via user units. This can only
141 // succeed if aElement is non-null (although that's not sufficient to
142 // guarantee success).
144 SVGElementMetrics userSpaceMetrics(aElement);
146 float userUnitsPerCurrentUnit = GetPixelsPerUnit(userSpaceMetrics, aAxis);
147 float userUnitsPerNewUnit =
148 SVGLength(0.0f, aUnit).GetPixelsPerUnit(userSpaceMetrics, aAxis);
150 float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
152 // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
153 // be zero.
154 if (std::isfinite(value)) {
155 return value;
157 return std::numeric_limits<float>::quiet_NaN();
160 // Helpers:
162 /*static*/
163 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
164 uint8_t aUnitType, uint8_t aAxis) {
165 switch (aUnitType) {
166 case SVG_LENGTHTYPE_NUMBER:
167 case SVG_LENGTHTYPE_PX:
168 return 1.0f;
169 case SVG_LENGTHTYPE_PERCENTAGE:
170 return aMetrics.GetAxisLength(aAxis) / 100.0f;
171 case SVG_LENGTHTYPE_EMS:
172 return aMetrics.GetEmLength(UserSpaceMetrics::Type::This);
173 case SVG_LENGTHTYPE_EXS:
174 return aMetrics.GetExLength(UserSpaceMetrics::Type::This);
175 case SVG_LENGTHTYPE_CH:
176 return aMetrics.GetChSize(UserSpaceMetrics::Type::This);
177 case SVG_LENGTHTYPE_REM:
178 return aMetrics.GetEmLength(UserSpaceMetrics::Type::Root);
179 case SVG_LENGTHTYPE_IC:
180 return aMetrics.GetIcWidth(UserSpaceMetrics::Type::This);
181 case SVG_LENGTHTYPE_CAP:
182 return aMetrics.GetCapHeight(UserSpaceMetrics::Type::This);
183 case SVG_LENGTHTYPE_VW:
184 return aMetrics.GetCSSViewportSize().width / 100.f;
185 case SVG_LENGTHTYPE_VH:
186 return aMetrics.GetCSSViewportSize().height / 100.f;
187 case SVG_LENGTHTYPE_VMIN: {
188 auto sz = aMetrics.GetCSSViewportSize();
189 return std::min(sz.width, sz.height) / 100.f;
191 case SVG_LENGTHTYPE_VMAX: {
192 auto sz = aMetrics.GetCSSViewportSize();
193 return std::max(sz.width, sz.height) / 100.f;
195 default:
196 MOZ_ASSERT(IsAbsoluteUnit(aUnitType));
197 return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX, aUnitType);
201 /* static */
202 nsCSSUnit SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit) {
203 switch (aSpecifiedUnit) {
204 case SVG_LENGTHTYPE_NUMBER:
205 case SVG_LENGTHTYPE_PX:
206 return nsCSSUnit::eCSSUnit_Pixel;
208 case SVG_LENGTHTYPE_MM:
209 return nsCSSUnit::eCSSUnit_Millimeter;
211 case SVG_LENGTHTYPE_CM:
212 return nsCSSUnit::eCSSUnit_Centimeter;
214 case SVG_LENGTHTYPE_IN:
215 return nsCSSUnit::eCSSUnit_Inch;
217 case SVG_LENGTHTYPE_PT:
218 return nsCSSUnit::eCSSUnit_Point;
220 case SVG_LENGTHTYPE_PC:
221 return nsCSSUnit::eCSSUnit_Pica;
223 case SVG_LENGTHTYPE_PERCENTAGE:
224 return nsCSSUnit::eCSSUnit_Percent;
226 case SVG_LENGTHTYPE_EMS:
227 return nsCSSUnit::eCSSUnit_EM;
229 case SVG_LENGTHTYPE_EXS:
230 return nsCSSUnit::eCSSUnit_XHeight;
232 case SVG_LENGTHTYPE_Q:
233 return nsCSSUnit::eCSSUnit_Quarter;
235 case SVG_LENGTHTYPE_CH:
236 return nsCSSUnit::eCSSUnit_Char;
238 case SVG_LENGTHTYPE_REM:
239 return nsCSSUnit::eCSSUnit_RootEM;
241 case SVG_LENGTHTYPE_IC:
242 return nsCSSUnit::eCSSUnit_Ideographic;
244 case SVG_LENGTHTYPE_CAP:
245 return nsCSSUnit::eCSSUnit_CapHeight;
247 case SVG_LENGTHTYPE_VW:
248 return nsCSSUnit::eCSSUnit_VW;
250 case SVG_LENGTHTYPE_VH:
251 return nsCSSUnit::eCSSUnit_VH;
253 case SVG_LENGTHTYPE_VMIN:
254 return nsCSSUnit::eCSSUnit_VMin;
256 case SVG_LENGTHTYPE_VMAX:
257 return nsCSSUnit::eCSSUnit_VMax;
259 default:
260 MOZ_ASSERT_UNREACHABLE("Unknown unit type");
261 return nsCSSUnit::eCSSUnit_Pixel;
265 /* static */
266 void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) {
267 switch (aUnitType) {
268 case SVG_LENGTHTYPE_NUMBER:
269 aUnit.Truncate();
270 return;
271 case SVG_LENGTHTYPE_PERCENTAGE:
272 aUnit.AssignLiteral("%");
273 return;
274 case SVG_LENGTHTYPE_EMS:
275 aUnit.AssignLiteral("em");
276 return;
277 case SVG_LENGTHTYPE_EXS:
278 aUnit.AssignLiteral("ex");
279 return;
280 case SVG_LENGTHTYPE_PX:
281 aUnit.AssignLiteral("px");
282 return;
283 case SVG_LENGTHTYPE_CM:
284 aUnit.AssignLiteral("cm");
285 return;
286 case SVG_LENGTHTYPE_MM:
287 aUnit.AssignLiteral("mm");
288 return;
289 case SVG_LENGTHTYPE_IN:
290 aUnit.AssignLiteral("in");
291 return;
292 case SVG_LENGTHTYPE_PT:
293 aUnit.AssignLiteral("pt");
294 return;
295 case SVG_LENGTHTYPE_PC:
296 aUnit.AssignLiteral("pc");
297 return;
298 case SVG_LENGTHTYPE_Q:
299 aUnit.AssignLiteral("q");
300 return;
301 case SVG_LENGTHTYPE_CH:
302 aUnit.AssignLiteral("ch");
303 return;
304 case SVG_LENGTHTYPE_REM:
305 aUnit.AssignLiteral("rem");
306 return;
307 case SVG_LENGTHTYPE_IC:
308 aUnit.AssignLiteral("ic");
309 return;
310 case SVG_LENGTHTYPE_CAP:
311 aUnit.AssignLiteral("cap");
312 return;
313 case SVG_LENGTHTYPE_VW:
314 aUnit.AssignLiteral("vw");
315 return;
316 case SVG_LENGTHTYPE_VH:
317 aUnit.AssignLiteral("vh");
318 return;
319 case SVG_LENGTHTYPE_VMIN:
320 aUnit.AssignLiteral("vmin");
321 return;
322 case SVG_LENGTHTYPE_VMAX:
323 aUnit.AssignLiteral("vmax");
324 return;
326 MOZ_ASSERT_UNREACHABLE(
327 "Unknown unit type! Someone's using an SVGLength "
328 "with an invalid unit?");
331 /* static */
332 uint16_t SVGLength::GetUnitTypeForString(const nsAString& aUnit) {
333 if (aUnit.IsEmpty()) {
334 return SVG_LENGTHTYPE_NUMBER;
336 if (aUnit.EqualsLiteral("%")) {
337 return SVG_LENGTHTYPE_PERCENTAGE;
339 if (aUnit.LowerCaseEqualsLiteral("em")) {
340 return SVG_LENGTHTYPE_EMS;
342 if (aUnit.LowerCaseEqualsLiteral("ex")) {
343 return SVG_LENGTHTYPE_EXS;
345 if (aUnit.LowerCaseEqualsLiteral("px")) {
346 return SVG_LENGTHTYPE_PX;
348 if (aUnit.LowerCaseEqualsLiteral("cm")) {
349 return SVG_LENGTHTYPE_CM;
351 if (aUnit.LowerCaseEqualsLiteral("mm")) {
352 return SVG_LENGTHTYPE_MM;
354 if (aUnit.LowerCaseEqualsLiteral("in")) {
355 return SVG_LENGTHTYPE_IN;
357 if (aUnit.LowerCaseEqualsLiteral("pt")) {
358 return SVG_LENGTHTYPE_PT;
360 if (aUnit.LowerCaseEqualsLiteral("pc")) {
361 return SVG_LENGTHTYPE_PC;
363 if (aUnit.LowerCaseEqualsLiteral("q")) {
364 return SVG_LENGTHTYPE_Q;
366 if (aUnit.LowerCaseEqualsLiteral("ch")) {
367 return SVG_LENGTHTYPE_CH;
369 if (aUnit.LowerCaseEqualsLiteral("rem")) {
370 return SVG_LENGTHTYPE_REM;
372 if (aUnit.LowerCaseEqualsLiteral("ic")) {
373 return SVG_LENGTHTYPE_IC;
375 if (aUnit.LowerCaseEqualsLiteral("cap")) {
376 return SVG_LENGTHTYPE_CAP;
378 if (aUnit.LowerCaseEqualsLiteral("vw")) {
379 return SVG_LENGTHTYPE_VW;
381 if (aUnit.LowerCaseEqualsLiteral("vh")) {
382 return SVG_LENGTHTYPE_VH;
384 if (aUnit.LowerCaseEqualsLiteral("vmin")) {
385 return SVG_LENGTHTYPE_VMIN;
387 if (aUnit.LowerCaseEqualsLiteral("vmax")) {
388 return SVG_LENGTHTYPE_VMAX;
390 return SVG_LENGTHTYPE_UNKNOWN;
393 } // namespace mozilla