Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGLength.cpp
blob058e4a453109ab3add65d589b99abd94d24bfc05
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/dom/SVGElement.h"
10 #include "nsCSSValue.h"
11 #include "nsTextFormatter.h"
12 #include "SVGContentUtils.h"
13 #include <limits>
14 #include <algorithm>
16 using namespace mozilla::dom;
17 using namespace mozilla::dom::SVGLength_Binding;
19 namespace mozilla {
21 // These types are numbered so that different length categories are in
22 // contiguous ranges - See `SVGLength::Is[..]Unit()`.
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_LH = 16;
29 const unsigned short SVG_LENGTHTYPE_RLH = 17;
30 const unsigned short SVG_LENGTHTYPE_VW = 18;
31 const unsigned short SVG_LENGTHTYPE_VH = 19;
32 const unsigned short SVG_LENGTHTYPE_VMIN = 20;
33 const unsigned short SVG_LENGTHTYPE_VMAX = 21;
35 void SVGLength::GetValueAsString(nsAString& aValue) const {
36 nsTextFormatter::ssprintf(aValue, u"%g", (double)mValue);
38 nsAutoString unitString;
39 GetUnitString(unitString, mUnit);
40 aValue.Append(unitString);
43 bool SVGLength::SetValueFromString(const nsAString& aString) {
44 bool success;
45 auto token = SVGContentUtils::GetAndEnsureOneToken(aString, success);
47 if (!success) {
48 return false;
51 nsAString::const_iterator iter, end;
52 aString.BeginReading(iter);
53 aString.EndReading(end);
55 float value;
57 if (!SVGContentUtils::ParseNumber(iter, end, value)) {
58 return false;
61 const nsAString& units = Substring(iter, end);
62 uint16_t unitType = GetUnitTypeForString(units);
63 if (unitType == SVG_LENGTHTYPE_UNKNOWN) {
64 return false;
66 mValue = value;
67 mUnit = uint8_t(unitType);
68 return true;
71 /*static*/
72 bool SVGLength::IsAbsoluteUnit(uint8_t aUnit) {
73 return aUnit == SVG_LENGTHTYPE_NUMBER ||
74 (aUnit >= SVG_LENGTHTYPE_PX && aUnit <= SVG_LENGTHTYPE_Q);
77 /*static*/
78 bool SVGLength::IsFontRelativeUnit(uint8_t aUnit) {
79 return aUnit == SVG_LENGTHTYPE_EMS || aUnit == SVG_LENGTHTYPE_EXS ||
80 (aUnit >= SVG_LENGTHTYPE_CH && aUnit <= SVG_LENGTHTYPE_RLH);
83 /**
84 * Helper to convert between different CSS absolute units without the need for
85 * an element, which provides more flexibility at the DOM level (and without
86 * the need for an intermediary conversion to user units, which avoids
87 * unnecessary overhead and rounding error).
89 * Example usage: to find out how many centimeters there are per inch:
91 * GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_CM, SVG_LENGTHTYPE_IN)
93 /*static*/
94 float SVGLength::GetAbsUnitsPerAbsUnit(uint8_t aUnits, uint8_t aPerUnit) {
95 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aUnits), "Not a CSS absolute unit");
96 MOZ_ASSERT(SVGLength::IsAbsoluteUnit(aPerUnit), "Not a CSS absolute unit");
98 static const float CSSAbsoluteUnitConversionFactors[7][7] = {
99 // columns: px, cm, mm, in, pt, pc, q
100 // px per...:
101 {1.0f, 37.7952755906f, 3.779528f, 96.0f, 1.33333333333333333f, 16.0f,
102 0.94488188988f},
103 // cm per...:
104 {0.02645833333f, 1.0f, 0.1f, 2.54f, 0.035277777777777778f,
105 0.42333333333333333f, 0.025f},
106 // mm per...:
107 {0.26458333333f, 10.0f, 1.0f, 25.4f, 0.35277777777777778f,
108 4.2333333333333333f, 0.25f},
109 // in per...:
110 {0.01041666666f, 0.39370078740157481f, 0.039370078740157481f, 1.0f,
111 0.013888888888888889f, 0.16666666666666667f, 0.02204860853f},
112 // pt per...:
113 {0.75f, 28.346456692913386f, 2.8346456692913386f, 72.0f, 1.0f, 12.0f,
114 0.70866141732f},
115 // pc per...:
116 {0.0625f, 2.3622047244094489f, 0.23622047244094489f, 6.0f,
117 0.083333333333333333f, 1.0f, 16.9333333333f},
118 // q per...:
119 {1.0583333332f, 40.0f, 4.0f, 45.354336f, 1.41111111111f, 16.9333333333f,
120 1.0f}};
122 auto ToIndex = [](uint8_t aUnit) {
123 return aUnit == SVG_LENGTHTYPE_NUMBER ? 0 : aUnit - 5;
126 return CSSAbsoluteUnitConversionFactors[ToIndex(aUnits)][ToIndex(aPerUnit)];
129 float SVGLength::GetValueInSpecifiedUnit(uint8_t aUnit,
130 const SVGElement* aElement,
131 uint8_t aAxis) const {
132 if (aUnit == mUnit) {
133 return mValue;
135 if ((aUnit == SVG_LENGTHTYPE_NUMBER && mUnit == SVG_LENGTHTYPE_PX) ||
136 (aUnit == SVG_LENGTHTYPE_PX && mUnit == SVG_LENGTHTYPE_NUMBER)) {
137 return mValue;
139 if (IsAbsoluteUnit(aUnit) && IsAbsoluteUnit(mUnit)) {
140 return mValue * GetAbsUnitsPerAbsUnit(aUnit, mUnit);
143 // Otherwise we do a two step conversion via user units. This can only
144 // succeed if aElement is non-null (although that's not sufficient to
145 // guarantee success).
147 SVGElementMetrics userSpaceMetrics(aElement);
149 float userUnitsPerCurrentUnit = GetPixelsPerUnit(userSpaceMetrics, aAxis);
150 float userUnitsPerNewUnit =
151 SVGLength(0.0f, aUnit).GetPixelsPerUnit(userSpaceMetrics, aAxis);
153 float value = mValue * userUnitsPerCurrentUnit / userUnitsPerNewUnit;
155 // userUnitsPerCurrentUnit could be infinity, or userUnitsPerNewUnit could
156 // be zero.
157 if (std::isfinite(value)) {
158 return value;
160 return std::numeric_limits<float>::quiet_NaN();
163 // Helpers:
165 enum class ZoomType { Self, SelfFromRoot, None };
167 /*static*/
168 float SVGLength::GetPixelsPerUnit(const UserSpaceMetrics& aMetrics,
169 uint8_t aUnitType, uint8_t aAxis,
170 bool aApplyZoom) {
171 auto zoomType = ZoomType::Self;
172 float value = [&]() -> float {
173 switch (aUnitType) {
174 case SVG_LENGTHTYPE_NUMBER:
175 case SVG_LENGTHTYPE_PX:
176 return 1.0f;
177 case SVG_LENGTHTYPE_PERCENTAGE:
178 zoomType = ZoomType::None;
179 return aMetrics.GetAxisLength(aAxis) / 100.0f;
180 case SVG_LENGTHTYPE_EMS:
181 zoomType = ZoomType::None;
182 return aMetrics.GetEmLength(UserSpaceMetrics::Type::This);
183 case SVG_LENGTHTYPE_EXS:
184 zoomType = ZoomType::None;
185 return aMetrics.GetExLength(UserSpaceMetrics::Type::This);
186 case SVG_LENGTHTYPE_CH:
187 zoomType = ZoomType::None;
188 return aMetrics.GetChSize(UserSpaceMetrics::Type::This);
189 case SVG_LENGTHTYPE_REM:
190 zoomType = ZoomType::SelfFromRoot;
191 return aMetrics.GetEmLength(UserSpaceMetrics::Type::Root);
192 case SVG_LENGTHTYPE_IC:
193 zoomType = ZoomType::None;
194 return aMetrics.GetIcWidth(UserSpaceMetrics::Type::This);
195 case SVG_LENGTHTYPE_CAP:
196 zoomType = ZoomType::None;
197 return aMetrics.GetCapHeight(UserSpaceMetrics::Type::This);
198 case SVG_LENGTHTYPE_VW:
199 return aMetrics.GetCSSViewportSize().width / 100.f;
200 case SVG_LENGTHTYPE_VH:
201 return aMetrics.GetCSSViewportSize().height / 100.f;
202 case SVG_LENGTHTYPE_VMIN: {
203 auto sz = aMetrics.GetCSSViewportSize();
204 return std::min(sz.width, sz.height) / 100.f;
206 case SVG_LENGTHTYPE_VMAX: {
207 auto sz = aMetrics.GetCSSViewportSize();
208 return std::max(sz.width, sz.height) / 100.f;
210 case SVG_LENGTHTYPE_LH:
211 zoomType = ZoomType::None;
212 return aMetrics.GetLineHeight(UserSpaceMetrics::Type::This);
213 case SVG_LENGTHTYPE_RLH:
214 zoomType = ZoomType::SelfFromRoot;
215 return aMetrics.GetLineHeight(UserSpaceMetrics::Type::Root);
216 default:
217 MOZ_ASSERT(IsAbsoluteUnit(aUnitType));
218 return GetAbsUnitsPerAbsUnit(SVG_LENGTHTYPE_PX, aUnitType);
220 }();
221 if (aApplyZoom) {
222 switch (zoomType) {
223 case ZoomType::None:
224 break;
225 case ZoomType::Self:
226 value *= aMetrics.GetZoom();
227 break;
228 case ZoomType::SelfFromRoot:
229 value *= aMetrics.GetZoom() / aMetrics.GetRootZoom();
230 break;
233 return value;
236 /* static */
237 nsCSSUnit SVGLength::SpecifiedUnitTypeToCSSUnit(uint8_t aSpecifiedUnit) {
238 switch (aSpecifiedUnit) {
239 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
240 case id: \
241 return cssValue;
242 #define SVG_LENGTH_UNIT(id, name, cssValue) SVG_LENGTH_EMPTY_UNIT(id, cssValue)
243 #include "mozilla/dom/SVGLengthUnits.h"
244 #undef SVG_LENGTH_UNIT
245 #undef SVG_LENGTH_EMPTY_UNIT
246 default:
247 MOZ_ASSERT_UNREACHABLE("Unknown unit type");
248 return nsCSSUnit::eCSSUnit_Pixel;
252 /* static */
253 void SVGLength::GetUnitString(nsAString& aUnit, uint16_t aUnitType) {
254 switch (aUnitType) {
255 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
256 case id: \
257 aUnit.Truncate(); \
258 return;
259 #define SVG_LENGTH_UNIT(id, name, cssValue) \
260 case id: \
261 aUnit.AssignLiteral(name); \
262 return;
263 #include "mozilla/dom/SVGLengthUnits.h"
264 #undef SVG_LENGTH_UNIT
265 #undef SVG_LENGTH_EMPTY_UNIT
267 MOZ_ASSERT_UNREACHABLE(
268 "Unknown unit type! Someone's using an SVGLength "
269 "with an invalid unit?");
272 /* static */
273 uint16_t SVGLength::GetUnitTypeForString(const nsAString& aUnit) {
274 #define SVG_LENGTH_EMPTY_UNIT(id, cssValue) \
275 if (aUnit.IsEmpty()) { \
276 return id; \
278 #define SVG_LENGTH_UNIT(id, name, cssValue) \
279 if (aUnit.LowerCaseEqualsLiteral(name)) { \
280 return id; \
282 #include "mozilla/dom/SVGLengthUnits.h"
283 #undef SVG_LENGTH_UNIT
284 #undef SVG_LENGTH_EMPTY_UNIT
286 return SVG_LENGTHTYPE_UNKNOWN;
289 } // namespace mozilla