Skip to content
Permalink
Browse files
Add support for CSSNumericValue.toSum()
https://bugs.webkit.org/show_bug.cgi?id=246673

Reviewed by Geoffrey Garen.

Add support for CSSNumericValue.toSum():
- https://drafts.css-houdini.org/css-typed-om/#dom-cssnumericvalue-tosum

* LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/stylevalue-subclasses/cssTransformComponent-toMatrix-relative-units-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-typed-om/stylevalue-subclasses/numeric-objects/toSum.tentative-expected.txt:
Rebaseline WPT tests now that more checks are passing.

* Source/WebCore/css/CSSUnits.cpp:
(WebCore::unitCategory):
Match the categories more closely with the specification. It is particularly important to
split the different types of lengths. "em" for example is a font-relative length. We used
to have it with the absolute lengths and would thus incorrectly convert it to "px" which
is the canonical unit for absolute lengths.

(WebCore::canonicalUnitTypeForCategory):
(WebCore::operator<<):
* Source/WebCore/css/CSSUnits.h:

* Source/WebCore/css/typedom/CSSNumericValue.cpp:
(WebCore::createCSSUnitValueFromAddend):
Properly implement:
- https://drafts.css-houdini.org/css-typed-om/#create-a-cssunitvalue-from-a-sum-value-item
and factor to its own function for reuse. In particular, we were supposed to fail if the
unit exponent is not 1.

(WebCore::CSSNumericValue::to):
Factor out some code to createCSSUnitValueFromAddend().

(WebCore::CSSNumericValue::toSum):
Implement as per:
- - https://drafts.css-houdini.org/css-typed-om/#dom-cssnumericvalue-tosum

* Source/WebCore/css/typedom/CSSUnitValue.cpp:
(WebCore::CSSUnitValue::toSumValue const):
* Source/WebCore/css/typedom/numeric/CSSNumericType.cpp:
(WebCore::CSSNumericType::create):

Canonical link: https://commits.webkit.org/255679@main
  • Loading branch information
cdumez committed Oct 18, 2022
1 parent eda4d9c commit e3fb2936dcb722b7844f67bb955012b62777be8d
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 70 deletions.
@@ -1,10 +1,5 @@

FAIL CSSTranslate.toMatrix() containing relative units throws TypeError assert_throws_js: function "() => {
return new CSSTranslate(
new CSSUnitValue(1, 'px'),
new CSSUnitValue(1, 'em')
).toMatrix();
}" did not throw
PASS CSSTranslate.toMatrix() containing relative units throws TypeError
FAIL CSSPerspective.toMatrix() containing relative units throws TypeError assert_throws_js: function "() => {
return new CSSPerspective(new CSSUnitValue(1, 'em')).toMatrix();
}" did not throw
@@ -1,13 +1,13 @@

FAIL Converting a CSSNumericValue to a sum with invalid units throws SyntaxError assert_throws_dom: function "() => CSS.px(1).toSum('px', 'lemon')" did not throw
PASS Converting a CSSNumericValue to a sum with invalid units throws SyntaxError
PASS Converting a CSSNumericValue with an invalid sum value to a sum throws TypeError
FAIL Converting a CSSNumericValue with compound units to a sum throws TypeError assert_throws_js: function "() => new CSSMathProduct(CSS.px(1), CSS.px(1)).to('px')" did not throw
FAIL Converting a CSSNumericValue to a sum with an incompatible unit throws TypeError assert_throws_js: function "() => CSS.px(1).toSum('number')" did not throw
FAIL Converting a CSSNumericValue to a sum with units that are not addable throws TypeError assert_throws_js: function "() => CSS.px(1).toSum('px', 's')" did not throw
FAIL Converting a CSSNumericValue with leftover units to a sum throws TypeError assert_throws_js: function "() => new CSSMathSum(CSS.px(1), CSS.em(1)).toSum('px')" did not throw
FAIL Converting CSSNumericValue to a sum with its own unit returns itself assert_equals: expected "px" but got "number"
FAIL Converting CSSNumericValue to a sum with no arguments returns all the units in sorted order assert_equals: expected 4 but got 1
FAIL Converting CSSNumericValue to a sum with a relative unit converts correctly assert_approx_equals: expected 20 +/- 0.000001 but got 1
FAIL Converting CSSNumericValue to a sum containing extra units returns zero for those units assert_equals: expected 3 but got 1
FAIL CSSNumericValue.toSum converts greedily assert_equals: expected 2 but got 1
PASS Converting a CSSNumericValue with compound units to a sum throws TypeError
PASS Converting a CSSNumericValue to a sum with an incompatible unit throws TypeError
PASS Converting a CSSNumericValue to a sum with units that are not addable throws TypeError
PASS Converting a CSSNumericValue with leftover units to a sum throws TypeError
PASS Converting CSSNumericValue to a sum with its own unit returns itself
PASS Converting CSSNumericValue to a sum with no arguments returns all the units in sorted order
PASS Converting CSSNumericValue to a sum with a relative unit converts correctly
PASS Converting CSSNumericValue to a sum containing extra units returns zero for those units
PASS CSSNumericValue.toSum converts greedily

