LibWeb: Resolve percentages for opacity properties at parse time

This commit is contained in:
Tim Ledbetter 2025-06-18 17:44:58 +01:00 committed by Jelle Raaijmakers
commit 269d5bb40e
Notes: github-actions[bot] 2025-06-19 08:28:47 +00:00
6 changed files with 87 additions and 4 deletions

View file

@ -377,8 +377,6 @@ float ComputedProperties::resolve_opacity_value(CSSStyleValue const& value)
else
dbgln("Unable to resolve calc() as opacity (number): {}", value.to_string(SerializationMode::Normal));
}
} else if (value.is_percentage()) {
unclamped_opacity = value.as_percentage().percentage().as_fraction();
}
return clamp(unclamped_opacity, 0.0f, 1.0f);

View file

@ -412,6 +412,7 @@ private:
RefPtr<CSSStyleValue const> parse_font_variant_numeric_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_list_style_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_math_depth_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_opacity_value(PropertyID property_id, TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_overflow_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_place_content_value(TokenStream<ComponentValue>&);
RefPtr<CSSStyleValue const> parse_place_items_value(TokenStream<ComponentValue>&);

View file

@ -695,6 +695,13 @@ Parser::ParseErrorOr<NonnullRefPtr<CSSStyleValue const>> Parser::parse_css_value
if (auto parsed_value = parse_math_depth_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::Opacity:
case PropertyID::FillOpacity:
case PropertyID::StopOpacity:
case PropertyID::StrokeOpacity:
if (auto parsed_value = parse_opacity_value(property_id, tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::Overflow:
if (auto parsed_value = parse_overflow_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
@ -3395,6 +3402,28 @@ RefPtr<CSSStyleValue const> Parser::parse_math_depth_value(TokenStream<Component
return nullptr;
}
RefPtr<CSSStyleValue const> Parser::parse_opacity_value(PropertyID property_id, TokenStream<ComponentValue>& tokens)
{
auto value = parse_css_value_for_property(property_id, tokens);
if (!value)
return nullptr;
// Percentages map to the range [0,1] for opacity values
if (value->is_percentage())
value = NumberStyleValue::create(value->as_percentage().percentage().as_fraction());
if (value->is_calculated() && value->as_calculated().resolves_to_percentage()) {
auto maybe_percentage = value->as_calculated().resolve_percentage({});
if (maybe_percentage.has_value()) {
auto resolved_percentage = maybe_percentage->as_fraction();
CalculationContext context {};
auto calc_node = NumericCalculationNode::create(Number { Number::Type::Number, resolved_percentage }, context);
value = CalculatedStyleValue::create(move(calc_node), CSSNumericType { CSSNumericType::BaseType::Length, 1 }, context);
}
}
return value;
}
RefPtr<CSSStyleValue const> Parser::parse_overflow_value(TokenStream<ComponentValue>& tokens)
{
auto transaction = tokens.begin_transaction();

View file

@ -138,8 +138,8 @@ ry: 'calc(2%)' -> '2%'
ry: 'calc(2% * var(--n))' -> 'calc(2 * 2%)'
stop-opacity: 'calc(2)' -> '2'
stop-opacity: 'calc(2 * var(--n))' -> '4'
stroke-opacity: 'calc(2%)' -> '2%'
stroke-opacity: 'calc(2% * var(--n))' -> '4%'
stroke-opacity: 'calc(2%)' -> '0.02'
stroke-opacity: 'calc(2% * var(--n))' -> '0.04'
stroke-width: 'calc(2px)' -> '2px'
stroke-width: 'calc(2px * var(--n))' -> '4px'
tab-size: 'calc(2px)' -> '2px'

View file

@ -0,0 +1,21 @@
Harness status: OK
Found 16 tests
16 Pass
Pass e.style['opacity'] = "1" should set the property value
Pass e.style['opacity'] = "0.5" should set the property value
Pass e.style['opacity'] = "0" should set the property value
Pass e.style['opacity'] = "-2" should set the property value
Pass e.style['opacity'] = "3" should set the property value
Pass e.style['opacity'] = "-100%" should set the property value
Pass e.style['opacity'] = "50%" should set the property value
Pass e.style['opacity'] = "300%" should set the property value
Pass e.style['opacity'] = "clamp(50%, 0%, 70%)" should set the property value
Pass e.style['opacity'] = "clamp(50%, 80%, 70%)" should set the property value
Pass e.style['opacity'] = "clamp(50%, 60%, 70%)" should set the property value
Pass e.style['opacity'] = "min(50%, 0%)" should set the property value
Pass e.style['opacity'] = "min(0%, 50%)" should set the property value
Pass e.style['opacity'] = "max(50%, 0%)" should set the property value
Pass e.style['opacity'] = "max(0%, 50%)" should set the property value
Pass e.style['opacity'] = "min(-40%, 50%)" should set the property value

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Color Level 3: parsing opacity with valid values</title>
<link rel="author" title="Eric Willigers" href="mailto:ericwilligers@chromium.org">
<link rel="help" href="https://www.w3.org/TR/css-color-3/#opacity">
<meta name="assert" content="opacity supports the full grammar '<alphavalue>'.">
<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("opacity", "1");
test_valid_value("opacity", "0.5");
test_valid_value("opacity", "0");
test_valid_value("opacity", "-2");
test_valid_value("opacity", "3");
test_valid_value("opacity", "-100%", "-1");
test_valid_value("opacity", "50%", "0.5");
test_valid_value("opacity", "300%", "3");
test_valid_value("opacity", "clamp(50%, 0%, 70%)", "calc(0.5)");
test_valid_value("opacity", "clamp(50%, 80%, 70%)", "calc(0.7)");
test_valid_value("opacity", "clamp(50%, 60%, 70%)", "calc(0.6)");
test_valid_value("opacity", "min(50%, 0%)", "calc(0)");
test_valid_value("opacity", "min(0%, 50%)", "calc(0)");
test_valid_value("opacity", "max(50%, 0%)", "calc(0.5)");
test_valid_value("opacity", "max(0%, 50%)", "calc(0.5)");
test_valid_value("opacity", "min(-40%, 50%)", "calc(-0.4)");
</script>
</body>
</html>