LibWeb/CSS: Set border-widths to 0 at used time, not computed time

...when the style is `none` or `hidden`. `outline-width` is not affected
by `outline-style: none` at all.

In our codebase, that means doing the border-width conversion when
assigning to ComputedValues.

Corresponds to:
2a3d1e4d10
09f11f2ef9
This commit is contained in:
Sam Atkins 2025-12-15 11:31:01 +00:00 committed by Jelle Raaijmakers
parent 40b25f631b
commit 45fc72214f
10 changed files with 54 additions and 46 deletions

View file

@ -902,10 +902,20 @@ RefPtr<StyleValue const> CSSStyleProperties::style_value_for_computed_property(L
return animation_duration_computed_value;
}
// If the border-style corresponding to a given border-width is none or hidden, then the used width is 0.
// https://drafts.csswg.org/css-backgrounds/#border-width
// NB: We do this adjustment when assigning to ComputedValues, so read from there.
case PropertyID::BorderBottomWidth:
return style_value_for_size(Size::make_px(layout_node.computed_values().border_bottom().width));
case PropertyID::BorderLeftWidth:
return style_value_for_size(Size::make_px(layout_node.computed_values().border_left().width));
case PropertyID::BorderRightWidth:
return style_value_for_size(Size::make_px(layout_node.computed_values().border_right().width));
case PropertyID::BorderTopWidth:
return style_value_for_size(Size::make_px(layout_node.computed_values().border_top().width));
// -> Any other property
// The resolved value is the computed value.
// NOTE: This is handled inside the `default` case.
case PropertyID::Contain: {
auto const& contain = layout_node.computed_values().contain();
if (contain.layout_containment && contain.style_containment && contain.paint_containment) {

View file

@ -2306,15 +2306,11 @@ NonnullRefPtr<StyleValue const> StyleComputer::compute_value_of_property(
case PropertyID::BackgroundSize:
return repeat_style_value_list_to_n_elements(absolutized_value, get_property_specified_value(PropertyID::BackgroundImage)->as_value_list().size());
case PropertyID::BorderBottomWidth:
return compute_border_or_outline_width(absolutized_value, get_property_specified_value(PropertyID::BorderBottomStyle), device_pixels_per_css_pixel);
case PropertyID::BorderLeftWidth:
return compute_border_or_outline_width(absolutized_value, get_property_specified_value(PropertyID::BorderLeftStyle), device_pixels_per_css_pixel);
case PropertyID::BorderRightWidth:
return compute_border_or_outline_width(absolutized_value, get_property_specified_value(PropertyID::BorderRightStyle), device_pixels_per_css_pixel);
case PropertyID::BorderTopWidth:
return compute_border_or_outline_width(absolutized_value, get_property_specified_value(PropertyID::BorderTopStyle), device_pixels_per_css_pixel);
case PropertyID::OutlineWidth:
return compute_border_or_outline_width(absolutized_value, get_property_specified_value(PropertyID::OutlineStyle), device_pixels_per_css_pixel);
return compute_border_or_outline_width(absolutized_value, device_pixels_per_css_pixel);
case PropertyID::CornerBottomLeftShape:
case PropertyID::CornerBottomRightShape:
case PropertyID::CornerTopLeftShape:
@ -2399,13 +2395,10 @@ NonnullRefPtr<StyleValue const> StyleComputer::compute_font_variation_settings(N
return StyleValueList::create(move(axis_tags), StyleValueList::Separator::Comma);
}
NonnullRefPtr<StyleValue const> StyleComputer::compute_border_or_outline_width(NonnullRefPtr<StyleValue const> const& absolutized_value, NonnullRefPtr<StyleValue const> const& style_specified_value, double device_pixels_per_css_pixel)
NonnullRefPtr<StyleValue const> StyleComputer::compute_border_or_outline_width(NonnullRefPtr<StyleValue const> const& absolutized_value, double device_pixels_per_css_pixel)
{
// https://drafts.csswg.org/css-backgrounds/#border-width
// absolute length, snapped as a border width; zero if the border style is none or hidden
if (first_is_one_of(style_specified_value->to_keyword(), Keyword::None, Keyword::Hidden))
return LengthStyleValue::create(Length::make_px(0));
// absolute length, snapped as a border width
auto const absolute_length = [&]() -> CSSPixels {
if (absolutized_value->is_calculated())
return absolutized_value->as_calculated().resolve_length({})->absolute_length_to_px();

View file

@ -129,7 +129,7 @@ public:
static NonnullRefPtr<StyleValue const> compute_value_of_property(PropertyID, NonnullRefPtr<StyleValue const> const& specified_value, Function<NonnullRefPtr<StyleValue const>(PropertyID)> const& get_property_specified_value, ComputationContext const&, double device_pixels_per_css_pixel);
static NonnullRefPtr<StyleValue const> compute_animation_name(NonnullRefPtr<StyleValue const> const& absolutized_value);
static NonnullRefPtr<StyleValue const> compute_border_or_outline_width(NonnullRefPtr<StyleValue const> const& absolutized_value, NonnullRefPtr<StyleValue const> const& style_specified_value, double device_pixels_per_css_pixel);
static NonnullRefPtr<StyleValue const> compute_border_or_outline_width(NonnullRefPtr<StyleValue const> const& absolutized_value, double device_pixels_per_css_pixel);
static NonnullRefPtr<StyleValue const> compute_corner_shape(NonnullRefPtr<StyleValue const> const& absolutized_value);
static NonnullRefPtr<StyleValue const> compute_font_size(NonnullRefPtr<StyleValue const> const& specified_value, int computed_math_depth, CSSPixels inherited_font_size, int inherited_math_depth, ComputationContext const&);
static NonnullRefPtr<StyleValue const> compute_font_style(NonnullRefPtr<StyleValue const> const& specified_value, ComputationContext const&);

View file

@ -604,8 +604,14 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
border.color = computed_style.color_or_fallback(color_property, color_resolution_context, computed_values.color());
border.line_style = computed_style.line_style(style_property);
// FIXME: Interpolation can cause negative values - we clamp here but should instead clamp as part of interpolation
border.width = max(CSSPixels { 0 }, computed_style.length(width_property).absolute_length_to_px());
// If the border-style corresponding to a given border-width is none or hidden, then the used width is 0.
// https://drafts.csswg.org/css-backgrounds/#border-width
if (border.line_style == CSS::LineStyle::None || border.line_style == CSS::LineStyle::Hidden) {
border.width = 0;
} else {
// FIXME: Interpolation can cause negative values - we clamp here but should instead clamp as part of interpolation
border.width = max(CSSPixels { 0 }, computed_style.length(width_property).absolute_length_to_px());
}
};
do_border_style(computed_values.border_left(), CSS::PropertyID::BorderLeftWidth, CSS::PropertyID::BorderLeftColor, CSS::PropertyID::BorderLeftStyle);

View file

@ -624,15 +624,15 @@ All supported properties and their default values exposed from CSSStylePropertie
'opacity': '1'
'order': '0'
'orphans': '2'
'outline': 'rgb(0, 0, 0) 0px'
'outline': 'rgb(0, 0, 0) 3px'
'outlineColor': 'rgb(0, 0, 0)'
'outline-color': 'rgb(0, 0, 0)'
'outlineOffset': '0px'
'outline-offset': '0px'
'outlineStyle': 'none'
'outline-style': 'none'
'outlineWidth': '0px'
'outline-width': '0px'
'outlineWidth': '3px'
'outline-width': '3px'
'overflow': 'visible'
'overflowBlock': 'visible'
'overflow-block': 'visible'

View file

@ -116,8 +116,8 @@ order: 'calc(2)' -> '2'
order: 'calc(2 * var(--n))' -> '4'
outline-offset: 'calc(2px)' -> '2px'
outline-offset: 'calc(2px * var(--n))' -> '4px'
outline-width: 'calc(2px)' -> '0px'
outline-width: 'calc(2px * var(--n))' -> '0px'
outline-width: 'calc(2px)' -> '2px'
outline-width: 'calc(2px * var(--n))' -> '4px'
padding-bottom: 'calc(2px)' -> '2px'
padding-bottom: 'calc(2px * var(--n))' -> '4px'
padding-left: 'calc(2%)' -> '15.6875px'

View file

@ -230,7 +230,7 @@ order: 0
outline-color: rgb(0, 0, 0)
outline-offset: 0px
outline-style: none
outline-width: 0px
outline-width: 3px
overflow-block: visible
overflow-inline: visible
overflow-x: visible

View file

@ -1,3 +1,3 @@
INPUT: rgb(0, 0, 0) auto 3px
BUTTON: rgb(0, 0, 0) 0px
BUTTON: rgb(0, 0, 0) 3px
DIV: rgb(0, 0, 0) auto 3px

View file

@ -2,8 +2,8 @@ Harness status: OK
Found 256 tests
232 Pass
24 Fail
252 Pass
4 Fail
Pass CSS Transitions: property <border-width> from [20px 40px 60px 80px] to [30px 50px 70px 90px] at (-0.3) should be [17px 37px 57px 77px]
Pass CSS Transitions: property <border-width> from [20px 40px 60px 80px] to [30px 50px 70px 90px] at (0) should be [20px 40px 60px 80px]
Pass CSS Transitions: property <border-width> from [20px 40px 60px 80px] to [30px 50px 70px 90px] at (0.3) should be [23px 43px 63px 83px]
@ -76,30 +76,30 @@ Pass Web Animations: property <border-left-width> from [initial] to [23px] at (0
Pass Web Animations: property <border-left-width> from [initial] to [23px] at (0.6) should be [15px]
Pass Web Animations: property <border-left-width> from [initial] to [23px] at (1) should be [23px]
Pass Web Animations: property <border-left-width> from [initial] to [23px] at (1.5) should be [33px]
Fail CSS Transitions: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Fail CSS Transitions: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Fail CSS Transitions: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Fail CSS Transitions: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Transitions: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Pass CSS Transitions: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Pass CSS Transitions: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Pass CSS Transitions: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Transitions: property <border-left-width> from [inherit] to [20px] at (1) should be [20px]
Fail CSS Transitions: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Fail CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Fail CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Fail CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Fail CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Transitions: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Pass CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Pass CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Pass CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Pass CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (1) should be [20px]
Fail CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Fail CSS Animations: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Fail CSS Animations: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Fail CSS Animations: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Fail CSS Animations: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Transitions with transition: all: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Pass CSS Animations: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Pass CSS Animations: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Pass CSS Animations: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Pass CSS Animations: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Animations: property <border-left-width> from [inherit] to [20px] at (1) should be [20px]
Fail CSS Animations: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Fail Web Animations: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Fail Web Animations: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Fail Web Animations: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Fail Web Animations: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass CSS Animations: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Pass Web Animations: property <border-left-width> from [inherit] to [20px] at (-0.3) should be [33px]
Pass Web Animations: property <border-left-width> from [inherit] to [20px] at (0) should be [30px]
Pass Web Animations: property <border-left-width> from [inherit] to [20px] at (0.3) should be [27px]
Pass Web Animations: property <border-left-width> from [inherit] to [20px] at (0.6) should be [24px]
Pass Web Animations: property <border-left-width> from [inherit] to [20px] at (1) should be [20px]
Fail Web Animations: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Pass Web Animations: property <border-left-width> from [inherit] to [20px] at (1.5) should be [15px]
Pass CSS Transitions: property <border-left-width> from [unset] to [23px] at (-0.3) should be [0px]
Pass CSS Transitions: property <border-left-width> from [unset] to [23px] at (0) should be [3px]
Pass CSS Transitions: property <border-left-width> from [unset] to [23px] at (0.3) should be [9px]

View file

@ -2,8 +2,7 @@ Harness status: OK
Found 9 tests
8 Pass
1 Fail
9 Pass
Pass Property outline-width value '2.5px'
Pass Property outline-width value '10px'
Pass Property outline-width value '0.5em'
@ -12,4 +11,4 @@ Pass Property outline-width value 'calc(10px - 0.5em)'
Pass Property outline-width value 'thin'
Pass Property outline-width value 'medium'
Pass Property outline-width value 'thick'
Fail outline-width is independent of the value of outline-style
Pass outline-width is independent of the value of outline-style