@@ -33,67 +33,78 @@ CSSUnitCategory unitCategory(CSSUnitType type)
return CSSUnitCategory::Number;
case CSSUnitType::CSS_PERCENTAGE:
return CSSUnitCategory::Percent;
// https://drafts.csswg.org/css-values-4/#absolute-lengths
case CSSUnitType::CSS_PX:
case CSSUnitType::CSS_CM:
case CSSUnitType::CSS_MM:
case CSSUnitType::CSS_IN:
case CSSUnitType::CSS_PT:
case CSSUnitType::CSS_PC:
case CSSUnitType::CSS_Q:
case CSSUnitType::CSS_IC:
return CSSUnitCategory::AbsoluteLength;
// https://drafts.csswg.org/css-values-4/#font-relative-lengths
case CSSUnitType::CSS_EMS:
case CSSUnitType::CSS_REMS:
case CSSUnitType::CSS_EXS:
case CSSUnitType::CSS_CHS:
case CSSUnitType::CSS_REMS:
case CSSUnitType::CSS_IC:
case CSSUnitType::CSS_LHS:
case CSSUnitType::CSS_RLHS:
return CSSUnitCategory::FontRelativeLength;
// https://drafts.csswg.org/css-values-4/#viewport-relative-lengths
case CSSUnitType::CSS_VW:
case CSSUnitType::CSS_VH:
case CSSUnitType::CSS_VMIN:
case CSSUnitType::CSS_VMAX:
case CSSUnitType::CSS_VB:
case CSSUnitType::CSS_VI:
case CSSUnitType::CSS_SVW:
case CSSUnitType::CSS_SVH:
case CSSUnitType::CSS_SVMIN:
case CSSUnitType::CSS_SVMAX:
case CSSUnitType::CSS_SVB:
case CSSUnitType::CSS_SVI:
case CSSUnitType::CSS_LVW:
case CSSUnitType::CSS_LVH:
case CSSUnitType::CSS_LVMIN:
case CSSUnitType::CSS_LVMAX:
case CSSUnitType::CSS_LVB:
case CSSUnitType::CSS_LVI:
case CSSUnitType::CSS_DVW:
case CSSUnitType::CSS_VH:
case CSSUnitType::CSS_SVH:
case CSSUnitType::CSS_LVH:
case CSSUnitType::CSS_DVH:
case CSSUnitType::CSS_VI:
case CSSUnitType::CSS_SVI:
case CSSUnitType::CSS_LVI:
case CSSUnitType::CSS_DVI:
case CSSUnitType::CSS_VB:
case CSSUnitType::CSS_SVB:
case CSSUnitType::CSS_LVB:
case CSSUnitType::CSS_DVB:
case CSSUnitType::CSS_VMIN:
case CSSUnitType::CSS_LVMIN:
case CSSUnitType::CSS_SVMIN:
case CSSUnitType::CSS_DVMIN:
case CSSUnitType::CSS_VMAX:
case CSSUnitType::CSS_SVMAX:
case CSSUnitType::CSS_LVMAX:
case CSSUnitType::CSS_DVMAX:
case CSSUnitType::CSS_DVB:
case CSSUnitType::CSS_DVI:
case CSSUnitType::CSS_CQW:
case CSSUnitType::CSS_CQH:
case CSSUnitType::CSS_CQI:
case CSSUnitType::CSS_CQB:
case CSSUnitType::CSS_CQMIN:
case CSSUnitType::CSS_CQMAX:
return CSSUnitCategory::Length;
return CSSUnitCategory::ViewportPercentageLength;
// https://drafts.csswg.org/css-values-4/#time
case CSSUnitType::CSS_MS:
case CSSUnitType::CSS_S:
return CSSUnitCategory::Time;
// https://drafts.csswg.org/css-values-4/#angles
case CSSUnitType::CSS_DEG:
case CSSUnitType::CSS_RAD:
case CSSUnitType::CSS_GRAD:
case CSSUnitType::CSS_TURN:
return CSSUnitCategory::Angle;
// https://drafts.csswg.org/css-values-4/#frequency
case CSSUnitType::CSS_HZ:
case CSSUnitType::CSS_KHZ:
return CSSUnitCategory::Frequency;
// https://drafts.csswg.org/css-values-4/#resolution
case CSSUnitType::CSS_DPPX:
case CSSUnitType::CSS_X:
case CSSUnitType::CSS_DPI:
case CSSUnitType::CSS_DPCM:
return CSSUnitCategory::Resolution;
case CSSUnitType::CSS_FR:
return CSSUnitCategory::Flex;
case CSSUnitType::CSS_CQW:
case CSSUnitType::CSS_CQH:
case CSSUnitType::CSS_CQI:
case CSSUnitType::CSS_CQB:
case CSSUnitType::CSS_CQMIN:
case CSSUnitType::CSS_CQMAX:
case CSSUnitType::CSS_ATTR:
case CSSUnitType::CSS_CALC:
case CSSUnitType::CSS_CALC_PERCENTAGE_WITH_LENGTH:
@@ -102,7 +113,6 @@ CSSUnitCategory unitCategory(CSSUnitType type)
case CSSUnitType::CSS_COUNTER_NAME:
case CSSUnitType::CSS_DIMENSION:
case CSSUnitType::CSS_FONT_FAMILY:
case CSSUnitType::CSS_FR:
case CSSUnitType::CSS_IDENT:
case CSSUnitType::CSS_PAIR:
case CSSUnitType::CSS_PROPERTY_ID:
@@ -128,7 +138,7 @@ CSSUnitType canonicalUnitTypeForCategory(CSSUnitCategory category)
switch (category) {
case CSSUnitCategory::Number:
return CSSUnitType::CSS_NUMBER;
case CSSUnitCategory::Length:
case CSSUnitCategory::AbsoluteLength:
return CSSUnitType::CSS_PX;
case CSSUnitCategory::Percent:
return CSSUnitType::CSS_UNKNOWN; // Cannot convert between numbers and percent.
@@ -140,6 +150,10 @@ CSSUnitType canonicalUnitTypeForCategory(CSSUnitCategory category)
return CSSUnitType::CSS_HZ;
case CSSUnitCategory::Resolution:
return CSSUnitType::CSS_DPPX;
case CSSUnitCategory::Flex:
return CSSUnitType::CSS_FR;
case CSSUnitCategory::FontRelativeLength:
case CSSUnitCategory::ViewportPercentageLength:
case CSSUnitCategory::Other:
return CSSUnitType::CSS_UNKNOWN;
}
@@ -152,11 +166,14 @@ TextStream& operator<<(TextStream& ts, CSSUnitCategory category)
switch (category) {
case CSSUnitCategory::Number: ts << "Number"; break;
case CSSUnitCategory::Percent: ts << "Percent"; break;
case CSSUnitCategory::Length: ts << "Length"; break;
case CSSUnitCategory::AbsoluteLength: ts << "AsboluteLength"; break;
case CSSUnitCategory::ViewportPercentageLength: ts << "ViewportPercentageLength"; break;
case CSSUnitCategory::FontRelativeLength: ts << "FontRelativeLength"; break;
case CSSUnitCategory::Angle: ts << "Angle"; break;
case CSSUnitCategory::Time: ts << "Time"; break;
case CSSUnitCategory::Frequency: ts << "Frequency"; break;
case CSSUnitCategory::Resolution: ts << "Resolution"; break;
case CSSUnitCategory::Flex: ts << "Flex"; break;
case CSSUnitCategory::Other: ts << "Other"; break;
}
return ts;
@@ -136,11 +136,14 @@ enum class CSSUnitType : uint8_t {
enum class CSSUnitCategory : uint8_t {
Number,
Percent,
Length,
AbsoluteLength,
FontRelativeLength,
ViewportPercentageLength,
Angle,
Time,
Frequency,
Resolution,
Flex,
Other
};

@@ -240,6 +240,19 @@ ExceptionOr<Ref<CSSUnitValue>> CSSNumericValue::to(String&& unit)
return to(CSSUnitValue::parseUnit(unit));
}

// https://drafts.css-houdini.org/css-typed-om/#create-a-cssunitvalue-from-a-sum-value-item
static RefPtr<CSSUnitValue> createCSSUnitValueFromAddend(CSSNumericValue::Addend addend)
{
if (addend.units.size() > 1)
return nullptr;
if (addend.units.isEmpty())
return CSSUnitValue::create(addend.value, CSSUnitType::CSS_NUMBER);
auto unit = addend.units.begin();
if (unit->value != 1)
return nullptr;
return CSSUnitValue::create(addend.value, unit->key);
}

ExceptionOr<Ref<CSSUnitValue>> CSSNumericValue::to(CSSUnitType unit)
{
// https://drafts.css-houdini.org/css-typed-om/#dom-cssnumericvalue-to
@@ -252,17 +265,7 @@ ExceptionOr<Ref<CSSUnitValue>> CSSNumericValue::to(CSSUnitType unit)
return Exception { TypeError };

auto& addend = (*sumValue)[0];
auto unconverted = [] (const auto& addend) -> RefPtr<CSSUnitValue> {
switch (addend.units.size()) {
case 0:
return CSSUnitValue::create(addend.value, CSSUnitType::CSS_NUMBER);
case 1:
return CSSUnitValue::create(addend.value, addend.units.begin()->key);
default:
break;
}
return nullptr;
} (addend);
auto unconverted = createCSSUnitValueFromAddend(addend);
if (!unconverted)
return Exception { TypeError };

@@ -272,12 +275,58 @@ ExceptionOr<Ref<CSSUnitValue>> CSSNumericValue::to(CSSUnitType unit)
return converted.releaseNonNull();
}

