mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 02:27:35 +00:00
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:
parent
0e15561df0
commit
44a082391b
5 changed files with 132 additions and 90 deletions
|
@ -4,9 +4,10 @@
|
||||||
<title>text-decoration test</title>
|
<title>text-decoration test</title>
|
||||||
<style>
|
<style>
|
||||||
.overline { text-decoration: wavy blue overline; }
|
.overline { text-decoration: wavy blue overline; }
|
||||||
.underline { text-decoration: double red underline; }
|
.underline { text-decoration: red underline double; }
|
||||||
.strikethrough { text-decoration: dotted green line-through; }
|
.strikethrough { text-decoration: line-through dotted green; }
|
||||||
.blink { text-decoration: blink; }
|
.blink { text-decoration: blink; }
|
||||||
|
.current-color { color: #8B4513; text-decoration: underline; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -14,5 +15,6 @@
|
||||||
<p class="underline">Underline</p>
|
<p class="underline">Underline</p>
|
||||||
<p class="strikethrough">Wombling</p>
|
<p class="strikethrough">Wombling</p>
|
||||||
<p class="blink">FREE!</p>
|
<p class="blink">FREE!</p>
|
||||||
|
<p class="current-color">This underline should match the text color</p>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -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());
|
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)
|
RefPtr<StyleValue> Parser::parse_as_css_value(PropertyID property_id)
|
||||||
{
|
{
|
||||||
auto component_values = parse_as_list_of_component_values();
|
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))
|
if (auto parsed_value = parse_list_style_value(m_context, component_values))
|
||||||
return parsed_value;
|
return parsed_value;
|
||||||
break;
|
break;
|
||||||
|
case PropertyID::TextDecoration:
|
||||||
|
if (auto parsed_value = parse_text_decoration_value(m_context, component_values))
|
||||||
|
return parsed_value;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,7 @@ private:
|
||||||
static RefPtr<StyleValue> parse_box_shadow_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
|
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_font_value(ParsingContext const&, Vector<StyleComponentValueRule> const&);
|
||||||
static RefPtr<StyleValue> parse_list_style_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
|
// 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>&);
|
static OwnPtr<CalculatedStyleValue::CalcSum> parse_calc_sum(ParsingContext const&, TokenStream<StyleComponentValueRule>&);
|
||||||
|
|
|
@ -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)
|
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);
|
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 (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);
|
style.set_property(CSS::PropertyID::TextDecorationColor, value);
|
||||||
return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,7 @@ public:
|
||||||
BoxShadow,
|
BoxShadow,
|
||||||
Font,
|
Font,
|
||||||
ListStyle,
|
ListStyle,
|
||||||
|
TextDecoration,
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
|
@ -253,6 +254,7 @@ public:
|
||||||
bool is_box_shadow() const { return type() == Type::BoxShadow; }
|
bool is_box_shadow() const { return type() == Type::BoxShadow; }
|
||||||
bool is_font() const { return type() == Type::Font; }
|
bool is_font() const { return type() == Type::Font; }
|
||||||
bool is_list_style() const { return type() == Type::ListStyle; }
|
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(); }
|
bool is_builtin() const { return is_inherit() || is_initial(); }
|
||||||
|
|
||||||
|
@ -756,6 +758,43 @@ private:
|
||||||
NonnullRefPtr<StyleValue> m_style_type;
|
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 {
|
class StyleValueList final : public StyleValue {
|
||||||
public:
|
public:
|
||||||
static NonnullRefPtr<StyleValueList> create(NonnullRefPtrVector<StyleValue>&& values) { return adopt_ref(*new StyleValueList(move(values))); }
|
static NonnullRefPtr<StyleValueList> create(NonnullRefPtrVector<StyleValue>&& values) { return adopt_ref(*new StyleValueList(move(values))); }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue