1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/core/css/css_math_function_value.h"
6
7 #include "third_party/blink/renderer/core/css/css_math_expression_node.h"
8 #include "third_party/blink/renderer/platform/geometry/calculation_expression_node.h"
9 #include "third_party/blink/renderer/platform/geometry/length.h"
10 #include "third_party/blink/renderer/platform/wtf/size_assertions.h"
11 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
12
13 namespace blink {
14
15 struct SameSizeAsCSSMathFunctionValue : CSSPrimitiveValue {
16 Member<void*> expression;
17 };
18 ASSERT_SIZE(CSSMathFunctionValue, SameSizeAsCSSMathFunctionValue);
19
TraceAfterDispatch(blink::Visitor * visitor) const20 void CSSMathFunctionValue::TraceAfterDispatch(blink::Visitor* visitor) const {
21 visitor->Trace(expression_);
22 CSSPrimitiveValue::TraceAfterDispatch(visitor);
23 }
24
CSSMathFunctionValue(const CSSMathExpressionNode * expression,ValueRange range)25 CSSMathFunctionValue::CSSMathFunctionValue(
26 const CSSMathExpressionNode* expression,
27 ValueRange range)
28 : CSSPrimitiveValue(kMathFunctionClass), expression_(expression) {
29 is_non_negative_math_function_ = range == kValueRangeNonNegative;
30 }
31
32 // static
Create(const CSSMathExpressionNode * expression,ValueRange range)33 CSSMathFunctionValue* CSSMathFunctionValue::Create(
34 const CSSMathExpressionNode* expression,
35 ValueRange range) {
36 if (!expression)
37 return nullptr;
38 return MakeGarbageCollected<CSSMathFunctionValue>(expression, range);
39 }
40
41 // static
Create(const Length & length,float zoom)42 CSSMathFunctionValue* CSSMathFunctionValue::Create(const Length& length,
43 float zoom) {
44 DCHECK(length.IsCalculated());
45 auto calc = length.GetCalculationValue().Zoom(1.0 / zoom);
46 return Create(CSSMathExpressionNode::Create(*calc), calc->GetValueRange());
47 }
48
MayHaveRelativeUnit() const49 bool CSSMathFunctionValue::MayHaveRelativeUnit() const {
50 UnitType resolved_type = expression_->ResolvedUnitType();
51 return IsRelativeUnit(resolved_type) || resolved_type == UnitType::kUnknown;
52 }
53
DoubleValue() const54 double CSSMathFunctionValue::DoubleValue() const {
55 #if DCHECK_IS_ON()
56 if (IsPercentage()) {
57 DCHECK(!AllowsNegativePercentageReference() ||
58 !expression_->InvolvesPercentageComparisons());
59 }
60 #endif
61 return ClampToPermittedRange(expression_->DoubleValue());
62 }
63
ComputeSeconds() const64 double CSSMathFunctionValue::ComputeSeconds() const {
65 DCHECK_EQ(kCalcTime, expression_->Category());
66 // TODO(crbug.com/984372): We currently use 'ms' as the canonical unit of
67 // <time>. Switch to 's' to follow the spec.
68 return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit() /
69 1000);
70 }
71
ComputeDegrees() const72 double CSSMathFunctionValue::ComputeDegrees() const {
73 DCHECK_EQ(kCalcAngle, expression_->Category());
74 return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit());
75 }
76
ComputeLengthPx(const CSSToLengthConversionData & conversion_data) const77 double CSSMathFunctionValue::ComputeLengthPx(
78 const CSSToLengthConversionData& conversion_data) const {
79 // |CSSToLengthConversionData| only resolves relative length units, but not
80 // percentages.
81 DCHECK_EQ(kCalcLength, expression_->Category());
82 return ClampToPermittedRange(expression_->ComputeLengthPx(conversion_data));
83 }
84
AccumulateLengthArray(CSSLengthArray & length_array,double multiplier) const85 bool CSSMathFunctionValue::AccumulateLengthArray(CSSLengthArray& length_array,
86 double multiplier) const {
87 return expression_->AccumulateLengthArray(length_array, multiplier);
88 }
89
ConvertToLength(const CSSToLengthConversionData & conversion_data) const90 Length CSSMathFunctionValue::ConvertToLength(
91 const CSSToLengthConversionData& conversion_data) const {
92 if (IsLength())
93 return Length::Fixed(ComputeLengthPx(conversion_data));
94 return Length(ToCalcValue(conversion_data));
95 }
96
BuildCSSText(const String & expression)97 static String BuildCSSText(const String& expression) {
98 StringBuilder result;
99 result.Append("calc");
100 result.Append('(');
101 result.Append(expression);
102 result.Append(')');
103 return result.ToString();
104 }
105
CustomCSSText() const106 String CSSMathFunctionValue::CustomCSSText() const {
107 const String& expression_text = expression_->CustomCSSText();
108 if (expression_->IsMathFunction()) {
109 // If |expression_| is already a math function (e.g., min/max), we don't
110 // need to wrap it in |calc()|.
111 return expression_text;
112 }
113 return BuildCSSText(expression_text);
114 }
115
Equals(const CSSMathFunctionValue & other) const116 bool CSSMathFunctionValue::Equals(const CSSMathFunctionValue& other) const {
117 return DataEquivalent(expression_, other.expression_);
118 }
119
ClampToPermittedRange(double value) const120 double CSSMathFunctionValue::ClampToPermittedRange(double value) const {
121 return IsNonNegative() && value < 0 ? 0 : value;
122 }
123
IsZero() const124 bool CSSMathFunctionValue::IsZero() const {
125 if (expression_->ResolvedUnitType() == UnitType::kUnknown)
126 return false;
127 return expression_->IsZero();
128 }
129
IsPx() const130 bool CSSMathFunctionValue::IsPx() const {
131 // TODO(crbug.com/979895): This is the result of refactoring, which might be
132 // an existing bug. Fix it if necessary.
133 return Category() == kCalcLength;
134 }
135
IsComputationallyIndependent() const136 bool CSSMathFunctionValue::IsComputationallyIndependent() const {
137 return expression_->IsComputationallyIndependent();
138 }
139
ToCalcValue(const CSSToLengthConversionData & conversion_data) const140 scoped_refptr<CalculationValue> CSSMathFunctionValue::ToCalcValue(
141 const CSSToLengthConversionData& conversion_data) const {
142 return expression_->ToCalcValue(conversion_data, PermittedValueRange());
143 }
144
145 } // namespace blink
146