LibWeb: Implement and use TextDecorationStyleValue

Modified text-decoration.html to better test that the values can be in
any order, and that it adopts the color from the `color` property if no
decoration color is specified. Right now, it always does because we do
not support a different decoration color. Later, we need to support the
`currentcolor` special CSS value for this purpose.
This commit is contained in:
Sam Atkins 2021-08-04 12:34:14 +01:00 committed by Andreas Kling
parent 0e15561df0
commit 44a082391b
Notes: sideshowbarker 2024-07-18 06:59:02 +09:00
5 changed files with 132 additions and 90 deletions

View File

@ -4,9 +4,10 @@
<title>text-decoration test</title>
<style>
.overline { text-decoration: wavy blue overline; }
.underline { text-decoration: double red underline; }
.strikethrough { text-decoration: dotted green line-through; }
.underline { text-decoration: red underline double; }
.strikethrough { text-decoration: line-through dotted green; }
.blink { text-decoration: blink; }
.current-color { color: #8B4513; text-decoration: underline; }
</style>
</head>
<body>
@ -14,5 +15,6 @@
<p class="underline">Underline</p>
<p class="strikethrough">Wombling</p>
<p class="blink">FREE!</p>
<p class="current-color">This underline should match the text color</p>
</body>
</html>

View File

@ -2173,6 +2173,80 @@ RefPtr<StyleValue> Parser::parse_list_style_value(ParsingContext const& context,
return ListStyleStyleValue::create(list_position.release_nonnull(), list_image.release_nonnull(), list_type.release_nonnull());
}
RefPtr<StyleValue> Parser::parse_text_decoration_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
{
auto is_text_decoration_line = [](StyleValue const& value) -> bool {
switch (value.to_identifier()) {
case ValueID::None:
case ValueID::Underline:
case ValueID::Overline:
case ValueID::LineThrough:
case ValueID::Blink:
return true;
default:
return false;
}
};
auto is_text_decoration_style = [](StyleValue const& value) -> bool {
switch (value.to_identifier()) {
case ValueID::Solid:
case ValueID::Double:
case ValueID::Dotted:
case ValueID::Dashed:
case ValueID::Wavy:
return true;
default:
return false;
}
};
if (component_values.size() > 3)
return nullptr;
RefPtr<StyleValue> decoration_line;
RefPtr<StyleValue> decoration_style;
RefPtr<StyleValue> decoration_color;
// FIXME: Implement 'text-decoration-thickness' parameter. https://www.w3.org/TR/css-text-decor-4/#text-decoration-width-property
for (auto& part : component_values) {
auto value = parse_css_value(context, PropertyID::TextDecoration, part);
if (!value)
return nullptr;
if (value->is_color()) {
if (decoration_color)
return nullptr;
decoration_color = value.release_nonnull();
continue;
}
if (is_text_decoration_line(*value)) {
if (decoration_line)
return nullptr;
decoration_line = value.release_nonnull();
continue;
}
if (is_text_decoration_style(*value)) {
if (decoration_style)
return nullptr;
decoration_style = value.release_nonnull();
continue;
}
return nullptr;
}
if (!decoration_line)
decoration_line = IdentifierStyleValue::create(ValueID::None);
if (!decoration_style)
decoration_style = IdentifierStyleValue::create(ValueID::Solid);
// FIXME: Should default to 'currentcolor' special value: https://www.w3.org/TR/css-color-3/#currentcolor
if (!decoration_color)
decoration_color = InitialStyleValue::create();
return TextDecorationStyleValue::create(decoration_line.release_nonnull(), decoration_style.release_nonnull(), decoration_color.release_nonnull());
}
RefPtr<StyleValue> Parser::parse_as_css_value(PropertyID property_id)
{
auto component_values = parse_as_list_of_component_values();
@ -2219,6 +2293,10 @@ RefPtr<StyleValue> Parser::parse_css_value(PropertyID property_id, TokenStream<S
if (auto parsed_value = parse_list_style_value(m_context, component_values))
return parsed_value;
break;
case PropertyID::TextDecoration:
if (auto parsed_value = parse_text_decoration_value(m_context, component_values))
return parsed_value;
break;
default:
break;
}

View File

@ -179,6 +179,7 @@ private:
static RefPtr<StyleValue> parse_box_shadow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
static RefPtr<StyleValue> parse_font_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
static RefPtr<StyleValue> parse_list_style_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
static RefPtr<StyleValue> parse_text_decoration_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
// calc() parsing, according to https://www.w3.org/TR/css-values-3/#calc-syntax
static OwnPtr<CalculatedStyleValue::CalcSum> parse_calc_sum(ParsingContext const&, TokenStream<StyleComponentValueRule>&);

View File

@ -336,40 +336,6 @@ static inline bool is_line_width(StyleValue const& value)
}
}
static inline bool is_text_decoration_line(StyleValue const& value)
{
if (value.is_builtin_or_dynamic())
return true;
switch (value.to_identifier()) {
case ValueID::None:
case ValueID::Underline:
case ValueID::Overline:
case ValueID::LineThrough:
case ValueID::Blink:
return true;
default:
return false;
}
}
static inline bool is_text_decoration_style(StyleValue const& value)
{
if (value.is_builtin_or_dynamic())
return true;
switch (value.to_identifier()) {
case ValueID::Solid:
case ValueID::Double:
case ValueID::Dotted:
case ValueID::Dashed:
case ValueID::Wavy:
return true;
default:
return false;
}
}
static void set_property_expanding_shorthands(StyleProperties& style, CSS::PropertyID property_id, StyleValue const& value, DOM::Document& document, bool is_internally_generated_pseudo_property = false)
{
CSS::ParsingContext context(document);
@ -404,63 +370,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
};
if (property_id == CSS::PropertyID::TextDecoration) {
if (value.is_color()) {
if (value.is_text_decoration()) {
auto& text_decoration = static_cast<TextDecorationStyleValue const&>(value);
style.set_property(CSS::PropertyID::TextDecorationLine, text_decoration.line());
style.set_property(CSS::PropertyID::TextDecorationStyle, text_decoration.style());
style.set_property(CSS::PropertyID::TextDecorationColor, text_decoration.color());
return;
}
if (value.is_builtin()) {
style.set_property(CSS::PropertyID::TextDecorationLine, value);
style.set_property(CSS::PropertyID::TextDecorationStyle, value);
style.set_property(CSS::PropertyID::TextDecorationColor, value);
return;
}
if (is_text_decoration_line(value)) {
style.set_property(CSS::PropertyID::TextDecorationLine, value);
return;
}
if (is_text_decoration_style(value)) {
style.set_property(CSS::PropertyID::TextDecorationStyle, value);
return;
}
if (value.is_component_value_list()) {
auto& parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
if (!parts.is_empty() && parts.size() <= 3) {
RefPtr<StyleValue> color_value;
RefPtr<StyleValue> line_value;
RefPtr<StyleValue> style_value;
for (auto& part : parts) {
auto value = Parser::parse_css_value(context, property_id, part);
if (!value)
return;
if (value->is_color()) {
if (color_value)
return;
color_value = move(value);
continue;
}
if (is_text_decoration_line(*value)) {
if (line_value)
return;
line_value = move(value);
continue;
}
if (is_text_decoration_style(*value)) {
if (style_value)
return;
style_value = move(value);
continue;
}
return;
}
if (color_value)
style.set_property(CSS::PropertyID::TextDecorationColor, *color_value);
if (line_value)
style.set_property(CSS::PropertyID::TextDecorationLine, *line_value);
if (style_value)
style.set_property(CSS::PropertyID::TextDecorationStyle, *style_value);
}
return;
}
return;
}

View File

@ -233,6 +233,7 @@ public:
BoxShadow,
Font,
ListStyle,
TextDecoration,
};
Type type() const { return m_type; }
@ -253,6 +254,7 @@ public:
bool is_box_shadow() const { return type() == Type::BoxShadow; }
bool is_font() const { return type() == Type::Font; }
bool is_list_style() const { return type() == Type::ListStyle; }
bool is_text_decoration() const { return type() == Type::TextDecoration; }
bool is_builtin() const { return is_inherit() || is_initial(); }
@ -756,6 +758,43 @@ private:
NonnullRefPtr<StyleValue> m_style_type;
};
class TextDecorationStyleValue final : public StyleValue {
public:
static NonnullRefPtr<TextDecorationStyleValue> create(
NonnullRefPtr<StyleValue> line,
NonnullRefPtr<StyleValue> style,
NonnullRefPtr<StyleValue> color)
{
return adopt_ref(*new TextDecorationStyleValue(line, style, color));
}
virtual ~TextDecorationStyleValue() override { }
NonnullRefPtr<StyleValue> line() const { return m_line; }
NonnullRefPtr<StyleValue> style() const { return m_style; }
NonnullRefPtr<StyleValue> color() const { return m_color; }
virtual String to_string() const override
{
return String::formatted("TextDecoration line: {}, style: {}, color: {}", m_line->to_string(), m_style->to_string(), m_color->to_string());
}
private:
TextDecorationStyleValue(
NonnullRefPtr<StyleValue> line,
NonnullRefPtr<StyleValue> style,
NonnullRefPtr<StyleValue> color)
: StyleValue(Type::TextDecoration)
, m_line(line)
, m_style(style)
, m_color(color)
{
}
NonnullRefPtr<StyleValue> m_line;
NonnullRefPtr<StyleValue> m_style;
NonnullRefPtr<StyleValue> m_color;
};
class StyleValueList final : public StyleValue {
public:
static NonnullRefPtr<StyleValueList> create(NonnullRefPtrVector<StyleValue>&& values) { return adopt_ref(*new StyleValueList(move(values))); }