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