blob: 371dc2fed10c1d66c0c257796dcba487860679e5 [file] [log] [blame]
// Copyright 2017 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/cssom/css_math_product.h"
#include "third_party/blink/renderer/core/css/css_math_expression_node.h"
#include "third_party/blink/renderer/core/css/cssom/css_math_invert.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
namespace {
CSSNumericSumValue::UnitMap MultiplyUnitMaps(
CSSNumericSumValue::UnitMap a,
const CSSNumericSumValue::UnitMap& b) {
for (const auto& unit_exponent : b) {
DCHECK_NE(unit_exponent.value, 0);
const auto old_value =
a.Contains(unit_exponent.key) ? a.at(unit_exponent.key) : 0;
// Remove any zero entries
if (old_value + unit_exponent.value == 0)
a.erase(unit_exponent.key);
else
a.Set(unit_exponent.key, old_value + unit_exponent.value);
}
return a;
}
} // namespace
CSSMathProduct* CSSMathProduct::Create(const HeapVector<CSSNumberish>& args,
ExceptionState& exception_state) {
if (args.IsEmpty()) {
exception_state.ThrowDOMException(DOMExceptionCode::kSyntaxError,
"Arguments can't be empty");
return nullptr;
}
CSSMathProduct* result = Create(CSSNumberishesToNumericValues(args));
if (!result) {
exception_state.ThrowTypeError("Incompatible types");
return nullptr;
}
return result;
}
CSSMathProduct* CSSMathProduct::Create(CSSNumericValueVector values) {
bool error = false;
CSSNumericValueType final_type =
CSSMathVariadic::TypeCheck(values, CSSNumericValueType::Multiply, error);
return error ? nullptr
: MakeGarbageCollected<CSSMathProduct>(
MakeGarbageCollected<CSSNumericArray>(std::move(values)),
final_type);
}
base::Optional<CSSNumericSumValue> CSSMathProduct::SumValue() const {
CSSNumericSumValue sum;
// Start with the number '1', which is the multiplicative identity.
sum.terms.push_back(CSSNumericSumValue::Term{1, {}});
for (const auto& value : NumericValues()) {
const auto child_sum = value->SumValue();
if (!child_sum)
return base::nullopt;
CSSNumericSumValue new_sum;
for (const auto& a : sum.terms) {
for (const auto& b : child_sum->terms) {
new_sum.terms.emplace_back(a.value * b.value,
MultiplyUnitMaps(a.units, b.units));
}
}
sum = new_sum;
}
return sum;
}
CSSMathExpressionNode* CSSMathProduct::ToCalcExpressionNode() const {
// TODO(crbug.com/782103): Handle the single value case correctly.
if (NumericValues().size() == 1)
return NumericValues()[0]->ToCalcExpressionNode();
CSSMathExpressionNode* node = CSSMathExpressionBinaryOperation::Create(
NumericValues()[0]->ToCalcExpressionNode(),
NumericValues()[1]->ToCalcExpressionNode(), CSSMathOperator::kMultiply);
for (wtf_size_t i = 2; i < NumericValues().size(); i++) {
node = CSSMathExpressionBinaryOperation::Create(
node, NumericValues()[i]->ToCalcExpressionNode(),
CSSMathOperator::kMultiply);
}
return node;
}
void CSSMathProduct::BuildCSSText(Nested nested,
ParenLess paren_less,
StringBuilder& result) const {
if (paren_less == ParenLess::kNo)
result.Append(nested == Nested::kYes ? "(" : "calc(");
const auto& values = NumericValues();
DCHECK(!values.IsEmpty());
values[0]->BuildCSSText(Nested::kYes, ParenLess::kNo, result);
for (wtf_size_t i = 1; i < values.size(); i++) {
const auto& arg = *values[i];
if (arg.GetType() == CSSStyleValue::kInvertType) {
result.Append(" / ");
static_cast<const CSSMathInvert&>(arg).Value().BuildCSSText(
Nested::kYes, ParenLess::kNo, result);
} else {
result.Append(" * ");
arg.BuildCSSText(Nested::kYes, ParenLess::kNo, result);
}
}
if (paren_less == ParenLess::kNo)
result.Append(")");
}
} // namespace blink