LibWeb: Parse text-underline-position property

This introduces the `TextUnderlinePositionStyleValue` class, it is
possible to represent `text-underline-position` as a `StyleValueList`
but would have required ugly workarounds for either serialization or in
`ComputedProperties::text_underline_position`
This commit is contained in:
Callum Law 2025-09-13 19:27:52 +12:00 committed by Tim Ledbetter
commit b0e3af7d10
Notes: github-actions[bot] 2025-09-15 14:25:30 +00:00
21 changed files with 306 additions and 62 deletions

View file

@ -260,6 +260,7 @@ set(SOURCES
CSS/StyleValues/ShorthandStyleValue.cpp CSS/StyleValues/ShorthandStyleValue.cpp
CSS/StyleValues/StyleValue.cpp CSS/StyleValues/StyleValue.cpp
CSS/StyleValues/StyleValueList.cpp CSS/StyleValues/StyleValueList.cpp
CSS/StyleValues/TextUnderlinePositionStyleValue.cpp
CSS/StyleValues/TransformationStyleValue.cpp CSS/StyleValues/TransformationStyleValue.cpp
CSS/StyleValues/TransitionStyleValue.cpp CSS/StyleValues/TransitionStyleValue.cpp
CSS/StyleValues/UnicodeRangeStyleValue.cpp CSS/StyleValues/UnicodeRangeStyleValue.cpp

View file

@ -716,6 +716,16 @@
"none", "none",
"uppercase" "uppercase"
], ],
"text-underline-position-horizontal": [
"auto",
"from-font",
"under"
],
"text-underline-position-vertical": [
"auto",
"left",
"right"
],
"text-wrap-mode": [ "text-wrap-mode": [
"wrap", "wrap",
"nowrap" "nowrap"

View file

@ -541,6 +541,7 @@
"ui-serif", "ui-serif",
"ultra-condensed", "ultra-condensed",
"ultra-expanded", "ultra-expanded",
"under",
"underline", "underline",
"unicase", "unicase",
"unicode", "unicode",

View file

@ -472,6 +472,7 @@ private:
RefPtr<StyleValue const> parse_single_shadow_value(TokenStream<ComponentValue>&, AllowInsetKeyword); RefPtr<StyleValue const> parse_single_shadow_value(TokenStream<ComponentValue>&, AllowInsetKeyword);
RefPtr<StyleValue const> parse_text_decoration_value(TokenStream<ComponentValue>&); RefPtr<StyleValue const> parse_text_decoration_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_text_decoration_line_value(TokenStream<ComponentValue>&); RefPtr<StyleValue const> parse_text_decoration_line_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_text_underline_position_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_rotate_value(TokenStream<ComponentValue>&); RefPtr<StyleValue const> parse_rotate_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_stroke_dasharray_value(TokenStream<ComponentValue>&); RefPtr<StyleValue const> parse_stroke_dasharray_value(TokenStream<ComponentValue>&);
RefPtr<StyleValue const> parse_easing_value(TokenStream<ComponentValue>&); RefPtr<StyleValue const> parse_easing_value(TokenStream<ComponentValue>&);

View file

@ -57,6 +57,7 @@
#include <LibWeb/CSS/StyleValues/StringStyleValue.h> #include <LibWeb/CSS/StyleValues/StringStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h> #include <LibWeb/CSS/StyleValues/StyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h> #include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h> #include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h> #include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransitionStyleValue.h> #include <LibWeb/CSS/StyleValues/TransitionStyleValue.h>
@ -749,6 +750,10 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::No); parsed_value && !tokens.has_next_token()) if (auto parsed_value = parse_shadow_value(tokens, AllowInsetKeyword::No); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull(); return parsed_value.release_nonnull();
return ParseError::SyntaxError; return ParseError::SyntaxError;
case PropertyID::TextUnderlinePosition:
if (auto parsed_value = parse_text_underline_position_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull();
return ParseError::SyntaxError;
case PropertyID::TouchAction: case PropertyID::TouchAction:
if (auto parsed_value = parse_touch_action_value(tokens); parsed_value && !tokens.has_next_token()) if (auto parsed_value = parse_touch_action_value(tokens); parsed_value && !tokens.has_next_token())
return parsed_value.release_nonnull(); return parsed_value.release_nonnull();
@ -4283,6 +4288,51 @@ RefPtr<StyleValue const> Parser::parse_text_decoration_line_value(TokenStream<Co
return StyleValueList::create(move(style_values), StyleValueList::Separator::Space); return StyleValueList::create(move(style_values), StyleValueList::Separator::Space);
} }
// https://drafts.csswg.org/css-text-decor-4/#text-underline-position-property
RefPtr<StyleValue const> Parser::parse_text_underline_position_value(TokenStream<ComponentValue>& tokens)
{
// auto | [ from-font | under ] || [ left | right ]
auto transaction = tokens.begin_transaction();
if (parse_all_as_single_keyword_value(tokens, Keyword::Auto)) {
transaction.commit();
return TextUnderlinePositionStyleValue::create(TextUnderlinePositionHorizontal::Auto, TextUnderlinePositionVertical::Auto);
}
Optional<TextUnderlinePositionHorizontal> horizontal_value;
Optional<TextUnderlinePositionVertical> vertical_value;
while (tokens.has_next_token()) {
auto keyword_value = parse_keyword_value(tokens);
if (!keyword_value)
return nullptr;
if (auto maybe_horizontal_value = keyword_to_text_underline_position_horizontal(keyword_value->to_keyword()); maybe_horizontal_value.has_value()) {
if (maybe_horizontal_value == TextUnderlinePositionHorizontal::Auto || horizontal_value.has_value())
return nullptr;
horizontal_value = maybe_horizontal_value;
continue;
}
if (auto maybe_vertical_value = keyword_to_text_underline_position_vertical(keyword_value->to_keyword()); maybe_vertical_value.has_value()) {
if (maybe_vertical_value == TextUnderlinePositionVertical::Auto || vertical_value.has_value())
return nullptr;
vertical_value = maybe_vertical_value;
continue;
}
return nullptr;
}
transaction.commit();
return TextUnderlinePositionStyleValue::create(horizontal_value.value_or(TextUnderlinePositionHorizontal::Auto), vertical_value.value_or(TextUnderlinePositionVertical::Auto));
}
// https://www.w3.org/TR/pointerevents/#the-touch-action-css-property // https://www.w3.org/TR/pointerevents/#the-touch-action-css-property
RefPtr<StyleValue const> Parser::parse_touch_action_value(TokenStream<ComponentValue>& tokens) RefPtr<StyleValue const> Parser::parse_touch_action_value(TokenStream<ComponentValue>& tokens)
{ {

View file

@ -3383,6 +3383,15 @@
], ],
"percentages-resolve-to": "length" "percentages-resolve-to": "length"
}, },
"text-underline-position": {
"animation-type": "discrete",
"inherited": true,
"initial": "auto",
"valid-types": [
"text-underline-position-horizontal",
"text-underline-position-vertical"
]
},
"text-wrap": { "text-wrap": {
"inherited": true, "inherited": true,
"initial": "wrap", "initial": "wrap",

View file

@ -66,6 +66,7 @@
#include <LibWeb/CSS/StyleValues/StringStyleValue.h> #include <LibWeb/CSS/StyleValues/StringStyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h> #include <LibWeb/CSS/StyleValues/StyleValue.h>
#include <LibWeb/CSS/StyleValues/StyleValueList.h> #include <LibWeb/CSS/StyleValues/StyleValueList.h>
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h> #include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h> #include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
#include <LibWeb/CSS/StyleValues/TransitionStyleValue.h> #include <LibWeb/CSS/StyleValues/TransitionStyleValue.h>

View file

@ -30,64 +30,65 @@
namespace Web::CSS { namespace Web::CSS {
#define ENUMERATE_CSS_STYLE_VALUE_TYPES \ #define ENUMERATE_CSS_STYLE_VALUE_TYPES \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Anchor, anchor, AnchorStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Anchor, anchor, AnchorStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(AnchorSize, anchor_size, AnchorSizeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(AnchorSize, anchor_size, AnchorSizeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Angle, angle, AngleStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Angle, angle, AngleStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(BackgroundSize, background_size, BackgroundSizeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(BackgroundSize, background_size, BackgroundSizeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(BasicShape, basic_shape, BasicShapeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(BasicShape, basic_shape, BasicShapeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(BorderImageSlice, border_image_slice, BorderImageSliceStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(BorderImageSlice, border_image_slice, BorderImageSliceStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(BorderRadius, border_radius, BorderRadiusStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(BorderRadius, border_radius, BorderRadiusStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Calculated, calculated, CalculatedStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Calculated, calculated, CalculatedStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(ColorScheme, color_scheme, ColorSchemeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(ColorScheme, color_scheme, ColorSchemeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Color, color, ColorStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Color, color, ColorStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(ConicGradient, conic_gradient, ConicGradientStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(ConicGradient, conic_gradient, ConicGradientStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Content, content, ContentStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Content, content, ContentStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Counter, counter, CounterStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Counter, counter, CounterStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(CounterDefinitions, counter_definitions, CounterDefinitionsStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(CounterDefinitions, counter_definitions, CounterDefinitionsStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Cursor, cursor, CursorStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Cursor, cursor, CursorStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(CustomIdent, custom_ident, CustomIdentStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(CustomIdent, custom_ident, CustomIdentStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Display, display, DisplayStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Display, display, DisplayStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Easing, easing, EasingStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Easing, easing, EasingStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Edge, edge, EdgeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Edge, edge, EdgeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(FilterValueList, filter_value_list, FilterValueListStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(FilterValueList, filter_value_list, FilterValueListStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(FitContent, fit_content, FitContentStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(FitContent, fit_content, FitContentStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Flex, flex, FlexStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Flex, flex, FlexStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(FontSource, font_source, FontSourceStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(FontSource, font_source, FontSourceStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(FontStyle, font_style, FontStyleStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(FontStyle, font_style, FontStyleStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Frequency, frequency, FrequencyStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Frequency, frequency, FrequencyStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(GridAutoFlow, grid_auto_flow, GridAutoFlowStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(GridAutoFlow, grid_auto_flow, GridAutoFlowStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(GridTemplateArea, grid_template_area, GridTemplateAreaStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(GridTemplateArea, grid_template_area, GridTemplateAreaStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(GridTrackPlacement, grid_track_placement, GridTrackPlacementStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(GridTrackPlacement, grid_track_placement, GridTrackPlacementStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(GridTrackSizeList, grid_track_size_list, GridTrackSizeListStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(GridTrackSizeList, grid_track_size_list, GridTrackSizeListStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(GuaranteedInvalid, guaranteed_invalid, GuaranteedInvalidStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(GuaranteedInvalid, guaranteed_invalid, GuaranteedInvalidStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Image, image, ImageStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Image, image, ImageStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Integer, integer, IntegerStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Integer, integer, IntegerStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Keyword, keyword, KeywordStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Keyword, keyword, KeywordStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Length, length, LengthStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Length, length, LengthStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(LinearGradient, linear_gradient, LinearGradientStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(LinearGradient, linear_gradient, LinearGradientStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(MathDepth, math_depth, MathDepthStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(MathDepth, math_depth, MathDepthStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Number, number, NumberStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Number, number, NumberStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(OpenTypeTagged, open_type_tagged, OpenTypeTaggedStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(OpenTypeTagged, open_type_tagged, OpenTypeTaggedStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(PendingSubstitution, pending_substitution, PendingSubstitutionStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(PendingSubstitution, pending_substitution, PendingSubstitutionStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Percentage, percentage, PercentageStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Percentage, percentage, PercentageStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Position, position, PositionStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Position, position, PositionStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(RadialGradient, radial_gradient, RadialGradientStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(RadialGradient, radial_gradient, RadialGradientStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Ratio, ratio, RatioStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Ratio, ratio, RatioStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Rect, rect, RectStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Rect, rect, RectStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(RepeatStyle, repeat_style, RepeatStyleStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(RepeatStyle, repeat_style, RepeatStyleStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Resolution, resolution, ResolutionStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Resolution, resolution, ResolutionStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollbarColor, scrollbar_color, ScrollbarColorStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollbarColor, scrollbar_color, ScrollbarColorStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollbarGutter, scrollbar_gutter, ScrollbarGutterStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(ScrollbarGutter, scrollbar_gutter, ScrollbarGutterStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Shadow, shadow, ShadowStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Shadow, shadow, ShadowStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Shorthand, shorthand, ShorthandStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Shorthand, shorthand, ShorthandStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(String, string, StringStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(String, string, StringStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Time, time, TimeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(TextUnderlinePosition, text_underline_position, TextUnderlinePositionStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Transformation, transformation, TransformationStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Time, time, TimeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Transition, transition, TransitionStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Transformation, transformation, TransformationStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(UnicodeRange, unicode_range, UnicodeRangeStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Transition, transition, TransitionStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Unresolved, unresolved, UnresolvedStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(UnicodeRange, unicode_range, UnicodeRangeStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(URL, url, URLStyleValue) \ __ENUMERATE_CSS_STYLE_VALUE_TYPE(Unresolved, unresolved, UnresolvedStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(URL, url, URLStyleValue) \
__ENUMERATE_CSS_STYLE_VALUE_TYPE(ValueList, value_list, StyleValueList) __ENUMERATE_CSS_STYLE_VALUE_TYPE(ValueList, value_list, StyleValueList)
template<typename T> template<typename T>

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2025, Callum Law <callumlaw1709@outlook.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
namespace Web::CSS {
String TextUnderlinePositionStyleValue::to_string(SerializationMode) const
{
if (m_horizontal == TextUnderlinePositionHorizontal::Auto && m_vertical == TextUnderlinePositionVertical::Auto)
return "auto"_string;
if (m_vertical == TextUnderlinePositionVertical::Auto)
return MUST(String::from_utf8(CSS::to_string(m_horizontal)));
if (m_horizontal == TextUnderlinePositionHorizontal::Auto)
return MUST(String::from_utf8(CSS::to_string(m_vertical)));
return MUST(String::formatted("{} {}", CSS::to_string(m_horizontal), CSS::to_string(m_vertical)));
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2025, Callum Law <callumlaw1709@outlook.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/CSS/Enums.h>
#include <LibWeb/CSS/StyleValues/StyleValue.h>
namespace Web::CSS {
class TextUnderlinePositionStyleValue : public StyleValueWithDefaultOperators<TextUnderlinePositionStyleValue> {
public:
static ValueComparingNonnullRefPtr<TextUnderlinePositionStyleValue const> create(TextUnderlinePositionHorizontal horizontal, TextUnderlinePositionVertical vertical)
{
return adopt_ref(*new (nothrow) TextUnderlinePositionStyleValue(horizontal, vertical));
}
virtual ~TextUnderlinePositionStyleValue() override = default;
TextUnderlinePositionHorizontal horizontal() const { return m_horizontal; }
TextUnderlinePositionVertical vertical() const { return m_vertical; }
virtual String to_string(SerializationMode serialization_mode) const override;
bool properties_equal(TextUnderlinePositionStyleValue const& other) const { return m_horizontal == other.m_horizontal && m_vertical == other.m_vertical; }
private:
explicit TextUnderlinePositionStyleValue(TextUnderlinePositionHorizontal horizontal, TextUnderlinePositionVertical vertical)
: StyleValueWithDefaultOperators(Type::TextUnderlinePosition)
, m_horizontal(horizontal)
, m_vertical(vertical)
{
}
TextUnderlinePositionHorizontal m_horizontal;
TextUnderlinePositionVertical m_vertical;
};
}

View file

@ -364,6 +364,7 @@ class StyleValue;
class StyleValueList; class StyleValueList;
class Supports; class Supports;
class SVGPaint; class SVGPaint;
class TextUnderlinePositionStyleValue;
class Time; class Time;
class TimeOrCalculated; class TimeOrCalculated;
class TimePercentage; class TimePercentage;

View file

@ -63,6 +63,7 @@ All properties associated with getComputedStyle(document.body):
"text-shadow", "text-shadow",
"text-transform", "text-transform",
"text-underline-offset", "text-underline-offset",
"text-underline-position",
"text-wrap-mode", "text-wrap-mode",
"text-wrap-style", "text-wrap-style",
"visibility", "visibility",

View file

@ -691,6 +691,8 @@ All supported properties and their default values exposed from CSSStylePropertie
'text-transform': 'none' 'text-transform': 'none'
'textUnderlineOffset': 'auto' 'textUnderlineOffset': 'auto'
'text-underline-offset': 'auto' 'text-underline-offset': 'auto'
'textUnderlinePosition': 'auto'
'text-underline-position': 'auto'
'textWrap': 'wrap' 'textWrap': 'wrap'
'text-wrap': 'wrap' 'text-wrap': 'wrap'
'textWrapMode': 'wrap' 'textWrapMode': 'wrap'

View file

@ -61,6 +61,7 @@ text-rendering: auto
text-shadow: none text-shadow: none
text-transform: none text-transform: none
text-underline-offset: auto text-underline-offset: auto
text-underline-position: auto
text-wrap-mode: wrap text-wrap-mode: wrap
text-wrap-style: auto text-wrap-style: auto
visibility: visible visibility: visible
@ -93,7 +94,7 @@ background-position-x: 0%
background-position-y: 0% background-position-y: 0%
background-repeat: repeat background-repeat: repeat
background-size: auto background-size: auto
block-size: 1425px block-size: 1440px
border-block-end-color: rgb(0, 0, 0) border-block-end-color: rgb(0, 0, 0)
border-block-end-style: none border-block-end-style: none
border-block-end-width: 0px border-block-end-width: 0px
@ -170,7 +171,7 @@ grid-row-start: auto
grid-template-areas: none grid-template-areas: none
grid-template-columns: none grid-template-columns: none
grid-template-rows: none grid-template-rows: none
height: 2580px height: 2595px
inline-size: 784px inline-size: 784px
inset-block-end: auto inset-block-end: auto
inset-block-start: auto inset-block-start: auto

View file

@ -1,8 +1,8 @@
Harness status: OK Harness status: OK
Found 259 tests Found 260 tests
252 Pass 253 Pass
7 Fail 7 Fail
Pass accent-color Pass accent-color
Pass border-collapse Pass border-collapse
@ -65,6 +65,7 @@ Pass text-rendering
Pass text-shadow Pass text-shadow
Pass text-transform Pass text-transform
Pass text-underline-offset Pass text-underline-offset
Pass text-underline-position
Pass text-wrap-mode Pass text-wrap-mode
Pass text-wrap-style Pass text-wrap-style
Pass visibility Pass visibility

View file

@ -0,0 +1,12 @@
Harness status: OK
Found 7 tests
7 Pass
Pass Property text-underline-position value 'auto'
Pass Property text-underline-position value 'under'
Pass Property text-underline-position value 'from-font'
Pass Property text-underline-position value 'left'
Pass Property text-underline-position value 'right'
Pass Property text-underline-position value 'under left'
Pass Property text-underline-position value 'from-font left'

View file

@ -0,0 +1,11 @@
Harness status: OK
Found 6 tests
6 Pass
Pass e.style['text-underline-position'] = "auto under" should not set the property value
Pass e.style['text-underline-position'] = "auto from-font" should not set the property value
Pass e.style['text-underline-position'] = "left auto" should not set the property value
Pass e.style['text-underline-position'] = "left right" should not set the property value
Pass e.style['text-underline-position'] = "right under left" should not set the property value
Pass e.style['text-underline-position'] = "under from-font" should not set the property value

View file

@ -0,0 +1,14 @@
Harness status: OK
Found 9 tests
9 Pass
Pass e.style['text-underline-position'] = "auto" should set the property value
Pass e.style['text-underline-position'] = "under" should set the property value
Pass e.style['text-underline-position'] = "from-font" should set the property value
Pass e.style['text-underline-position'] = "left" should set the property value
Pass e.style['text-underline-position'] = "right" should set the property value
Pass e.style['text-underline-position'] = "under left" should set the property value
Pass e.style['text-underline-position'] = "from-font left" should set the property value
Pass e.style['text-underline-position'] = "right under" should set the property value
Pass e.style['text-underline-position'] = "right from-font" should set the property value

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CSS Text Decoration Test: getComputedStyle().textUnderlinePosition</title>
<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#text-decoration-style-property">
<meta name="assert" content="text-underline-position computed value is as specified.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/computed-testcommon.js"></script>
</head>
<body>
<div id="target"></div>
<script>
test_computed_value("text-underline-position", "auto");
test_computed_value("text-underline-position", "under");
test_computed_value("text-underline-position", "from-font");
test_computed_value("text-underline-position", "left");
test_computed_value("text-underline-position", "right");
test_computed_value("text-underline-position", "under left");
test_computed_value("text-underline-position", "from-font left");
</script>
</body>
</html>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Text Decoration Test: Parsing text-underline-position with invalid values</title>
<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#text-decoration-style-property">
<meta name="assert" content="text-underline-position supports only the grammar 'auto | [ under || [ left | right ] ]'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
<script>
test_invalid_value("text-underline-position", "auto under");
test_invalid_value("text-underline-position", "auto from-font");
test_invalid_value("text-underline-position", "left auto");
test_invalid_value("text-underline-position", "left right");
test_invalid_value("text-underline-position", "right under left");
test_invalid_value("text-underline-position", "under from-font");
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Text Decoration Test: Parsing text-underline-position with valid values</title>
<link rel="help" href="https://drafts.csswg.org/css-text-decor-3/#text-decoration-style-property">
<meta name="assert" content="text-underline-position supports the full grammar 'auto | [ under || [ left | right ] ]'.">
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../css/support/parsing-testcommon.js"></script>
<script>
test_valid_value("text-underline-position", "auto");
test_valid_value("text-underline-position", "under");
test_valid_value("text-underline-position", "from-font");
test_valid_value("text-underline-position", "left");
test_valid_value("text-underline-position", "right");
test_valid_value("text-underline-position", "under left");
test_valid_value("text-underline-position", "from-font left");
test_valid_value("text-underline-position", "right under", "under right");
test_valid_value("text-underline-position", "right from-font", "from-font right");
</script>