mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-29 08:11:13 +00:00
LibWeb: Implement and use ListStyleStyleValue
Yes, the name is silly, but it's a StyleValue for list-style, so... yeah. :^) Since `list-style-type` and `list-style-image` can both have `none` as a value, and can appear in any order, we have to handle it separately, and then assign either or both of those to `none` depending on how many `none`s there are, and whether those sub-properties already have values. Added some extra test cases to lists.html to cover list-style-image and list-style-position parts of the list-style shorthand, and the `none` values.
This commit is contained in:
parent
dcbfb61816
commit
0e15561df0
Notes:
sideshowbarker
2024-07-18 06:59:06 +09:00
Author: https://github.com/AtkinsSJ Commit: https://github.com/SerenityOS/serenity/commit/0e15561df08 Pull-request: https://github.com/SerenityOS/serenity/pull/9301 Reviewed-by: https://github.com/TobyAsE
BIN
Base/res/html/misc/custom-list-item.png
Normal file
BIN
Base/res/html/misc/custom-list-item.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 633 B |
BIN
Base/res/html/misc/custome-list-item-2.png
Normal file
BIN
Base/res/html/misc/custome-list-item-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 231 B |
|
@ -32,6 +32,18 @@
|
|||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: inside url(list-item.png)</p>
|
||||
<ul style="list-style: inside disc url(custom-list-item.png);">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: outside url(list-item.png)</p>
|
||||
<ul style="list-style: outside disc url(custom-list-item.png);">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<h2>ol</h2>
|
||||
<p>default</p>
|
||||
<ol>
|
||||
|
@ -175,6 +187,78 @@
|
|||
<li>Another Entry</li>
|
||||
</ol>
|
||||
|
||||
<p>list-style: outside lower-roman url(list-file.png)</p>
|
||||
<ol style="list-style: outside lower-roman url(custome-list-item-2.png);">
|
||||
<li>First</li>
|
||||
<li>Second</li>
|
||||
<li>Third</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
<li>Another Entry</li>
|
||||
</ol>
|
||||
|
||||
<h2>'none' values</h2>
|
||||
<p>list-style: none</p>
|
||||
<ul style="list-style: none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: none square</p>
|
||||
<ul style="list-style: none square;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: square none</p>
|
||||
<ul style="list-style: square none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: url(list-item.png) none</p>
|
||||
<ul style="list-style: url(custom-list-item.png) none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: none none </p>
|
||||
<ul style="list-style: none none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: inside none none</p>
|
||||
<ul style="list-style: inside none none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: square none none (error)</p>
|
||||
<ul style="list-style: square none none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
<p>list-style: none none none (is an error)</p>
|
||||
<ul style="list-style: none none none;">
|
||||
<li>Entry one</li>
|
||||
<li>Another entry</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -2063,6 +2063,116 @@ RefPtr<StyleValue> Parser::parse_font_value(ParsingContext const& context, Vecto
|
|||
return FontStyleValue::create(font_style.release_nonnull(), font_weight.release_nonnull(), font_size.release_nonnull(), line_height.release_nonnull(), move(font_families));
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_list_style_value(ParsingContext const& context, Vector<StyleComponentValueRule> const& component_values)
|
||||
{
|
||||
auto is_list_style_image = [](StyleValue const& value) -> bool {
|
||||
if (value.is_image())
|
||||
return true;
|
||||
if (value.is_identifier() && value.to_identifier() == ValueID::None)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
auto is_list_style_position = [](StyleValue const& value) -> bool {
|
||||
switch (value.to_identifier()) {
|
||||
case ValueID::Inside:
|
||||
case ValueID::Outside:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
auto is_list_style_type = [](StyleValue const& value) -> bool {
|
||||
// FIXME: Handle strings and symbols("...") syntax
|
||||
switch (value.to_identifier()) {
|
||||
case CSS::ValueID::None:
|
||||
case CSS::ValueID::Disc:
|
||||
case CSS::ValueID::Circle:
|
||||
case CSS::ValueID::Square:
|
||||
case CSS::ValueID::Decimal:
|
||||
case CSS::ValueID::DecimalLeadingZero:
|
||||
case CSS::ValueID::LowerAlpha:
|
||||
case CSS::ValueID::LowerLatin:
|
||||
case CSS::ValueID::UpperAlpha:
|
||||
case CSS::ValueID::UpperLatin:
|
||||
case CSS::ValueID::UpperRoman:
|
||||
case CSS::ValueID::LowerRoman:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
if (component_values.size() > 3)
|
||||
return nullptr;
|
||||
|
||||
RefPtr<StyleValue> list_position;
|
||||
RefPtr<StyleValue> list_image;
|
||||
RefPtr<StyleValue> list_type;
|
||||
int found_nones = 0;
|
||||
|
||||
for (auto& part : component_values) {
|
||||
auto value = parse_css_value(context, PropertyID::ListStyle, part);
|
||||
if (!value)
|
||||
return nullptr;
|
||||
|
||||
if (value->to_identifier() == ValueID::None) {
|
||||
found_nones++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_list_style_position(*value)) {
|
||||
if (list_position)
|
||||
return nullptr;
|
||||
list_position = value.release_nonnull();
|
||||
continue;
|
||||
}
|
||||
if (is_list_style_image(*value)) {
|
||||
if (list_image)
|
||||
return nullptr;
|
||||
list_image = value.release_nonnull();
|
||||
continue;
|
||||
}
|
||||
if (is_list_style_type(*value)) {
|
||||
if (list_type)
|
||||
return nullptr;
|
||||
list_type = value.release_nonnull();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_nones > 2)
|
||||
return nullptr;
|
||||
|
||||
if (found_nones == 2) {
|
||||
if (list_image || list_type)
|
||||
return nullptr;
|
||||
auto none = IdentifierStyleValue::create(ValueID::None);
|
||||
list_image = none;
|
||||
list_type = none;
|
||||
|
||||
} else if (found_nones == 1) {
|
||||
if (list_image && list_type)
|
||||
return nullptr;
|
||||
auto none = IdentifierStyleValue::create(ValueID::None);
|
||||
if (!list_image)
|
||||
list_image = none;
|
||||
if (!list_type)
|
||||
list_type = none;
|
||||
}
|
||||
|
||||
if (!list_position)
|
||||
list_position = IdentifierStyleValue::create(ValueID::Outside);
|
||||
if (!list_image)
|
||||
list_image = IdentifierStyleValue::create(ValueID::None);
|
||||
if (!list_type)
|
||||
list_type = IdentifierStyleValue::create(ValueID::Disc);
|
||||
|
||||
return ListStyleStyleValue::create(list_position.release_nonnull(), list_image.release_nonnull(), list_type.release_nonnull());
|
||||
}
|
||||
|
||||
RefPtr<StyleValue> Parser::parse_as_css_value(PropertyID property_id)
|
||||
{
|
||||
auto component_values = parse_as_list_of_component_values();
|
||||
|
@ -2105,6 +2215,10 @@ RefPtr<StyleValue> Parser::parse_css_value(PropertyID property_id, TokenStream<S
|
|||
if (auto parsed_value = parse_font_value(m_context, component_values))
|
||||
return parsed_value;
|
||||
break;
|
||||
case PropertyID::ListStyle:
|
||||
if (auto parsed_value = parse_list_style_value(m_context, component_values))
|
||||
return parsed_value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -178,6 +178,7 @@ private:
|
|||
static RefPtr<StyleValue> parse_background_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_list_style_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>&);
|
||||
|
|
|
@ -336,56 +336,6 @@ static inline bool is_line_width(StyleValue const& value)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool is_list_style_image(StyleValue const& value)
|
||||
{
|
||||
if (value.is_builtin_or_dynamic())
|
||||
return true;
|
||||
if (value.is_image())
|
||||
return true;
|
||||
if (value.is_identifier() && value.to_identifier() == ValueID::None)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_list_style_position(StyleValue const& value)
|
||||
{
|
||||
if (value.is_builtin_or_dynamic())
|
||||
return true;
|
||||
|
||||
switch (value.to_identifier()) {
|
||||
case ValueID::Inside:
|
||||
case ValueID::Outside:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_list_style_type(StyleValue const& value)
|
||||
{
|
||||
if (value.is_builtin_or_dynamic())
|
||||
return true;
|
||||
// FIXME: Handle strings and symbols("...") syntax
|
||||
switch (value.to_identifier()) {
|
||||
case CSS::ValueID::None:
|
||||
case CSS::ValueID::Disc:
|
||||
case CSS::ValueID::Circle:
|
||||
case CSS::ValueID::Square:
|
||||
case CSS::ValueID::Decimal:
|
||||
case CSS::ValueID::DecimalLeadingZero:
|
||||
case CSS::ValueID::LowerAlpha:
|
||||
case CSS::ValueID::LowerLatin:
|
||||
case CSS::ValueID::UpperAlpha:
|
||||
case CSS::ValueID::UpperLatin:
|
||||
case CSS::ValueID::UpperRoman:
|
||||
case CSS::ValueID::LowerRoman:
|
||||
return true;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_text_decoration_line(StyleValue const& value)
|
||||
{
|
||||
if (value.is_builtin_or_dynamic())
|
||||
|
@ -868,59 +818,19 @@ static void set_property_expanding_shorthands(StyleProperties& style, CSS::Prope
|
|||
}
|
||||
|
||||
if (property_id == CSS::PropertyID::ListStyle) {
|
||||
if (value.is_component_value_list()) {
|
||||
auto parts = static_cast<CSS::ValueListStyleValue const&>(value).values();
|
||||
|
||||
if (!parts.is_empty() && parts.size() <= 3) {
|
||||
RefPtr<StyleValue> position_value;
|
||||
RefPtr<StyleValue> image_value;
|
||||
RefPtr<StyleValue> type_value;
|
||||
|
||||
// FIXME: `none` is ambiguous as it is a valid value for ListStyleImage and ListStyleType,
|
||||
// so requires special handling. https://www.w3.org/TR/css-lists-3/#propdef-list-style
|
||||
|
||||
for (auto& part : parts) {
|
||||
auto value = Parser::parse_css_value(context, property_id, part);
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
if (is_list_style_position(*value)) {
|
||||
if (position_value)
|
||||
return;
|
||||
position_value = move(value);
|
||||
continue;
|
||||
}
|
||||
if (is_list_style_image(*value)) {
|
||||
if (image_value)
|
||||
return;
|
||||
image_value = move(value);
|
||||
continue;
|
||||
}
|
||||
if (is_list_style_type(*value)) {
|
||||
if (type_value)
|
||||
return;
|
||||
type_value = move(value);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (position_value)
|
||||
style.set_property(CSS::PropertyID::ListStylePosition, *position_value);
|
||||
if (image_value)
|
||||
style.set_property(CSS::PropertyID::ListStyleImage, *image_value);
|
||||
if (type_value)
|
||||
style.set_property(CSS::PropertyID::ListStyleType, *type_value);
|
||||
}
|
||||
if (value.is_list_style()) {
|
||||
auto& list_style = static_cast<CSS::ListStyleStyleValue const&>(value);
|
||||
style.set_property(CSS::PropertyID::ListStylePosition, list_style.position());
|
||||
style.set_property(CSS::PropertyID::ListStyleImage, list_style.image());
|
||||
style.set_property(CSS::PropertyID::ListStyleType, list_style.style_type());
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_list_style_position(value))
|
||||
if (value.is_builtin()) {
|
||||
style.set_property(CSS::PropertyID::ListStylePosition, value);
|
||||
else if (is_list_style_image(value))
|
||||
style.set_property(CSS::PropertyID::ListStyleImage, value);
|
||||
else if (is_list_style_type(value))
|
||||
style.set_property(CSS::PropertyID::ListStyleType, value);
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,6 +232,7 @@ public:
|
|||
Background,
|
||||
BoxShadow,
|
||||
Font,
|
||||
ListStyle,
|
||||
};
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
@ -251,6 +252,7 @@ public:
|
|||
bool is_background() const { return type() == Type::Background; }
|
||||
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_builtin() const { return is_inherit() || is_initial(); }
|
||||
|
||||
|
@ -717,6 +719,43 @@ private:
|
|||
// FIXME: Implement font-stretch and font-variant.
|
||||
};
|
||||
|
||||
class ListStyleStyleValue final : public StyleValue {
|
||||
public:
|
||||
static NonnullRefPtr<ListStyleStyleValue> create(
|
||||
NonnullRefPtr<StyleValue> position,
|
||||
NonnullRefPtr<StyleValue> image,
|
||||
NonnullRefPtr<StyleValue> style_type)
|
||||
{
|
||||
return adopt_ref(*new ListStyleStyleValue(position, image, style_type));
|
||||
}
|
||||
virtual ~ListStyleStyleValue() override { }
|
||||
|
||||
NonnullRefPtr<StyleValue> position() const { return m_position; }
|
||||
NonnullRefPtr<StyleValue> image() const { return m_image; }
|
||||
NonnullRefPtr<StyleValue> style_type() const { return m_style_type; }
|
||||
|
||||
virtual String to_string() const override
|
||||
{
|
||||
return String::formatted("ListStyle position: {}, image: {}, style_type: {}", m_position->to_string(), m_image->to_string(), m_style_type->to_string());
|
||||
}
|
||||
|
||||
private:
|
||||
ListStyleStyleValue(
|
||||
NonnullRefPtr<StyleValue> position,
|
||||
NonnullRefPtr<StyleValue> image,
|
||||
NonnullRefPtr<StyleValue> style_type)
|
||||
: StyleValue(Type::ListStyle)
|
||||
, m_position(position)
|
||||
, m_image(image)
|
||||
, m_style_type(style_type)
|
||||
{
|
||||
}
|
||||
|
||||
NonnullRefPtr<StyleValue> m_position;
|
||||
NonnullRefPtr<StyleValue> m_image;
|
||||
NonnullRefPtr<StyleValue> m_style_type;
|
||||
};
|
||||
|
||||
class StyleValueList final : public StyleValue {
|
||||
public:
|
||||
static NonnullRefPtr<StyleValueList> create(NonnullRefPtrVector<StyleValue>&& values) { return adopt_ref(*new StyleValueList(move(values))); }
|
||||
|
|
Loading…
Reference in a new issue