// https://drafts.css-houdini.org/css-typed-om/#dom-cssnumericvalue-tosum
ExceptionOr<Ref<CSSMathSum>> CSSNumericValue::toSum(FixedVector<String>&& units)
{
UNUSED_PARAM(units);
// https://drafts.css-houdini.org/css-typed-om/#dom-cssnumericvalue-tosum
// FIXME: add impl.
return CSSMathSum::create(FixedVector<CSSNumberish> { 1.0 });
Vector<CSSUnitType> parsedUnits;
parsedUnits.reserveInitialCapacity(units.size());
for (auto& unit : units) {
auto parsedUnit = CSSUnitValue::parseUnit(unit);
if (parsedUnit == CSSUnitType::CSS_UNKNOWN)
return Exception { SyntaxError, "Invalid unit parameter"_s };
parsedUnits.uncheckedAppend(parsedUnit);
}
auto sumValue = toSumValue();
if (!sumValue)
return Exception { TypeError, "Could not create a sum value"_s };

Vector<Ref<CSSNumericValue>> values;
values.reserveInitialCapacity(sumValue->size());
for (auto& addend : *sumValue) {
auto cssUnitValue = createCSSUnitValueFromAddend(addend);
if (!cssUnitValue)
return Exception { TypeError, "Could not create CSSUnitValue"_s };
values.uncheckedAppend(cssUnitValue.releaseNonNull());
}

WTFLogAlways("CHRIS: ParsedUnitSize: %lu, valuesSize: %lu", parsedUnits.size(), values.size());
if (parsedUnits.isEmpty()) {
std::sort(values.begin(), values.end(), [](auto& a, auto& b) {
WTFLogAlways("CHRIS: Comparing '%s' and '%s', result : %d", static_reference_cast<CSSUnitValue>(a)->unitSerialization().characters(), static_reference_cast<CSSUnitValue>(b)->unitSerialization().characters(), strcmp(static_reference_cast<CSSUnitValue>(a)->unitSerialization().characters(), static_reference_cast<CSSUnitValue>(b)->unitSerialization().characters()));
return strcmp(static_reference_cast<CSSUnitValue>(a)->unitSerialization().characters(), static_reference_cast<CSSUnitValue>(b)->unitSerialization().characters()) < 0;
});
return CSSMathSum::create(WTFMove(values));
}

Vector<Ref<CSSNumericValue>> result;
for (auto& parsedUnit : parsedUnits) {
auto temp = CSSUnitValue::create(0, parsedUnit);
for (size_t i = 0; i < values.size();) {
auto value = static_reference_cast<CSSUnitValue>(values[i]);
if (auto convertedValue = value->convertTo(parsedUnit)) {
temp->setValue(temp->value() + convertedValue->value());
values.remove(i);
} else
++i;
}
result.append(WTFMove(temp));
}

if (!values.isEmpty())
return Exception { TypeError, "Failed to convert all values"_s };

return CSSMathSum::create(WTFMove(result));
}

