Commit 3ce9c7d3 authored by Darren Shen's avatar Darren Shen Committed by Commit Bot

[css-typed-om] Implement CSSMathValue serialization.

This patch implements the new CSSMathValue serialization algorithm [1].
As explained in the bug, this serialization is impossible to do by
converting to CSSValues and then serializing them, so we have to
implement this from scratch.

We tried to make the code follow the spec algorithm as closely as
possible for easier understanding/verification.

[1] https://drafts.css-houdini.org/css-typed-om-1/#calc-serialization

Bug: 803688
Change-Id: I48987d2b76b0bac55389a014588f3fe40aecf10a
Reviewed-on: https://chromium-review.googlesource.com/896422Reviewed-by: default avatarnainar <nainar@chromium.org>
Commit-Queue: Darren Shen <shend@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533530}
parent 482fd9a3
This is a testharness.js-based test.
PASS CSSMathSum with 1 argument serializes correctly
PASS CSSMathSum with 2 arguments serializes correctly
PASS CSSMathSum with more than 2 arguments serializes correctly
PASS CSSMathProduct with 1 argument serializes correctly
PASS CSSMathProduct with 2 arguments serializes correctly
PASS CSSMathProduct with more than 2 arguments serializes correctly
FAIL CSSMathProduct of two different base types serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
FAIL CSSMathMax with one argument serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
FAIL CSSMathMax with more than one argument serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
FAIL CSSMathMin with one argument serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
FAIL CSSMathMin with more than one argument serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
FAIL CSSMathNegate serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
FAIL CSSMathInvert serializes correctly Failed to execute 'toString' on 'CSSStyleValue': Some CSSMathValues can't be serialized yet. See crbug.com/803739
Harness: the test ran to completion.
<!doctype html>
<meta charset="utf-8">
<title>IDL-constructed CSSMathValue serialization tests</title>
<!-- Tentative because this depends on css-values-4 spec, which is still WIP:
https://drafts.csswg.org/css-values-4/#calc-notation -->
<link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#calc-serialization">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
......@@ -12,69 +10,104 @@
const gTestCases = [
{
description: 'CSSMathSum with 1 argument',
value: new CSSMathSum(CSS.px(1)),
cssText: 'calc(1px)',
description: 'CSSMathMax with one argument',
value: new CSSMathMax(1),
cssText: 'max(1)',
},
{
description: 'CSSMathSum with 2 arguments',
value: new CSSMathSum(CSS.px(1), CSS.px(2)),
cssText: 'calc(1px + 2px)',
description: 'CSSMathMax with more than one argument',
value: new CSSMathMax(1, 2, 3),
cssText: 'max(1, 2, 3)',
},
{
description: 'CSSMathSum with more than 2 arguments',
value: new CSSMathSum(CSS.px(1), CSS.px(2), CSS.px(3)),
cssText: 'calc((1px + 2px) + 3px)',
description: 'CSSMathMax containing nested CSSMathValues',
value: new CSSMathMax(new CSSMathSum(1, 2), 3),
cssText: 'max(1 + 2, 3)',
},
{
description: 'CSSMathProduct with 1 argument',
value: new CSSMathProduct(CSS.px(1)),
cssText: 'calc(1px)',
description: 'CSSMathMin with one argument',
value: new CSSMathMin(1),
cssText: 'min(1)',
},
{
description: 'CSSMathProduct with 2 arguments',
value: new CSSMathProduct(CSS.px(1), 2),
cssText: 'calc(1px * 2)',
description: 'CSSMathMin with more than one argument',
value: new CSSMathMin(1, 2, 3),
cssText: 'min(1, 2, 3)',
},
{
description: 'CSSMathProduct with more than 2 arguments',
value: new CSSMathProduct(CSS.px(1), 2, 3),
cssText: 'calc((1px * 2) * 3)',
description: 'CSSMathMin containing nested CSSMathValues',
value: new CSSMathMin(new CSSMathSum(1, 2), 3),
cssText: 'min(1 + 2, 3)',
},
{
description: 'CSSMathProduct of two different base types',
value: new CSSMathProduct(CSS.px(1), CSS.s(1)),
cssText: 'calc(1px * 1s)',
description: 'CSSMathSum with one argument',
value: new CSSMathSum(1),
cssText: 'calc(1)',
},
{
description: 'CSSMathMax with one argument',
value: new CSSMathMax(CSS.px(2)),
cssText: 'max(1px)',
description: 'CSSMathSum with more than one argument',
value: new CSSMathSum(1, 2, 3),
cssText: 'calc(1 + 2 + 3)',
},
{
description: 'CSSMathMax with more than one argument',
value: new CSSMathMax(CSS.px(2), CSS.px(1)),
cssText: 'max(1px, 1s)',
description: 'CSSMathSum with a CSSMathNegate as first value',
value: new CSSMathSum(new CSSMathNegate(1), 2, 3),
cssText: 'calc((-1) + 2 + 3)',
},
{
description: 'CSSMathMin with one argument',
value: new CSSMathMin(CSS.px(1)),
cssText: 'min(1px)',
description: 'CSSMathSum containing a CSSMathNegate after first value',
value: new CSSMathSum(1, new CSSMathNegate(2), 3),
cssText: 'calc(1 - 2 + 3)',
},
{
description: 'CSSMathMin with more than one argument',
value: new CSSMathMin(CSS.px(1), CSS.px(2)),
cssText: 'min(1px, 2px)',
description: 'CSSMathSum nested inside a CSSMathValue',
value: new CSSMathSum(new CSSMathSum(1, 2), 3),
cssText: 'calc((1 + 2) + 3)',
},
{
description: 'CSSMathNegate',
value: new CSSMathNegate(CSS.px(1)),
cssText: 'calc(- 1px)',
value: new CSSMathNegate(1),
cssText: 'calc(-1)',
},
{
description: 'CSSMathNegate nested inside a CSSMathValue',
value: new CSSMathProduct(new CSSMathNegate(1)),
cssText: 'calc((-1))',
},
{
description: 'CSSMathProduct with one argument',
value: new CSSMathProduct(1),
cssText: 'calc(1)',
},
{
description: 'CSSMathProduct with more than one argument',
value: new CSSMathProduct(1, 2, 3),
cssText: 'calc(1 * 2 * 3)',
},
{
description: 'CSSMathProduct with a CSSMathInvert as first value',
value: new CSSMathProduct(new CSSMathInvert(1), 2, 3),
cssText: 'calc((1 / 1) * 2 * 3)',
},
{
description: 'CSSMathProduct containing a CSSMathInvert after first value',
value: new CSSMathProduct(1, new CSSMathInvert(2), 3),
cssText: 'calc(1 / 2 * 3)',
},
{
description: 'CSSMathProduct nested inside a CSSMathValue',
value: new CSSMathProduct(new CSSMathProduct(1, 2), 3),
cssText: 'calc((1 * 2) * 3)',
},
{
description: 'CSSMathInvert',
value: new CSSMathInvert(CSS.px(1)),
cssText: 'calc(1 / 1px)',
value: new CSSMathInvert(1),
cssText: 'calc(1 / 1)',
},
{
description: 'CSSMathInvert nested inside a CSSMathValue',
value: new CSSMathSum(new CSSMathInvert(1)),
cssText: 'calc((1 / 1))',
},
];
......
......@@ -20,4 +20,17 @@ WTF::Optional<CSSNumericSumValue> CSSMathInvert::SumValue() const {
return sum;
}
void CSSMathInvert::BuildCSSText(Nested nested,
ParenLess paren_less,
StringBuilder& result) const {
if (paren_less == ParenLess::kNo)
result.Append(nested == Nested::kYes ? "(" : "calc(");
result.Append("1 / ");
value_->BuildCSSText(Nested::kYes, ParenLess::kNo, result);
if (paren_less == ParenLess::kNo)
result.Append(")");
}
} // namespace blink
......@@ -30,6 +30,9 @@ class CORE_EXPORT CSSMathInvert : public CSSMathValue {
void value(CSSNumberish& value) { value.SetCSSNumericValue(value_); }
// Blink-internal methods
const CSSNumericValue& Value() const { return *value_; }
// From CSSStyleValue.
StyleValueType GetType() const final { return CSSStyleValue::kInvertType; }
......@@ -60,6 +63,8 @@ class CORE_EXPORT CSSMathInvert : public CSSMathValue {
CSSNumericValue* Invert() final { return value_.Get(); }
WTF::Optional<CSSNumericSumValue> SumValue() const final;
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
Member<CSSNumericValue> value_;
DISALLOW_COPY_AND_ASSIGN(CSSMathInvert);
};
......
......@@ -50,4 +50,20 @@ WTF::Optional<CSSNumericSumValue> CSSMathMax::SumValue() const {
return cur_max;
}
void CSSMathMax::BuildCSSText(Nested, ParenLess, StringBuilder& result) const {
result.Append("max(");
bool first_iteration = true;
for (const auto& value : NumericValues()) {
if (!first_iteration)
result.Append(", ");
first_iteration = false;
DCHECK(value);
value->BuildCSSText(Nested::kYes, ParenLess::kYes, result);
}
result.Append(")");
}
} // namespace blink
......@@ -36,6 +36,8 @@ class CORE_EXPORT CSSMathMax final : public CSSMathVariadic {
CSSMathMax(CSSNumericArray* values, const CSSNumericValueType& type)
: CSSMathVariadic(values, type) {}
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
WTF::Optional<CSSNumericSumValue> SumValue() const final;
DISALLOW_COPY_AND_ASSIGN(CSSMathMax);
};
......
......@@ -50,4 +50,19 @@ WTF::Optional<CSSNumericSumValue> CSSMathMin::SumValue() const {
return cur_min;
}
void CSSMathMin::BuildCSSText(Nested, ParenLess, StringBuilder& result) const {
result.Append("min(");
bool first_iteration = true;
for (const auto& value : NumericValues()) {
if (!first_iteration)
result.Append(", ");
first_iteration = false;
value->BuildCSSText(Nested::kYes, ParenLess::kYes, result);
}
result.Append(")");
}
} // namespace blink
......@@ -38,6 +38,8 @@ class CORE_EXPORT CSSMathMin final : public CSSMathVariadic {
CSSMathMin(CSSNumericArray* values, const CSSNumericValueType& type)
: CSSMathVariadic(values, type) {}
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
WTF::Optional<CSSNumericSumValue> SumValue() const final;
DISALLOW_COPY_AND_ASSIGN(CSSMathMin);
};
......
......@@ -18,4 +18,17 @@ WTF::Optional<CSSNumericSumValue> CSSMathNegate::SumValue() const {
return maybe_sum;
}
void CSSMathNegate::BuildCSSText(Nested nested,
ParenLess paren_less,
StringBuilder& result) const {
if (paren_less == ParenLess::kNo)
result.Append(nested == Nested::kYes ? "(" : "calc(");
result.Append("-");
value_->BuildCSSText(Nested::kYes, ParenLess::kNo, result);
if (paren_less == ParenLess::kNo)
result.Append(")");
}
} // namespace blink
......@@ -29,6 +29,9 @@ class CORE_EXPORT CSSMathNegate : public CSSMathValue {
void value(CSSNumberish& value) { value.SetCSSNumericValue(value_); }
// Blink-internal methods
const CSSNumericValue& Value() const { return *value_; }
// From CSSStyleValue.
StyleValueType GetType() const final { return CSSStyleValue::kNegateType; }
......@@ -59,6 +62,8 @@ class CORE_EXPORT CSSMathNegate : public CSSMathValue {
CSSNumericValue* Negate() final { return value_.Get(); }
WTF::Optional<CSSNumericSumValue> SumValue() const final;
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
Member<CSSNumericValue> value_;
DISALLOW_COPY_AND_ASSIGN(CSSMathNegate);
};
......
......@@ -5,6 +5,7 @@
#include "core/css/cssom/CSSMathProduct.h"
#include "core/css/CSSCalculationValue.h"
#include "core/css/cssom/CSSMathInvert.h"
namespace blink {
......@@ -94,4 +95,30 @@ CSSCalcExpressionNode* CSSMathProduct::ToCalcExpressionNode() const {
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 (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
......@@ -33,6 +33,8 @@ class CORE_EXPORT CSSMathProduct final : public CSSMathVariadic {
CSSMathProduct(CSSNumericArray* values, const CSSNumericValueType& type)
: CSSMathVariadic(values, type) {}
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
WTF::Optional<CSSNumericSumValue> SumValue() const final;
DISALLOW_COPY_AND_ASSIGN(CSSMathProduct);
};
......
......@@ -5,6 +5,7 @@
#include "core/css/cssom/CSSMathSum.h"
#include "core/css/CSSCalculationValue.h"
#include "core/css/cssom/CSSMathNegate.h"
namespace blink {
......@@ -112,4 +113,30 @@ CSSCalcExpressionNode* CSSMathSum::ToCalcExpressionNode() const {
return node;
}
void CSSMathSum::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 (size_t i = 1; i < values.size(); i++) {
const auto& arg = *values[i];
if (arg.GetType() == CSSStyleValue::kNegateType) {
result.Append(" - ");
static_cast<const CSSMathNegate&>(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
......@@ -33,6 +33,8 @@ class CORE_EXPORT CSSMathSum final : public CSSMathVariadic {
CSSMathSum(CSSNumericArray* values, const CSSNumericValueType& type)
: CSSMathVariadic(values, type) {}
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
WTF::Optional<CSSNumericSumValue> SumValue() const final;
DISALLOW_COPY_AND_ASSIGN(CSSMathSum);
};
......
......@@ -465,6 +465,12 @@ bool CSSNumericValue::equals(const HeapVector<CSSNumberish>& args) {
[this](const auto& v) { return this->Equals(*v); });
}
String CSSNumericValue::toString() const {
StringBuilder result;
BuildCSSText(Nested::kNo, ParenLess::kNo, result);
return result.ToString();
}
CSSNumericValue* CSSNumericValue::Negate() {
return CSSMathNegate::Create(this);
}
......
......@@ -52,6 +52,8 @@ class CORE_EXPORT CSSNumericValue : public CSSStyleValue {
void type(CSSNumericType&) const;
String toString() const final;
// Internal methods.
// Arithmetic
virtual CSSNumericValue* Negate();
......@@ -67,6 +69,10 @@ class CORE_EXPORT CSSNumericValue : public CSSStyleValue {
virtual CSSCalcExpressionNode* ToCalcExpressionNode() const = 0;
enum class Nested : bool { kYes, kNo };
enum class ParenLess : bool { kYes, kNo };
virtual void BuildCSSText(Nested, ParenLess, StringBuilder&) const = 0;
protected:
static bool IsValidUnit(CSSPrimitiveValue::UnitType);
static CSSPrimitiveValue::UnitType UnitFromName(const String& name);
......
......@@ -73,15 +73,9 @@ Optional<CSSStyleValueVector> CSSStyleValue::parseAll(
return style_value_vector;
}
String CSSStyleValue::toString(ExceptionState& exception_state) const {
String CSSStyleValue::toString() const {
const CSSValue* result = ToCSSValue();
// TODO(crbug.com/803739): Remove this once all CSSStyleValues
// support toCSSValue().
if (!result) {
exception_state.ThrowTypeError(
"Some CSSMathValues can't be serialized yet. See crbug.com/803739");
return "";
}
DCHECK(result);
return result->CssText();
}
......
......@@ -70,7 +70,7 @@ class CORE_EXPORT CSSStyleValue : public ScriptWrappable {
virtual const CSSValue* ToCSSValueWithProperty(CSSPropertyID) const {
return ToCSSValue();
}
virtual String toString(ExceptionState&) const;
virtual String toString() const;
protected:
static String StyleValueTypeToString(StyleValueType);
......
......@@ -9,7 +9,7 @@
[
Exposed(Window CSSTypedOM, PaintWorklet CSSPaintAPI)
] interface CSSStyleValue {
[RaisesException] stringifier;
stringifier;
// Putting Exposed=Window in the next line makes |parse| not exposed to PaintWorklet.
[RaisesException, Exposed=Window, CallWith=ExecutionContext] static CSSStyleValue? parse(DOMString property, DOMString cssText);
[RaisesException, Exposed=Window, CallWith=ExecutionContext] static sequence<CSSStyleValue>? parseAll(DOMString property, DOMString cssText);
......
......@@ -160,4 +160,10 @@ CSSNumericValue* CSSUnitValue::Invert() {
return CSSMathInvert::Create(this);
}
void CSSUnitValue::BuildCSSText(Nested,
ParenLess,
StringBuilder& result) const {
result.Append(ToCSSValue()->CssText());
}
} // namespace blink
......@@ -62,6 +62,8 @@ class CORE_EXPORT CSSUnitValue final : public CSSNumericValue {
double ConvertFixedLength(CSSPrimitiveValue::UnitType) const;
double ConvertAngle(CSSPrimitiveValue::UnitType) const;
void BuildCSSText(Nested, ParenLess, StringBuilder&) const final;
// From CSSNumericValue
CSSNumericValue* Negate() final;
CSSNumericValue* Invert() final;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment