1 /*
2 * (C) 1999-2003 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012 Apple Inc. All rights
4 * reserved.
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 #include "third_party/blink/renderer/core/css/css_primitive_value.h"
23
24 #include <cmath>
25
26 #include "build/build_config.h"
27 #include "third_party/blink/renderer/core/css/css_markup.h"
28 #include "third_party/blink/renderer/core/css/css_math_expression_node.h"
29 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
30 #include "third_party/blink/renderer/core/css/css_numeric_literal_value.h"
31 #include "third_party/blink/renderer/core/css/css_resolution_units.h"
32 #include "third_party/blink/renderer/core/css/css_to_length_conversion_data.h"
33 #include "third_party/blink/renderer/core/css/css_value_pool.h"
34 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
35 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
36 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
37 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
38 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
39
40 namespace blink {
41
42 namespace {
43
44 // Max/min values for CSS, needs to slightly smaller/larger than the true
45 // max/min values to allow for rounding without overflowing.
46 // Subtract two (rather than one) to allow for values to be converted to float
47 // and back without exceeding the LayoutUnit::Max.
48 const int kMaxValueForCssLength = INT_MAX / kFixedPointDenominator - 2;
49 const int kMinValueForCssLength = INT_MIN / kFixedPointDenominator + 2;
50
51 } // namespace
52
53 struct SameSizeAsCSSPrimitiveValue : CSSValue {
54 };
55 ASSERT_SIZE(CSSPrimitiveValue, SameSizeAsCSSPrimitiveValue);
56
ClampToCSSLengthRange(double value)57 float CSSPrimitiveValue::ClampToCSSLengthRange(double value) {
58 return clampTo<float>(value, kMinValueForCssLength, kMaxValueForCssLength);
59 }
60
UnitTypeToUnitCategory(UnitType type)61 CSSPrimitiveValue::UnitCategory CSSPrimitiveValue::UnitTypeToUnitCategory(
62 UnitType type) {
63 switch (type) {
64 case UnitType::kNumber:
65 return CSSPrimitiveValue::kUNumber;
66 case UnitType::kPercentage:
67 return CSSPrimitiveValue::kUPercent;
68 case UnitType::kPixels:
69 case UnitType::kCentimeters:
70 case UnitType::kMillimeters:
71 case UnitType::kQuarterMillimeters:
72 case UnitType::kInches:
73 case UnitType::kPoints:
74 case UnitType::kPicas:
75 case UnitType::kUserUnits:
76 return CSSPrimitiveValue::kULength;
77 case UnitType::kMilliseconds:
78 case UnitType::kSeconds:
79 return CSSPrimitiveValue::kUTime;
80 case UnitType::kDegrees:
81 case UnitType::kRadians:
82 case UnitType::kGradians:
83 case UnitType::kTurns:
84 return CSSPrimitiveValue::kUAngle;
85 case UnitType::kHertz:
86 case UnitType::kKilohertz:
87 return CSSPrimitiveValue::kUFrequency;
88 case UnitType::kDotsPerPixel:
89 case UnitType::kDotsPerInch:
90 case UnitType::kDotsPerCentimeter:
91 return CSSPrimitiveValue::kUResolution;
92 default:
93 return CSSPrimitiveValue::kUOther;
94 }
95 }
96
IsCalculatedPercentageWithLength() const97 bool CSSPrimitiveValue::IsCalculatedPercentageWithLength() const {
98 // TODO(crbug.com/979895): Move this function to |CSSMathFunctionValue|.
99 return IsCalculated() &&
100 To<CSSMathFunctionValue>(this)->Category() == kCalcPercentLength;
101 }
102
IsResolution() const103 bool CSSPrimitiveValue::IsResolution() const {
104 // TODO(crbug.com/983613): Either support math functions on resolutions; or
105 // provide a justification for not supporting it, and move this function to
106 // |CSSNumericLiteralValue|.
107 return IsNumericLiteralValue() &&
108 To<CSSNumericLiteralValue>(this)->IsResolution();
109 }
110
IsFlex() const111 bool CSSPrimitiveValue::IsFlex() const {
112 // TODO(crbug.com/993136): Either support math functions on flexible lengths;
113 // or provide a justification for not supporting it, and move this function to
114 // |CSSNumericLiteralValue|.
115 return IsNumericLiteralValue() && To<CSSNumericLiteralValue>(this)->IsFlex();
116 }
117
IsAngle() const118 bool CSSPrimitiveValue::IsAngle() const {
119 if (IsNumericLiteralValue())
120 return To<CSSNumericLiteralValue>(this)->IsAngle();
121 return To<CSSMathFunctionValue>(this)->IsAngle();
122 }
123
IsLength() const124 bool CSSPrimitiveValue::IsLength() const {
125 if (IsNumericLiteralValue())
126 return To<CSSNumericLiteralValue>(this)->IsLength();
127 return To<CSSMathFunctionValue>(this)->IsLength();
128 }
129
IsPx() const130 bool CSSPrimitiveValue::IsPx() const {
131 if (IsNumericLiteralValue())
132 return To<CSSNumericLiteralValue>(this)->IsPx();
133 return To<CSSMathFunctionValue>(this)->IsPx();
134 }
135
IsNumber() const136 bool CSSPrimitiveValue::IsNumber() const {
137 if (IsNumericLiteralValue())
138 return To<CSSNumericLiteralValue>(this)->IsNumber();
139 return To<CSSMathFunctionValue>(this)->IsNumber();
140 }
141
IsInteger() const142 bool CSSPrimitiveValue::IsInteger() const {
143 // TODO(crbug.com/931216): Support integer math functions properly.
144 return IsNumericLiteralValue() &&
145 To<CSSNumericLiteralValue>(this)->IsInteger();
146 }
147
IsPercentage() const148 bool CSSPrimitiveValue::IsPercentage() const {
149 if (IsNumericLiteralValue())
150 return To<CSSNumericLiteralValue>(this)->IsPercentage();
151 return To<CSSMathFunctionValue>(this)->IsPercentage();
152 }
153
IsTime() const154 bool CSSPrimitiveValue::IsTime() const {
155 if (IsNumericLiteralValue())
156 return To<CSSNumericLiteralValue>(this)->IsTime();
157 return To<CSSMathFunctionValue>(this)->IsTime();
158 }
159
IsComputationallyIndependent() const160 bool CSSPrimitiveValue::IsComputationallyIndependent() const {
161 if (IsNumericLiteralValue())
162 return To<CSSNumericLiteralValue>(this)->IsComputationallyIndependent();
163 return To<CSSMathFunctionValue>(this)->IsComputationallyIndependent();
164 }
165
CSSPrimitiveValue(ClassType class_type)166 CSSPrimitiveValue::CSSPrimitiveValue(ClassType class_type)
167 : CSSValue(class_type) {}
168
169 // static
CreateFromLength(const Length & length,float zoom)170 CSSPrimitiveValue* CSSPrimitiveValue::CreateFromLength(const Length& length,
171 float zoom) {
172 switch (length.GetType()) {
173 case Length::kPercent:
174 return CSSNumericLiteralValue::Create(length.Percent(),
175 UnitType::kPercentage);
176 case Length::kFixed:
177 return CSSNumericLiteralValue::Create(length.Value() / zoom,
178 UnitType::kPixels);
179 case Length::kCalculated: {
180 const CalculationValue& calc = length.GetCalculationValue();
181 if (calc.IsExpression() || (calc.Pixels() && calc.Percent()))
182 return CSSMathFunctionValue::Create(length, zoom);
183 if (!calc.Pixels()) {
184 double num = calc.Percent();
185 if (num < 0 && calc.IsNonNegative())
186 num = 0;
187 return CSSNumericLiteralValue::Create(num, UnitType::kPercentage);
188 }
189 double num = calc.Pixels() / zoom;
190 if (num < 0 && calc.IsNonNegative())
191 num = 0;
192 return CSSNumericLiteralValue::Create(num, UnitType::kPixels);
193 }
194 default:
195 break;
196 }
197 NOTREACHED();
198 return nullptr;
199 }
200
ComputeSeconds() const201 double CSSPrimitiveValue::ComputeSeconds() const {
202 if (IsCalculated())
203 return To<CSSMathFunctionValue>(this)->ComputeSeconds();
204 return To<CSSNumericLiteralValue>(this)->ComputeSeconds();
205 }
206
ComputeDegrees() const207 double CSSPrimitiveValue::ComputeDegrees() const {
208 if (IsCalculated())
209 return To<CSSMathFunctionValue>(this)->ComputeDegrees();
210 return To<CSSNumericLiteralValue>(this)->ComputeDegrees();
211 }
212
ComputeDotsPerPixel() const213 double CSSPrimitiveValue::ComputeDotsPerPixel() const {
214 // TODO(crbug.com/983613): Either support math functions on resolutions; or
215 // provide a justification for not supporting it.
216 DCHECK(IsNumericLiteralValue());
217 return To<CSSNumericLiteralValue>(this)->ComputeDotsPerPixel();
218 }
219
220 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const221 int CSSPrimitiveValue::ComputeLength(
222 const CSSToLengthConversionData& conversion_data) const {
223 return RoundForImpreciseConversion<int>(ComputeLengthDouble(conversion_data));
224 }
225
226 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const227 unsigned CSSPrimitiveValue::ComputeLength(
228 const CSSToLengthConversionData& conversion_data) const {
229 return RoundForImpreciseConversion<unsigned>(
230 ComputeLengthDouble(conversion_data));
231 }
232
233 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const234 Length CSSPrimitiveValue::ComputeLength(
235 const CSSToLengthConversionData& conversion_data) const {
236 return Length::Fixed(
237 ClampToCSSLengthRange(ComputeLengthDouble(conversion_data)));
238 }
239
240 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const241 int16_t CSSPrimitiveValue::ComputeLength(
242 const CSSToLengthConversionData& conversion_data) const {
243 return RoundForImpreciseConversion<int16_t>(
244 ComputeLengthDouble(conversion_data));
245 }
246
247 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const248 uint16_t CSSPrimitiveValue::ComputeLength(
249 const CSSToLengthConversionData& conversion_data) const {
250 return RoundForImpreciseConversion<uint16_t>(
251 ComputeLengthDouble(conversion_data));
252 }
253
254 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const255 uint8_t CSSPrimitiveValue::ComputeLength(
256 const CSSToLengthConversionData& conversion_data) const {
257 return RoundForImpreciseConversion<uint8_t>(
258 ComputeLengthDouble(conversion_data));
259 }
260
261 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const262 float CSSPrimitiveValue::ComputeLength(
263 const CSSToLengthConversionData& conversion_data) const {
264 return clampTo<float>(ComputeLengthDouble(conversion_data));
265 }
266
267 template <>
ComputeLength(const CSSToLengthConversionData & conversion_data) const268 double CSSPrimitiveValue::ComputeLength(
269 const CSSToLengthConversionData& conversion_data) const {
270 return ComputeLengthDouble(conversion_data);
271 }
272
ComputeLengthDouble(const CSSToLengthConversionData & conversion_data) const273 double CSSPrimitiveValue::ComputeLengthDouble(
274 const CSSToLengthConversionData& conversion_data) const {
275 if (IsCalculated())
276 return To<CSSMathFunctionValue>(this)->ComputeLengthPx(conversion_data);
277 return To<CSSNumericLiteralValue>(this)->ComputeLengthPx(conversion_data);
278 }
279
AccumulateLengthArray(CSSLengthArray & length_array,double multiplier) const280 bool CSSPrimitiveValue::AccumulateLengthArray(CSSLengthArray& length_array,
281 double multiplier) const {
282 DCHECK_EQ(length_array.values.size(),
283 static_cast<unsigned>(kLengthUnitTypeCount));
284 if (IsCalculated()) {
285 return To<CSSMathFunctionValue>(this)->AccumulateLengthArray(length_array,
286 multiplier);
287 }
288 return To<CSSNumericLiteralValue>(this)->AccumulateLengthArray(length_array,
289 multiplier);
290 }
291
AccumulateLengthUnitTypes(LengthTypeFlags & types) const292 void CSSPrimitiveValue::AccumulateLengthUnitTypes(
293 LengthTypeFlags& types) const {
294 if (IsCalculated())
295 return To<CSSMathFunctionValue>(this)->AccumulateLengthUnitTypes(types);
296 To<CSSNumericLiteralValue>(this)->AccumulateLengthUnitTypes(types);
297 }
298
ConversionToCanonicalUnitsScaleFactor(UnitType unit_type)299 double CSSPrimitiveValue::ConversionToCanonicalUnitsScaleFactor(
300 UnitType unit_type) {
301 double factor = 1.0;
302 // FIXME: the switch can be replaced by an array of scale factors.
303 switch (unit_type) {
304 // These are "canonical" units in their respective categories.
305 case UnitType::kPixels:
306 case UnitType::kUserUnits:
307 case UnitType::kDegrees:
308 case UnitType::kMilliseconds:
309 case UnitType::kHertz:
310 break;
311 case UnitType::kCentimeters:
312 factor = kCssPixelsPerCentimeter;
313 break;
314 case UnitType::kDotsPerCentimeter:
315 factor = 1 / kCssPixelsPerCentimeter;
316 break;
317 case UnitType::kMillimeters:
318 factor = kCssPixelsPerMillimeter;
319 break;
320 case UnitType::kQuarterMillimeters:
321 factor = kCssPixelsPerQuarterMillimeter;
322 break;
323 case UnitType::kInches:
324 factor = kCssPixelsPerInch;
325 break;
326 case UnitType::kDotsPerInch:
327 factor = 1 / kCssPixelsPerInch;
328 break;
329 case UnitType::kPoints:
330 factor = kCssPixelsPerPoint;
331 break;
332 case UnitType::kPicas:
333 factor = kCssPixelsPerPica;
334 break;
335 case UnitType::kRadians:
336 factor = 180 / kPiDouble;
337 break;
338 case UnitType::kGradians:
339 factor = 0.9;
340 break;
341 case UnitType::kTurns:
342 factor = 360;
343 break;
344 case UnitType::kSeconds:
345 case UnitType::kKilohertz:
346 factor = 1000;
347 break;
348 default:
349 break;
350 }
351
352 return factor;
353 }
354
ConvertToLength(const CSSToLengthConversionData & conversion_data) const355 Length CSSPrimitiveValue::ConvertToLength(
356 const CSSToLengthConversionData& conversion_data) const {
357 if (IsLength())
358 return ComputeLength<Length>(conversion_data);
359 if (IsPercentage()) {
360 if (IsNumericLiteralValue() ||
361 !To<CSSMathFunctionValue>(this)->AllowsNegativePercentageReference()) {
362 return Length::Percent(GetDoubleValue());
363 }
364 }
365 DCHECK(IsCalculated());
366 return To<CSSMathFunctionValue>(this)->ConvertToLength(conversion_data);
367 }
368
GetDoubleValue() const369 double CSSPrimitiveValue::GetDoubleValue() const {
370 return IsCalculated() ? To<CSSMathFunctionValue>(this)->DoubleValue()
371 : To<CSSNumericLiteralValue>(this)->DoubleValue();
372 }
373
IsZero() const374 bool CSSPrimitiveValue::IsZero() const {
375 return IsCalculated() ? To<CSSMathFunctionValue>(this)->IsZero()
376 : To<CSSNumericLiteralValue>(this)->IsZero();
377 }
378
CanonicalUnitTypeForCategory(UnitCategory category)379 CSSPrimitiveValue::UnitType CSSPrimitiveValue::CanonicalUnitTypeForCategory(
380 UnitCategory category) {
381 // The canonical unit type is chosen according to the way
382 // CSSPropertyParser::ValidUnit() chooses the default unit in each category
383 // (based on unitflags).
384 switch (category) {
385 case kUNumber:
386 return UnitType::kNumber;
387 case kULength:
388 return UnitType::kPixels;
389 case kUPercent:
390 return UnitType::kUnknown; // Cannot convert between numbers and percent.
391 case kUTime:
392 return UnitType::kMilliseconds;
393 case kUAngle:
394 return UnitType::kDegrees;
395 case kUFrequency:
396 return UnitType::kHertz;
397 case kUResolution:
398 return UnitType::kDotsPerPixel;
399 default:
400 return UnitType::kUnknown;
401 }
402 }
403
UnitTypeToLengthUnitType(UnitType unit_type,LengthUnitType & length_type)404 bool CSSPrimitiveValue::UnitTypeToLengthUnitType(UnitType unit_type,
405 LengthUnitType& length_type) {
406 switch (unit_type) {
407 case CSSPrimitiveValue::UnitType::kPixels:
408 case CSSPrimitiveValue::UnitType::kCentimeters:
409 case CSSPrimitiveValue::UnitType::kMillimeters:
410 case CSSPrimitiveValue::UnitType::kQuarterMillimeters:
411 case CSSPrimitiveValue::UnitType::kInches:
412 case CSSPrimitiveValue::UnitType::kPoints:
413 case CSSPrimitiveValue::UnitType::kPicas:
414 case CSSPrimitiveValue::UnitType::kUserUnits:
415 length_type = kUnitTypePixels;
416 return true;
417 case CSSPrimitiveValue::UnitType::kEms:
418 case CSSPrimitiveValue::UnitType::kQuirkyEms:
419 length_type = kUnitTypeFontSize;
420 return true;
421 case CSSPrimitiveValue::UnitType::kExs:
422 length_type = kUnitTypeFontXSize;
423 return true;
424 case CSSPrimitiveValue::UnitType::kRems:
425 length_type = kUnitTypeRootFontSize;
426 return true;
427 case CSSPrimitiveValue::UnitType::kChs:
428 length_type = kUnitTypeZeroCharacterWidth;
429 return true;
430 case CSSPrimitiveValue::UnitType::kPercentage:
431 length_type = kUnitTypePercentage;
432 return true;
433 case CSSPrimitiveValue::UnitType::kViewportWidth:
434 length_type = kUnitTypeViewportWidth;
435 return true;
436 case CSSPrimitiveValue::UnitType::kViewportHeight:
437 length_type = kUnitTypeViewportHeight;
438 return true;
439 case CSSPrimitiveValue::UnitType::kViewportMin:
440 length_type = kUnitTypeViewportMin;
441 return true;
442 case CSSPrimitiveValue::UnitType::kViewportMax:
443 length_type = kUnitTypeViewportMax;
444 return true;
445 default:
446 return false;
447 }
448 }
449
LengthUnitTypeToUnitType(LengthUnitType type)450 CSSPrimitiveValue::UnitType CSSPrimitiveValue::LengthUnitTypeToUnitType(
451 LengthUnitType type) {
452 switch (type) {
453 case kUnitTypePixels:
454 return CSSPrimitiveValue::UnitType::kPixels;
455 case kUnitTypeFontSize:
456 return CSSPrimitiveValue::UnitType::kEms;
457 case kUnitTypeFontXSize:
458 return CSSPrimitiveValue::UnitType::kExs;
459 case kUnitTypeRootFontSize:
460 return CSSPrimitiveValue::UnitType::kRems;
461 case kUnitTypeZeroCharacterWidth:
462 return CSSPrimitiveValue::UnitType::kChs;
463 case kUnitTypePercentage:
464 return CSSPrimitiveValue::UnitType::kPercentage;
465 case kUnitTypeViewportWidth:
466 return CSSPrimitiveValue::UnitType::kViewportWidth;
467 case kUnitTypeViewportHeight:
468 return CSSPrimitiveValue::UnitType::kViewportHeight;
469 case kUnitTypeViewportMin:
470 return CSSPrimitiveValue::UnitType::kViewportMin;
471 case kUnitTypeViewportMax:
472 return CSSPrimitiveValue::UnitType::kViewportMax;
473 case kLengthUnitTypeCount:
474 break;
475 }
476 NOTREACHED();
477 return CSSPrimitiveValue::UnitType::kUnknown;
478 }
479
UnitTypeToString(UnitType type)480 const char* CSSPrimitiveValue::UnitTypeToString(UnitType type) {
481 switch (type) {
482 case UnitType::kNumber:
483 case UnitType::kInteger:
484 case UnitType::kUserUnits:
485 return "";
486 case UnitType::kPercentage:
487 return "%";
488 case UnitType::kEms:
489 case UnitType::kQuirkyEms:
490 return "em";
491 case UnitType::kExs:
492 return "ex";
493 case UnitType::kRems:
494 return "rem";
495 case UnitType::kChs:
496 return "ch";
497 case UnitType::kPixels:
498 return "px";
499 case UnitType::kCentimeters:
500 return "cm";
501 case UnitType::kDotsPerPixel:
502 return "dppx";
503 case UnitType::kDotsPerInch:
504 return "dpi";
505 case UnitType::kDotsPerCentimeter:
506 return "dpcm";
507 case UnitType::kMillimeters:
508 return "mm";
509 case UnitType::kQuarterMillimeters:
510 return "q";
511 case UnitType::kInches:
512 return "in";
513 case UnitType::kPoints:
514 return "pt";
515 case UnitType::kPicas:
516 return "pc";
517 case UnitType::kDegrees:
518 return "deg";
519 case UnitType::kRadians:
520 return "rad";
521 case UnitType::kGradians:
522 return "grad";
523 case UnitType::kMilliseconds:
524 return "ms";
525 case UnitType::kSeconds:
526 return "s";
527 case UnitType::kHertz:
528 return "hz";
529 case UnitType::kKilohertz:
530 return "khz";
531 case UnitType::kTurns:
532 return "turn";
533 case UnitType::kFraction:
534 return "fr";
535 case UnitType::kViewportWidth:
536 return "vw";
537 case UnitType::kViewportHeight:
538 return "vh";
539 case UnitType::kViewportMin:
540 return "vmin";
541 case UnitType::kViewportMax:
542 return "vmax";
543 default:
544 break;
545 }
546 NOTREACHED();
547 return "";
548 }
549
CustomCSSText() const550 String CSSPrimitiveValue::CustomCSSText() const {
551 if (IsCalculated())
552 return To<CSSMathFunctionValue>(this)->CustomCSSText();
553 return To<CSSNumericLiteralValue>(this)->CustomCSSText();
554 }
555
TraceAfterDispatch(blink::Visitor * visitor) const556 void CSSPrimitiveValue::TraceAfterDispatch(blink::Visitor* visitor) const {
557 CSSValue::TraceAfterDispatch(visitor);
558 }
559
560 } // namespace blink
561