LibWeb: Only resolve transform-origin keywords for the computed value

Previously, we were resolving these keywords at parse time, which gave
an incorrect serialization of the specified value.
This commit is contained in:
Tim Ledbetter 2025-06-14 00:47:59 +01:00 committed by Alexander Kalenik
commit a8d5758777
Notes: github-actions[bot] 2025-06-15 14:02:58 +00:00
7 changed files with 131 additions and 17 deletions

View file

@ -640,15 +640,30 @@ TransformBox ComputedProperties::transform_box() const
TransformOrigin ComputedProperties::transform_origin() const TransformOrigin ComputedProperties::transform_origin() const
{ {
auto length_percentage_with_keywords_resolved = [](CSSStyleValue const& value) -> Optional<LengthPercentage> {
if (value.is_keyword()) {
auto keyword = value.to_keyword();
if (keyword == Keyword::Left || keyword == Keyword::Top)
return Percentage(0);
if (keyword == Keyword::Center)
return Percentage(50);
if (keyword == Keyword::Right || keyword == Keyword::Bottom)
return Percentage(100);
VERIFY_NOT_REACHED();
}
return length_percentage_for_style_value(value);
};
auto const& value = property(PropertyID::TransformOrigin); auto const& value = property(PropertyID::TransformOrigin);
if (!value.is_value_list() || value.as_value_list().size() != 2) if (!value.is_value_list() || value.as_value_list().size() != 2)
return {}; return {};
auto const& list = value.as_value_list(); auto const& list = value.as_value_list();
auto x_value = length_percentage_for_style_value(list.values()[0]);
auto y_value = length_percentage_for_style_value(list.values()[1]); auto x_value = length_percentage_with_keywords_resolved(list.values()[0]);
if (!x_value.has_value() || !y_value.has_value()) { auto y_value = length_percentage_with_keywords_resolved(list.values()[1]);
if (!x_value.has_value() || !y_value.has_value())
return {}; return {};
}
return { x_value.value(), y_value.value() }; return { x_value.value(), y_value.value() };
} }

View file

