diff --git a/Libraries/LibWeb/CSS/ComputedProperties.cpp b/Libraries/LibWeb/CSS/ComputedProperties.cpp index 89330897b58..966722f3188 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.cpp +++ b/Libraries/LibWeb/CSS/ComputedProperties.cpp @@ -1008,8 +1008,11 @@ Vector ComputedProperties::text_decoration_line() const return lines; } - if (value.is_keyword() && value.to_keyword() == Keyword::None) - return {}; + if (value.is_keyword()) { + if (value.to_keyword() == Keyword::None) + return {}; + return { keyword_to_text_decoration_line(value.to_keyword()).release_value() }; + } dbgln("FIXME: Unsupported value for text-decoration-line: {}", value.to_string(CSSStyleValue::SerializationMode::Normal)); return {}; diff --git a/Libraries/LibWeb/CSS/Enums.json b/Libraries/LibWeb/CSS/Enums.json index 6b03f3f473c..eb328f1d00c 100644 --- a/Libraries/LibWeb/CSS/Enums.json +++ b/Libraries/LibWeb/CSS/Enums.json @@ -545,7 +545,9 @@ "underline", "overline", "line-through", - "blink" + "blink", + "spelling-error", + "grammar-error" ], "text-decoration-style": [ "dashed", diff --git a/Libraries/LibWeb/CSS/Keywords.json b/Libraries/LibWeb/CSS/Keywords.json index 5c6267ed0b9..82a1d38bd59 100644 --- a/Libraries/LibWeb/CSS/Keywords.json +++ b/Libraries/LibWeb/CSS/Keywords.json @@ -190,6 +190,7 @@ "fullscreen", "grab", "grabbing", + "grammar-error", "graytext", "grid", "groove", @@ -404,6 +405,7 @@ "space-around", "space-between", "space-evenly", + "spelling-error", "square", "square-button", "srgb", diff --git a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp index 03b0b84e6a3..896253fe0b7 100644 --- a/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp +++ b/Libraries/LibWeb/CSS/Parser/PropertyParsing.cpp @@ -3510,6 +3510,8 @@ RefPtr Parser::parse_text_decoration_line_value(TokenStream Parser::parse_text_decoration_line_value(TokenStream Parser::parse_text_decoration_line_value(TokenStream 1 && includes_spelling_or_grammar_error_value) + return nullptr; + + if (style_values.size() == 1) + return *style_values.first(); + quick_sort(style_values, [](auto& left, auto& right) { return *keyword_to_text_decoration_line(left->to_keyword()) < *keyword_to_text_decoration_line(right->to_keyword()); }); diff --git a/Libraries/LibWeb/Painting/PaintableBox.cpp b/Libraries/LibWeb/Painting/PaintableBox.cpp index 70f6c55050f..d7ae415cc31 100644 --- a/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -682,6 +682,7 @@ void paint_text_decoration(PaintContext& context, TextPaintable const& paintable auto baseline = fragment.baseline(); auto line_color = paintable.computed_values().text_decoration_color(); + auto line_style = paintable.computed_values().text_decoration_style(); auto const& text_paintable = static_cast(fragment.paintable()); auto device_line_thickness = context.rounded_device_pixels(text_paintable.text_decoration_thickness()); @@ -690,6 +691,24 @@ void paint_text_decoration(PaintContext& context, TextPaintable const& paintable DevicePixelPoint line_start_point {}; DevicePixelPoint line_end_point {}; + if (line == CSS::TextDecorationLine::SpellingError) { + // https://drafts.csswg.org/css-text-decor-4/#valdef-text-decoration-line-spelling-error + // This value indicates the type of text decoration used by the user agent to highlight spelling mistakes. + // Its appearance is UA-defined, and may be platform-dependent. It is often rendered as a red wavy underline. + line_color = Color::Red; + device_line_thickness = context.rounded_device_pixels(1); + line_style = CSS::TextDecorationStyle::Wavy; + line = CSS::TextDecorationLine::Underline; + } else if (line == CSS::TextDecorationLine::GrammarError) { + // https://drafts.csswg.org/css-text-decor-4/#valdef-text-decoration-line-grammar-error + // This value indicates the type of text decoration used by the user agent to highlight grammar mistakes. + // Its appearance is UA defined, and may be platform-dependent. It is often rendered as a green wavy underline. + line_color = Color::DarkGreen; + device_line_thickness = context.rounded_device_pixels(1); + line_style = CSS::TextDecorationStyle::Wavy; + line = CSS::TextDecorationLine::Underline; + } + switch (line) { case CSS::TextDecorationLine::None: return; @@ -710,9 +729,13 @@ void paint_text_decoration(PaintContext& context, TextPaintable const& paintable case CSS::TextDecorationLine::Blink: // Conforming user agents may simply not blink the text return; + case CSS::TextDecorationLine::SpellingError: + case CSS::TextDecorationLine::GrammarError: + // Handled above. + VERIFY_NOT_REACHED(); } - switch (paintable.computed_values().text_decoration_style()) { + switch (line_style) { case CSS::TextDecorationStyle::Solid: painter.draw_line(line_start_point.to_type(), line_end_point.to_type(), line_color, device_line_thickness.value(), Gfx::LineStyle::Solid); break; @@ -742,7 +765,24 @@ void paint_text_decoration(PaintContext& context, TextPaintable const& paintable painter.draw_line(line_start_point.to_type(), line_end_point.to_type(), line_color, device_line_thickness.value(), Gfx::LineStyle::Dotted); break; case CSS::TextDecorationStyle::Wavy: - painter.draw_triangle_wave(line_start_point.to_type(), line_end_point.to_type(), line_color, device_line_thickness.value() + 1, device_line_thickness.value()); + auto amplitude = device_line_thickness.value() * 3; + switch (line) { + case CSS::TextDecorationLine::Underline: + line_start_point.translate_by(0, device_line_thickness + context.rounded_device_pixels(1)); + line_end_point.translate_by(0, device_line_thickness + context.rounded_device_pixels(1)); + break; + case CSS::TextDecorationLine::Overline: + line_start_point.translate_by(0, -device_line_thickness - context.rounded_device_pixels(1)); + line_end_point.translate_by(0, -device_line_thickness - context.rounded_device_pixels(1)); + break; + case CSS::TextDecorationLine::LineThrough: + line_start_point.translate_by(0, -device_line_thickness / 2); + line_end_point.translate_by(0, -device_line_thickness / 2); + break; + default: + VERIFY_NOT_REACHED(); + } + painter.draw_triangle_wave(line_start_point.to_type(), line_end_point.to_type(), line_color, amplitude, device_line_thickness.value()); break; } } diff --git a/Tests/LibWeb/Screenshot/images/text-decorations.png b/Tests/LibWeb/Screenshot/images/text-decorations.png index 74bf0e3938f..dfb47c2b2b6 100644 Binary files a/Tests/LibWeb/Screenshot/images/text-decorations.png and b/Tests/LibWeb/Screenshot/images/text-decorations.png differ diff --git a/Tests/LibWeb/Screenshot/input/text-decorations.html b/Tests/LibWeb/Screenshot/input/text-decorations.html index 0ec0b414cfa..05cf67a12e8 100644 --- a/Tests/LibWeb/Screenshot/input/text-decorations.html +++ b/Tests/LibWeb/Screenshot/input/text-decorations.html @@ -8,6 +8,8 @@ .strikethrough { text-decoration: line-through dotted green 5px; } .current-color { color: #8B4513; text-decoration: underline; } .overboard { text-decoration: double overline underline line-through magenta; } + .spelling-error { text-decoration: spelling-error; } + .grammar-error { text-decoration: grammar-error; } @@ -17,5 +19,7 @@

This underline should match the text color

This should have an underline, overline and line-through, all in glorious magenta.

+

This should look like a spelling error.

+

This should look like a grammar error.

diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-computed.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-computed.txt index 4bf73195b20..7e200ec73e3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-computed.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-computed.txt @@ -2,8 +2,7 @@ Harness status: OK Found 18 tests -16 Pass -2 Fail +18 Pass Pass Property text-decoration-line value 'none' Pass Property text-decoration-line value 'underline' Pass Property text-decoration-line value 'overline' @@ -20,5 +19,5 @@ Pass Property text-decoration-line value 'underline overline blink' Pass Property text-decoration-line value 'underline line-through blink' Pass Property text-decoration-line value 'overline line-through blink' Pass Property text-decoration-line value 'underline overline line-through blink' -Fail Property text-decoration-line value 'spelling-error' -Fail Property text-decoration-line value 'grammar-error' \ No newline at end of file +Pass Property text-decoration-line value 'spelling-error' +Pass Property text-decoration-line value 'grammar-error' \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-valid.txt b/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-valid.txt index 1bbd4f0b608..1ddf11c6b53 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-valid.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/css/css-text-decor/parsing/text-decoration-line-valid.txt @@ -2,8 +2,7 @@ Harness status: OK Found 67 tests -65 Pass -2 Fail +67 Pass Pass e.style['text-decoration-line'] = "none" should set the property value Pass e.style['text-decoration-line'] = "underline" should set the property value Pass e.style['text-decoration-line'] = "overline" should set the property value @@ -69,5 +68,5 @@ Pass e.style['text-decoration-line'] = "blink overline underline line-through" s Pass e.style['text-decoration-line'] = "blink overline line-through underline" should set the property value Pass e.style['text-decoration-line'] = "blink line-through underline overline" should set the property value Pass e.style['text-decoration-line'] = "blink line-through overline underline" should set the property value -Fail e.style['text-decoration-line'] = "spelling-error" should set the property value -Fail e.style['text-decoration-line'] = "grammar-error" should set the property value \ No newline at end of file +Pass e.style['text-decoration-line'] = "spelling-error" should set the property value +Pass e.style['text-decoration-line'] = "grammar-error" should set the property value \ No newline at end of file