1 /*
2  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
4  * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_PRIMITIVE_VALUE_H_
23 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_PRIMITIVE_VALUE_H_
24 
25 #include <bitset>
26 #include "third_party/blink/renderer/core/core_export.h"
27 #include "third_party/blink/renderer/core/css/css_value.h"
28 #include "third_party/blink/renderer/platform/wtf/casting.h"
29 #include "third_party/blink/renderer/platform/wtf/forward.h"
30 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
31 #include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
32 #include "third_party/blink/renderer/platform/wtf/text/string_view.h"
33 
34 namespace blink {
35 
36 class CSSToLengthConversionData;
37 class Length;
38 
39 // Dimension calculations are imprecise, often resulting in values of e.g.
40 // 44.99998. We need to go ahead and round if we're really close to the next
41 // integer value.
42 template <typename T>
RoundForImpreciseConversion(double value)43 inline T RoundForImpreciseConversion(double value) {
44   value += (value < 0) ? -0.01 : +0.01;
45   return ((value > std::numeric_limits<T>::max()) ||
46           (value < std::numeric_limits<T>::min()))
47              ? 0
48              : static_cast<T>(value);
49 }
50 
51 template <>
RoundForImpreciseConversion(double value)52 inline float RoundForImpreciseConversion(double value) {
53   double ceiled_value = ceil(value);
54   double proximity_to_next_int = ceiled_value - value;
55   if (proximity_to_next_int <= 0.01 && value > 0)
56     return static_cast<float>(ceiled_value);
57   if (proximity_to_next_int >= 0.99 && value < 0)
58     return static_cast<float>(floor(value));
59   return static_cast<float>(value);
60 }
61 
62 // Common interface for numeric data types, including both literals (e.g. 1,
63 // 10px, 4%) and values involving math functions (e.g. calc(3px + 2em)).
64 class CORE_EXPORT CSSPrimitiveValue : public CSSValue {
65  public:
66   // These units are iterated through, so be careful when adding or changing the
67   // order.
68   enum class UnitType {
69     kUnknown,
70     kNumber,
71     kPercentage,
72     // Length units
73     kEms,
74     kExs,
75     kPixels,
76     kCentimeters,
77     kMillimeters,
78     kInches,
79     kPoints,
80     kPicas,
81     kQuarterMillimeters,
82     kViewportWidth,
83     kViewportHeight,
84     kViewportMin,
85     kViewportMax,
86     kRems,
87     kChs,
88     kUserUnits,  // The SVG term for unitless lengths
89     // Angle units
90     kDegrees,
91     kRadians,
92     kGradians,
93     kTurns,
94     // Time units
95     kMilliseconds,
96     kSeconds,
97     kHertz,
98     kKilohertz,
99     // Resolution
100     kDotsPerPixel,
101     kDotsPerInch,
102     kDotsPerCentimeter,
103     // Other units
104     kFraction,
105     kInteger,
106 
107     // This value is used to handle quirky margins in reflow roots (body, td,
108     // and th) like WinIE. The basic idea is that a stylesheet can use the value
109     // __qem (for quirky em) instead of em. When the quirky value is used, if
110     // you're in quirks mode, the margin will collapse away inside a table cell.
111     // This quirk is specified in the HTML spec but our impl is different.
112     // TODO: Remove this. crbug.com/443952
113     kQuirkyEms,
114   };
115 
116   enum LengthUnitType {
117     kUnitTypePixels = 0,
118     kUnitTypePercentage,
119     kUnitTypeFontSize,
120     kUnitTypeFontXSize,
121     kUnitTypeRootFontSize,
122     kUnitTypeZeroCharacterWidth,
123     kUnitTypeViewportWidth,
124     kUnitTypeViewportHeight,
125     kUnitTypeViewportMin,
126     kUnitTypeViewportMax,
127 
128     // This value must come after the last length unit type to enable iteration
129     // over the length unit types.
130     kLengthUnitTypeCount,
131   };
132 
133   using LengthTypeFlags = std::bitset<kLengthUnitTypeCount>;
134   struct CSSLengthArray {
CSSLengthArrayCSSLengthArray135     CSSLengthArray() : values(kLengthUnitTypeCount) {
136     }
137 
138     Vector<double, CSSPrimitiveValue::kLengthUnitTypeCount> values;
139     LengthTypeFlags type_flags;
140   };
141 
142   // Returns false if the value cannot be represented as a length array, which
143   // happens when comparisons are involved (e.g., max(10px, 10%)).
144   bool AccumulateLengthArray(CSSLengthArray&, double multiplier = 1) const;
145 
146   // Returns all types of length units involved in this value.
147   void AccumulateLengthUnitTypes(LengthTypeFlags& types) const;
148 
149   enum UnitCategory {
150     kUNumber,
151     kUPercent,
152     kULength,
153     kUAngle,
154     kUTime,
155     kUFrequency,
156     kUResolution,
157     kUOther
158   };
159   static UnitCategory UnitTypeToUnitCategory(UnitType);
160   static float ClampToCSSLengthRange(double);
161 
IsAngle(UnitType unit)162   static bool IsAngle(UnitType unit) {
163     return unit == UnitType::kDegrees || unit == UnitType::kRadians ||
164            unit == UnitType::kGradians || unit == UnitType::kTurns;
165   }
166   bool IsAngle() const;
IsViewportPercentageLength(UnitType type)167   static bool IsViewportPercentageLength(UnitType type) {
168     return type >= UnitType::kViewportWidth && type <= UnitType::kViewportMax;
169   }
IsLength(UnitType type)170   static bool IsLength(UnitType type) {
171     return (type >= UnitType::kEms && type <= UnitType::kUserUnits) ||
172            type == UnitType::kQuirkyEms;
173   }
IsRelativeUnit(UnitType type)174   static inline bool IsRelativeUnit(UnitType type) {
175     return type == UnitType::kPercentage || type == UnitType::kEms ||
176            type == UnitType::kExs || type == UnitType::kRems ||
177            type == UnitType::kChs || IsViewportPercentageLength(type);
178   }
179   bool IsLength() const;
180   bool IsNumber() const;
181   bool IsInteger() const;
182   bool IsPercentage() const;
183   bool IsPx() const;
IsTime(UnitType unit)184   static bool IsTime(UnitType unit) {
185     return unit == UnitType::kSeconds || unit == UnitType::kMilliseconds;
186   }
187   bool IsTime() const;
IsFrequency(UnitType unit)188   static bool IsFrequency(UnitType unit) {
189     return unit == UnitType::kHertz || unit == UnitType::kKilohertz;
190   }
IsCalculated()191   bool IsCalculated() const { return IsMathFunctionValue(); }
192   bool IsCalculatedPercentageWithLength() const;
IsResolution(UnitType type)193   static bool IsResolution(UnitType type) {
194     return type >= UnitType::kDotsPerPixel &&
195            type <= UnitType::kDotsPerCentimeter;
196   }
197   bool IsResolution() const;
IsFlex(UnitType unit)198   static bool IsFlex(UnitType unit) { return unit == UnitType::kFraction; }
199   bool IsFlex() const;
200 
201   // https://drafts.css-houdini.org/css-properties-values-api-1/#computationally-independent
202   // A property value is computationally independent if it can be converted into
203   // a computed value using only the value of the property on the element, and
204   // "global" information that cannot be changed by CSS.
205   bool IsComputationallyIndependent() const;
206 
207   // Creates either a |CSSNumericLiteralValue| or a |CSSMathFunctionValue|,
208   // depending on whether |value| is calculated or not. We should never create a
209   // |CSSPrimitiveValue| that's not of any of its subclasses.
210   static CSSPrimitiveValue* CreateFromLength(const Length& value, float zoom);
211 
212   double ComputeDegrees() const;
213   double ComputeSeconds() const;
214   double ComputeDotsPerPixel() const;
215 
216   // Computes a length in pixels, resolving relative lengths
217   template <typename T>
218   T ComputeLength(const CSSToLengthConversionData&) const;
219 
220   // Converts to a Length (Fixed, Percent or Calculated)
221   Length ConvertToLength(const CSSToLengthConversionData&) const;
222 
223   bool IsZero() const;
224 
225   // TODO(crbug.com/979895): The semantics of these untyped getters are not very
226   // clear if |this| is a math function. Do not add new callers before further
227   // refactoring and cleanups.
228   // These getters can be called only when |this| is a numeric literal or a math
229   // expression can be resolved into a single numeric value *without any type
230   // conversion* (e.g., between px and em). Otherwise, it hits a DCHECK.
231   double GetDoubleValue() const;
GetFloatValue()232   float GetFloatValue() const { return GetValue<float>(); }
GetIntValue()233   int GetIntValue() const { return GetValue<int>(); }
234   template <typename T>
GetValue()235   inline T GetValue() const {
236     return clampTo<T>(GetDoubleValue());
237   }
238 
239   template <typename T>
240   inline T ConvertTo() const;  // Defined in CSSPrimitiveValueMappings.h
241 
242   static const char* UnitTypeToString(UnitType);
StringToUnitType(StringView string)243   static UnitType StringToUnitType(StringView string) {
244     if (string.Is8Bit())
245       return StringToUnitType(string.Characters8(), string.length());
246     return StringToUnitType(string.Characters16(), string.length());
247   }
248 
249   String CustomCSSText() const;
250 
251   void TraceAfterDispatch(blink::Visitor*) const;
252 
253   static UnitType CanonicalUnitTypeForCategory(UnitCategory);
254   static double ConversionToCanonicalUnitsScaleFactor(UnitType);
255 
256   // Returns true and populates lengthUnitType, if unitType is a length unit.
257   // Otherwise, returns false.
258   static bool UnitTypeToLengthUnitType(UnitType, LengthUnitType&);
259   static UnitType LengthUnitTypeToUnitType(LengthUnitType);
260 
261  protected:
262   explicit CSSPrimitiveValue(ClassType class_type);
263 
264   // Code generated by css_primitive_value_unit_trie.cc.tmpl
265   static UnitType StringToUnitType(const LChar*, unsigned length);
266   static UnitType StringToUnitType(const UChar*, unsigned length);
267 
268   double ComputeLengthDouble(const CSSToLengthConversionData&) const;
269 };
270 
271 using CSSLengthArray = CSSPrimitiveValue::CSSLengthArray;
272 
273 template <>
274 struct DowncastTraits<CSSPrimitiveValue> {
275   static bool AllowFrom(const CSSValue& value) {
276     return value.IsPrimitiveValue();
277   }
278 };
279 
280 template <>
281 int CSSPrimitiveValue::ComputeLength(const CSSToLengthConversionData&) const;
282 
283 template <>
284 Length CSSPrimitiveValue::ComputeLength(const CSSToLengthConversionData&) const;
285 
286 template <>
287 unsigned CSSPrimitiveValue::ComputeLength(
288     const CSSToLengthConversionData&) const;
289 
290 template <>
291 int16_t CSSPrimitiveValue::ComputeLength(
292     const CSSToLengthConversionData&) const;
293 
294 template <>
295 CORE_EXPORT float CSSPrimitiveValue::ComputeLength(
296     const CSSToLengthConversionData&) const;
297 
298 template <>
299 double CSSPrimitiveValue::ComputeLength(const CSSToLengthConversionData&) const;
300 }  // namespace blink
301 
302 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_PRIMITIVE_VALUE_H_
303