@ -3830,15 +3830,15 @@ RefPtr<CSSStyleValue const> Parser::parse_transform_origin_value(TokenStream<Com
if (value->is_keyword()) { if (value->is_keyword()) {
switch (value->to_keyword()) { switch (value->to_keyword()) {
case Keyword::Top: case Keyword::Top:
return AxisOffset { Axis::Y, PercentageStyleValue::create(Percentage(0)) }; return AxisOffset { Axis::Y, value.release_nonnull() };
case Keyword::Left: case Keyword::Left:
return AxisOffset { Axis::X, PercentageStyleValue::create(Percentage(0)) }; return AxisOffset { Axis::X, value.release_nonnull() };
case Keyword::Center: case Keyword::Center:
return AxisOffset { Axis::None, PercentageStyleValue::create(Percentage(50)) }; return AxisOffset { Axis::None, value.release_nonnull() };
case Keyword::Bottom: case Keyword::Bottom:
return AxisOffset { Axis::Y, PercentageStyleValue::create(Percentage(100)) }; return AxisOffset { Axis::Y, value.release_nonnull() };
case Keyword::Right: case Keyword::Right:
return AxisOffset { Axis::X, PercentageStyleValue::create(Percentage(100)) }; return AxisOffset { Axis::X, value.release_nonnull() };
default: default:
return OptionalNone {}; return OptionalNone {};
} }
@ -3866,9 +3866,9 @@ RefPtr<CSSStyleValue const> Parser::parse_transform_origin_value(TokenStream<Com
switch (single_value->axis) { switch (single_value->axis) {
case Axis::None: case Axis::None:
case Axis::X: case Axis::X:
return make_list(single_value->offset, PercentageStyleValue::create(Percentage(50))); return make_list(single_value->offset, CSSKeywordValue::create(Keyword::Center));
case Axis::Y: case Axis::Y:
return make_list(PercentageStyleValue::create(Percentage(50)), single_value->offset); return make_list(CSSKeywordValue::create(Keyword::Center), single_value->offset);
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }

View file

@ -1,7 +1,7 @@
center => 50% 50% center => center center
10px => 10px 50% 10px => 10px center
25% => 25% 50% 25% => 25% center
left 20% => 0% 20% left 20% => left 20%
20px bottom => 20px 100% 20px bottom => 20px bottom
top right => 100% 0% top right => right top
"center" => (invalid) "center" => (invalid)

View file

@ -0,0 +1,16 @@
Harness status: OK
Found 10 tests
8 Pass
2 Fail
Pass e.style['transform-origin'] = "1px 2px 3%" should not set the property value
Pass e.style['transform-origin'] = "1px 2px left" should not set the property value
Pass e.style['transform-origin'] = "1px 2px 3px 4px" should not set the property value
Fail e.style['transform-origin'] = "1px left" should not set the property value
Fail e.style['transform-origin'] = "top 1px" should not set the property value
Pass e.style['transform-origin'] = "right left" should not set the property value
Pass e.style['transform-origin'] = "top bottom" should not set the property value
Pass e.style['transform-origin'] = "bottom 10% right 20%" should not set the property value
Pass e.style['transform-origin'] = "right 30% top -60px" should not set the property value
Pass e.style['transform-origin'] = "right 20px bottom 30px" should not set the property value

View file

@ -0,0 +1,22 @@
Harness status: OK
Found 16 tests
12 Pass
4 Fail
Pass e.style['transform-origin'] = "left" should set the property value
Pass e.style['transform-origin'] = "center" should set the property value
Pass e.style['transform-origin'] = "right" should set the property value
Pass e.style['transform-origin'] = "top" should set the property value
Pass e.style['transform-origin'] = "bottom" should set the property value
Pass e.style['transform-origin'] = "-1px" should set the property value
Pass e.style['transform-origin'] = "calc(2em + 3ex)" should set the property value
Pass e.style['transform-origin'] = "-4%" should set the property value
Pass e.style['transform-origin'] = "left center" should set the property value
Pass e.style['transform-origin'] = "center top" should set the property value
Pass e.style['transform-origin'] = "right -4%" should set the property value
Fail e.style['transform-origin'] = "-1px bottom 5px" should set the property value
Fail e.style['transform-origin'] = "center left 6px" should set the property value
Pass e.style['transform-origin'] = "top center" should set the property value
Fail e.style['transform-origin'] = "bottom right 7px" should set the property value
Fail e.style['transform-origin'] = "-1px -2px -3px" should set the property value

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Transform Module Level 1: parsing transform-origin with invalid values</title>
<link rel="help" href="https://drafts.csswg.org/css-transforms/#transform-origin-property">
<meta name="assert" content="transform-origin supports only the grammar from spec.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_invalid_value("transform-origin", "1px 2px 3%");
test_invalid_value("transform-origin", "1px 2px left");
test_invalid_value("transform-origin", "1px 2px 3px 4px");
test_invalid_value("transform-origin", "1px left");
test_invalid_value("transform-origin", "top 1px"); // Blink fails.
test_invalid_value("transform-origin", "right left");
test_invalid_value("transform-origin", "top bottom");
test_invalid_value("transform-origin", "bottom 10% right 20%");
test_invalid_value("transform-origin", "right 30% top -60px");
test_invalid_value("transform-origin", "right 20px bottom 30px");
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Transform Module Level 1: parsing transform-origin with valid values</title>
<link rel="help" href="https://drafts.csswg.org/css-transforms/#transform-origin-property">
<meta name="assert" content="transform-origin supports the full grammar from spec.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
</head>
<body>
<script>
test_valid_value("transform-origin", "left", ["left center 0px", "left 50% 0px", "left center"]);
test_valid_value("transform-origin", "center", ["center center 0px", "center 50% 0px", "center center"]);
test_valid_value("transform-origin", "right", ["right center 0px", "right 50% 0px", "right center"]);
test_valid_value("transform-origin", "top", ["center top 0px", "50% top 0px", "center top"]);
test_valid_value("transform-origin", "bottom", ["center bottom 0px", "50% bottom 0px", "center bottom"]);
test_valid_value("transform-origin", "-1px", ["-1px center 0px", "-1px 50% 0px", "-1px center"]);
test_valid_value("transform-origin", "calc(2em + 3ex)", ["calc(2em + 3ex) center 0px", "calc(2em + 3ex) 50% 0px", "calc(2em + 3ex) center"]);
test_valid_value("transform-origin", "-4%", ["-4% center 0px", "-4% 50% 0px", "-4% center"]);
test_valid_value("transform-origin", "left center", ["left center 0px", "left center"]);
test_valid_value("transform-origin", "center top", ["center top 0px", "center top"]);
test_valid_value("transform-origin", "right -4%", ["right -4% 0px", "right -4%"]);
test_valid_value("transform-origin", "-1px bottom 5px");
test_valid_value("transform-origin", "center left 6px", "left center 6px");
test_valid_value("transform-origin", "top center", ["center top 0px", "center top"]);
test_valid_value("transform-origin", "bottom right 7px", "right bottom 7px");
test_valid_value("transform-origin", "-1px -2px -3px");
</script>
</body>
</html>