ExceptionOr<Ref<CSSNumericValue>> CSSNumericValue::parse(String&& cssText)
@@ -158,14 +158,13 @@ auto CSSUnitValue::toSumValue() const -> std::optional<SumValue>
switch (category) {
case CSSUnitCategory::Percent:
return CSSUnitType::CSS_PERCENTAGE;
case CSSUnitCategory::Other:
if (unit == CSSUnitType::CSS_FR)
return CSSUnitType::CSS_FR;
break;
case CSSUnitCategory::Flex:
return CSSUnitType::CSS_FR;
default:
break;
}
return canonicalUnitTypeForCategory(category);
auto result = canonicalUnitTypeForCategory(category);
return result == CSSUnitType::CSS_UNKNOWN ? unit : result;
} (m_unit);
auto convertedValue = m_value * conversionToCanonicalUnitsScaleFactor(unitEnum()) / conversionToCanonicalUnitsScaleFactor(canonicalUnit);

@@ -42,7 +42,9 @@ std::optional<CSSNumericType> CSSNumericType::create(CSSUnitType unit, int expon
case CSSUnitCategory::Percent:
type.percent = exponent;
return { WTFMove(type) };
case CSSUnitCategory::Length:
case CSSUnitCategory::AbsoluteLength:
case CSSUnitCategory::FontRelativeLength:
case CSSUnitCategory::ViewportPercentageLength:
type.length = exponent;
return { WTFMove(type) };
case CSSUnitCategory::Angle:
@@ -57,11 +59,10 @@ std::optional<CSSNumericType> CSSNumericType::create(CSSUnitType unit, int expon
case CSSUnitCategory::Resolution:
type.resolution = exponent;
return { WTFMove(type) };
case CSSUnitCategory::Flex:
type.flex = exponent;
return { WTFMove(type) };
case CSSUnitCategory::Other:
if (unit == CSSUnitType::CSS_FR) {
type.flex = exponent;
return { WTFMove(type) };
}
break;
}

0 comments on commit e3fb293

Please sign in to comment.