blob: 195407bf2142ce1e88ea3ed75975f34817943aed [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/css/css_math_function_value.h"
#include "third_party/blink/renderer/core/css/css_math_expression_node.h"
#include "third_party/blink/renderer/core/css/css_value_clamping_utils.h"
#include "third_party/blink/renderer/platform/geometry/calculation_expression_node.h"
#include "third_party/blink/renderer/platform/geometry/length.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
struct SameSizeAsCSSMathFunctionValue : CSSPrimitiveValue {
Member<void*> expression;
};
ASSERT_SIZE(CSSMathFunctionValue, SameSizeAsCSSMathFunctionValue);
void CSSMathFunctionValue::TraceAfterDispatch(blink::Visitor* visitor) const {
visitor->Trace(expression_);
CSSPrimitiveValue::TraceAfterDispatch(visitor);
}
CSSMathFunctionValue::CSSMathFunctionValue(
const CSSMathExpressionNode* expression,
ValueRange range)
: CSSPrimitiveValue(kMathFunctionClass), expression_(expression) {
is_non_negative_math_function_ = range == kValueRangeNonNegative;
}
// static
CSSMathFunctionValue* CSSMathFunctionValue::Create(
const CSSMathExpressionNode* expression,
ValueRange range) {
if (!expression)
return nullptr;
return MakeGarbageCollected<CSSMathFunctionValue>(expression, range);
}
// static
CSSMathFunctionValue* CSSMathFunctionValue::Create(const Length& length,
float zoom) {
DCHECK(length.IsCalculated());
auto calc = length.GetCalculationValue().Zoom(1.0 / zoom);
return Create(CSSMathExpressionNode::Create(*calc), calc->GetValueRange());
}
bool CSSMathFunctionValue::MayHaveRelativeUnit() const {
UnitType resolved_type = expression_->ResolvedUnitType();
return IsRelativeUnit(resolved_type) || resolved_type == UnitType::kUnknown;
}
double CSSMathFunctionValue::DoubleValue() const {
#if DCHECK_IS_ON()
if (IsPercentage()) {
DCHECK(!AllowsNegativePercentageReference() ||
!expression_->InvolvesPercentageComparisons());
}
#endif
return ClampToPermittedRange(expression_->DoubleValue());
}
double CSSMathFunctionValue::ComputeSeconds() const {
DCHECK_EQ(kCalcTime, expression_->Category());
// TODO(crbug.com/984372): We currently use 'ms' as the canonical unit of
// <time>. Switch to 's' to follow the spec.
return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit() /
1000);
}
double CSSMathFunctionValue::ComputeDegrees() const {
DCHECK_EQ(kCalcAngle, expression_->Category());
return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit());
}
double CSSMathFunctionValue::ComputeLengthPx(
const CSSToLengthConversionData& conversion_data) const {
// |CSSToLengthConversionData| only resolves relative length units, but not
// percentages.
DCHECK_EQ(kCalcLength, expression_->Category());
return ClampToPermittedRange(expression_->ComputeLengthPx(conversion_data));
}
bool CSSMathFunctionValue::AccumulateLengthArray(CSSLengthArray& length_array,
double multiplier) const {
return expression_->AccumulateLengthArray(length_array, multiplier);
}
Length CSSMathFunctionValue::ConvertToLength(
const CSSToLengthConversionData& conversion_data) const {
if (IsLength())
return Length::Fixed(ComputeLengthPx(conversion_data));
return Length(ToCalcValue(conversion_data));
}
static String BuildCSSText(const String& expression) {
StringBuilder result;
result.Append("calc");
result.Append('(');
result.Append(expression);
result.Append(')');
return result.ToString();
}
String CSSMathFunctionValue::CustomCSSText() const {
const String& expression_text = expression_->CustomCSSText();
if (expression_->IsMathFunction()) {
// If |expression_| is already a math function (e.g., min/max), we don't
// need to wrap it in |calc()|.
return expression_text;
}
return BuildCSSText(expression_text);
}
bool CSSMathFunctionValue::Equals(const CSSMathFunctionValue& other) const {
return DataEquivalent(expression_, other.expression_);
}
double CSSMathFunctionValue::ClampToPermittedRange(double value) const {
return IsNonNegative() && value < 0 ? 0 : value;
}
bool CSSMathFunctionValue::IsZero() const {
if (expression_->ResolvedUnitType() == UnitType::kUnknown)
return false;
return expression_->IsZero();
}
bool CSSMathFunctionValue::IsPx() const {
// TODO(crbug.com/979895): This is the result of refactoring, which might be
// an existing bug. Fix it if necessary.
return Category() == kCalcLength;
}
bool CSSMathFunctionValue::IsComputationallyIndependent() const {
return expression_->IsComputationallyIndependent();
}
scoped_refptr<CalculationValue> CSSMathFunctionValue::ToCalcValue(
const CSSToLengthConversionData& conversion_data) const {
return expression_->ToCalcValue(conversion_data, PermittedValueRange(),
AllowsNegativePercentageReference());
}
} // namespace blink