From 0750513993720aa7971cc1944d56c50b2fa8fb0c Mon Sep 17 00:00:00 2001 From: Glenn Skrzypczak Date: Mon, 27 Jan 2025 18:50:27 +0100 Subject: [PATCH] LibWeb: Support reversed ordered lists This PR adds support for the `reversed` attribute of ordered lists. --- Libraries/LibWeb/DOM/Element.cpp | 110 +++++++++++++++++- Libraries/LibWeb/DOM/Element.h | 4 + Libraries/LibWeb/HTML/HTMLOListElement.cpp | 24 ++++ Libraries/LibWeb/HTML/HTMLOListElement.h | 2 + Libraries/LibWeb/Layout/ListItemMarkerBox.cpp | 51 ++++---- Libraries/LibWeb/Layout/ListItemMarkerBox.h | 10 +- Libraries/LibWeb/Layout/TreeBuilder.cpp | 28 +---- Libraries/LibWeb/Painting/MarkerPaintable.cpp | 2 +- .../Screenshot/expected/ordered-list-ref.html | 9 ++ .../Screenshot/images/ordered-list-ref.png | Bin 0 -> 13220 bytes .../LibWeb/Screenshot/input/ordered-list.html | 29 +++++ 11 files changed, 214 insertions(+), 55 deletions(-) create mode 100644 Tests/LibWeb/Screenshot/expected/ordered-list-ref.html create mode 100644 Tests/LibWeb/Screenshot/images/ordered-list-ref.png create mode 100644 Tests/LibWeb/Screenshot/input/ordered-list.html diff --git a/Libraries/LibWeb/DOM/Element.cpp b/Libraries/LibWeb/DOM/Element.cpp index 38f8da5939c..2045afe47f3 100644 --- a/Libraries/LibWeb/DOM/Element.cpp +++ b/Libraries/LibWeb/DOM/Element.cpp @@ -49,6 +49,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -57,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -3142,6 +3146,111 @@ bool Element::has_paint_containment() const return false; } +size_t Element::number_of_owned_list_items() const +{ + auto number_of_owned_li_elements = 0; + for_each_child_of_type([&](auto& child) { + if (child.list_owner() == this) { + number_of_owned_li_elements++; + } + return IterationDecision::Continue; + }); + return number_of_owned_li_elements; +} + +// https://html.spec.whatwg.org/multipage/grouping-content.html#list-owner +Element const* Element::list_owner() const +{ + // Any element whose computed value of 'display' is 'list-item' has a list owner, which is determined as follows: + if (!computed_properties()->display().is_list_item()) + return nullptr; + + // 1. If the element is not being rendered, return null; the element has no list owner. + if (!layout_node()) + return nullptr; + + // 2. Let ancestor be the element's parent. + auto const* ancestor = parent_element(); + + // 3. If the element has an ol, ul, or menu ancestor, set ancestor to the closest such ancestor element. + for_each_ancestor([&ancestor](GC::Ref node) { + if (is(*node) || is(*node) || is(*node)) { + ancestor = static_cast(node.ptr()); + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + + // 4. Return the closest inclusive ancestor of ancestor that produces a CSS box. + // Spec-Note: Such an element will always exist, as at the very least the document element will always produce a CSS box. + ancestor->for_each_inclusive_ancestor([&ancestor](GC::Ref node) { + if (is(*node) && node->paintable_box()) { + ancestor = static_cast(node.ptr()); + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + return ancestor; +} + +// https://html.spec.whatwg.org/multipage/grouping-content.html#ordinal-value +size_t Element::ordinal_value() const +{ + // NOTE: The spec provides an algorithm to determine the ordinal value of each element owned by a given list owner. + // However, we are only interested in the ordinal value of this element. + + // FIXME: 1. Let i be 1. + + // 2. If owner is an ol element, let numbering be owner's starting value. Otherwise, let numbering be 1. + auto const* owner = list_owner(); + if (!owner) { + return 1; + } + + auto numbering = 1; + auto reversed = false; + if (is(owner)) { + auto const* ol_element = static_cast(owner); + numbering = ol_element->starting_value(); + reversed = ol_element->has_attribute(HTML::AttributeNames::reversed); + } + + // FIXME: 3. Loop : If i is greater than the number of list items that owner owns, then return; all of owner's owned list items have been assigned ordinal values. + // FIXME: 4. Let item be the ith of owner's owned list items, in tree order. + + owner->for_each_child_of_type([&](auto& item) { + if (item.list_owner() == owner) { + // 5. If item is an li element that has a value attribute, then: + auto value_attribute = item.get_attribute(HTML::AttributeNames::value); + if (is(item) && value_attribute.has_value()) { + // 1. Let parsed be the result of parsing the value of the attribute as an integer. + auto parsed = HTML::parse_integer(value_attribute.value()); + + // 2. If parsed is not an error, then set numbering to parsed. + if (parsed.has_value()) + numbering = parsed.value(); + } + + // FIXME: 6. The ordinal value of item is numbering. + if (&item == this) + return IterationDecision::Break; + + // 7. If owner is an ol element, and owner has a reversed attribute, decrement numbering by 1; otherwise, increment numbering by 1. + if (reversed) { + numbering--; + } else { + numbering++; + } + + // FIXME: 8. Increment i by 1. + } + return IterationDecision::Continue; + }); + + // FIXME: 9. Go to the step labeled loop. + return numbering; +} + bool Element::id_reference_exists(String const& id_reference) const { return document().get_element_by_id(id_reference); @@ -3687,5 +3796,4 @@ Optional Element::lang() const return {}; return maybe_lang.release_value(); } - } diff --git a/Libraries/LibWeb/DOM/Element.h b/Libraries/LibWeb/DOM/Element.h index 0b728d24708..bc45e76fae6 100644 --- a/Libraries/LibWeb/DOM/Element.h +++ b/Libraries/LibWeb/DOM/Element.h @@ -454,6 +454,10 @@ public: return affected_by_sibling_combinator() || affected_by_first_or_last_child_pseudo_class() || affected_by_nth_child_pseudo_class(); } + size_t number_of_owned_list_items() const; + Element const* list_owner() const; + size_t ordinal_value() const; + protected: Element(Document&, DOM::QualifiedName); virtual void initialize(JS::Realm&) override; diff --git a/Libraries/LibWeb/HTML/HTMLOListElement.cpp b/Libraries/LibWeb/HTML/HTMLOListElement.cpp index 7f1b706f54f..290ab9a3185 100644 --- a/Libraries/LibWeb/HTML/HTMLOListElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLOListElement.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,29 @@ WebIDL::Long HTMLOListElement::start() return 1; } +// https://html.spec.whatwg.org/multipage/grouping-content.html#concept-ol-start +size_t HTMLOListElement::starting_value() const +{ + // 1. If the ol element has a start attribute, then: + auto start = get_attribute(AttributeNames::start); + if (start.has_value()) { + // 1. Let parsed be the result of parsing the value of the attribute as an integer. + auto parsed = parse_integer(start.value()); + + // 2. If parsed is not an error, then return parsed. + if (parsed.has_value()) + return parsed.value(); + } + + // 2. If the ol element has a reversed attribute, then return the number of owned li elements. + if (has_attribute(AttributeNames::reversed)) { + return number_of_owned_list_items(); + } + + // 3. Return 1. + return 1; +} + bool HTMLOListElement::is_presentational_hint(FlyString const& name) const { if (Base::is_presentational_hint(name)) diff --git a/Libraries/LibWeb/HTML/HTMLOListElement.h b/Libraries/LibWeb/HTML/HTMLOListElement.h index 23264cdfe42..49fc153d853 100644 --- a/Libraries/LibWeb/HTML/HTMLOListElement.h +++ b/Libraries/LibWeb/HTML/HTMLOListElement.h @@ -28,6 +28,8 @@ public: MUST(set_attribute(AttributeNames::start, String::number(start))); } + size_t starting_value() const; + private: HTMLOListElement(DOM::Document&, DOM::QualifiedName); diff --git a/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp b/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp index d87657fa34e..0f5abdc9e5c 100644 --- a/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp +++ b/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp @@ -12,56 +12,63 @@ namespace Web::Layout { GC_DEFINE_ALLOCATOR(ListItemMarkerBox); -ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document, CSS::ListStyleType style_type, CSS::ListStylePosition style_position, size_t index, GC::Ref style) +ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document, CSS::ListStyleType style_type, CSS::ListStylePosition style_position, GC::Ref list_item_element, GC::Ref style) : Box(document, nullptr, move(style)) , m_list_style_type(style_type) , m_list_style_position(style_position) - , m_index(index) + , m_list_item_element(list_item_element) { - m_list_style_type.visit( - [this](CSS::CounterStyleNameKeyword keyword) { +} + +ListItemMarkerBox::~ListItemMarkerBox() = default; + +Optional ListItemMarkerBox::text() const +{ + auto index = m_list_item_element->ordinal_value(); + + return m_list_style_type.visit( + [index](CSS::CounterStyleNameKeyword keyword) -> Optional { switch (keyword) { case CSS::CounterStyleNameKeyword::Square: case CSS::CounterStyleNameKeyword::Circle: case CSS::CounterStyleNameKeyword::Disc: case CSS::CounterStyleNameKeyword::DisclosureClosed: case CSS::CounterStyleNameKeyword::DisclosureOpen: - break; + return {}; case CSS::CounterStyleNameKeyword::Decimal: - m_text = MUST(String::formatted("{}.", m_index)); - break; + return MUST(String::formatted("{}.", index)); case CSS::CounterStyleNameKeyword::DecimalLeadingZero: // This is weird, but in accordance to spec. - m_text = m_index < 10 ? MUST(String::formatted("0{}.", m_index)) : MUST(String::formatted("{}.", m_index)); - break; + return MUST(index < 10 ? String::formatted("0{}.", index) : String::formatted("{}.", index)); case CSS::CounterStyleNameKeyword::LowerAlpha: case CSS::CounterStyleNameKeyword::LowerLatin: - m_text = String::bijective_base_from(m_index - 1, String::Case::Lower); - break; + return String::bijective_base_from(index - 1, String::Case::Lower); case CSS::CounterStyleNameKeyword::UpperAlpha: case CSS::CounterStyleNameKeyword::UpperLatin: - m_text = String::bijective_base_from(m_index - 1, String::Case::Upper); - break; + return String::bijective_base_from(index - 1, String::Case::Upper); case CSS::CounterStyleNameKeyword::LowerRoman: - m_text = String::roman_number_from(m_index, String::Case::Lower); - break; + return String::roman_number_from(index, String::Case::Lower); case CSS::CounterStyleNameKeyword::UpperRoman: - m_text = String::roman_number_from(m_index, String::Case::Upper); - break; + return String::roman_number_from(index, String::Case::Upper); case CSS::CounterStyleNameKeyword::None: - break; + return {}; + default: + VERIFY_NOT_REACHED(); } }, - [this](String const& string) { - m_text = string; + [](String const& string) -> Optional { + return string; }); } -ListItemMarkerBox::~ListItemMarkerBox() = default; - GC::Ptr ListItemMarkerBox::create_paintable() const { return Painting::MarkerPaintable::create(*this); } +void ListItemMarkerBox::visit_edges(Cell::Visitor& visitor) +{ + Base::visit_edges(visitor); + visitor.visit(m_list_item_element); +} } diff --git a/Libraries/LibWeb/Layout/ListItemMarkerBox.h b/Libraries/LibWeb/Layout/ListItemMarkerBox.h index 2ae9527c54a..dcaa29882ef 100644 --- a/Libraries/LibWeb/Layout/ListItemMarkerBox.h +++ b/Libraries/LibWeb/Layout/ListItemMarkerBox.h @@ -17,10 +17,10 @@ class ListItemMarkerBox final : public Box { GC_DECLARE_ALLOCATOR(ListItemMarkerBox); public: - explicit ListItemMarkerBox(DOM::Document&, CSS::ListStyleType, CSS::ListStylePosition, size_t index, GC::Ref); + explicit ListItemMarkerBox(DOM::Document&, CSS::ListStyleType, CSS::ListStylePosition, GC::Ref, GC::Ref); virtual ~ListItemMarkerBox() override; - Optional const& text() const { return m_text; } + Optional text() const; virtual GC::Ptr create_paintable() const override; @@ -28,14 +28,14 @@ public: CSS::ListStylePosition list_style_position() const { return m_list_style_position; } private: + virtual void visit_edges(Cell::Visitor&) override; + virtual bool is_list_item_marker_box() const final { return true; } virtual bool can_have_children() const override { return false; } CSS::ListStyleType m_list_style_type { CSS::CounterStyleNameKeyword::None }; CSS::ListStylePosition m_list_style_position { CSS::ListStylePosition::Outside }; - size_t m_index; - - Optional m_text {}; + GC::Ref m_list_item_element; }; template<> diff --git a/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Libraries/LibWeb/Layout/TreeBuilder.cpp index 132b5bd316e..60e302bf77c 100644 --- a/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -214,7 +214,7 @@ void TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element, CSS::Se document, pseudo_element_node->computed_values().list_style_type(), pseudo_element_node->computed_values().list_style_position(), - 0, + element, marker_style); static_cast(*pseudo_element_node).set_marker(list_item_marker); element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Type::Marker, list_item_marker); @@ -421,30 +421,6 @@ static bool is_ignorable_whitespace(Layout::Node const& node) return false; } -i32 TreeBuilder::calculate_list_item_index(DOM::Node& dom_node) -{ - if (is(dom_node)) { - auto& li = static_cast(dom_node); - if (li.value() != 0) - return li.value(); - } - - if (dom_node.previous_sibling() != nullptr) { - DOM::Node* current = dom_node.previous_sibling(); - while (current != nullptr) { - if (is(*current)) - return calculate_list_item_index(*current) + 1; - current = current->previous_sibling(); - } - } - - if (is(*dom_node.parent())) { - auto& ol = static_cast(*dom_node.parent()); - return ol.start(); - } - return 1; -} - void TreeBuilder::update_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& context, MustCreateSubtree must_create_subtree) { bool should_create_layout_node = must_create_subtree == MustCreateSubtree::Yes @@ -708,7 +684,7 @@ void TreeBuilder::update_layout_tree_after_children(DOM::Node& dom_node, GC::Ref if (is(*layout_node)) { auto& element = static_cast(dom_node); auto marker_style = style_computer.compute_style(element, CSS::Selector::PseudoElement::Type::Marker); - auto list_item_marker = document.heap().allocate(document, layout_node->computed_values().list_style_type(), layout_node->computed_values().list_style_position(), calculate_list_item_index(dom_node), marker_style); + auto list_item_marker = document.heap().allocate(document, layout_node->computed_values().list_style_type(), layout_node->computed_values().list_style_position(), element, marker_style); static_cast(*layout_node).set_marker(list_item_marker); element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Type::Marker, list_item_marker); layout_node->append_child(*list_item_marker); diff --git a/Libraries/LibWeb/Painting/MarkerPaintable.cpp b/Libraries/LibWeb/Painting/MarkerPaintable.cpp index b84441183f4..d54e8c9c104 100644 --- a/Libraries/LibWeb/Painting/MarkerPaintable.cpp +++ b/Libraries/LibWeb/Painting/MarkerPaintable.cpp @@ -66,7 +66,7 @@ void MarkerPaintable::paint(PaintContext& context, PaintPhase phase) const auto color = computed_values().color(); - if (auto& text = layout_box().text(); text.has_value()) { + if (auto text = layout_box().text(); text.has_value()) { // FIXME: This should use proper text layout logic! // This does not line up with the text in the
  • element which looks very sad :( context.display_list_recorder().draw_text(device_enclosing.to_type(), *text, layout_box().scaled_font(context), Gfx::TextAlignment::Center, color); diff --git a/Tests/LibWeb/Screenshot/expected/ordered-list-ref.html b/Tests/LibWeb/Screenshot/expected/ordered-list-ref.html new file mode 100644 index 00000000000..7381ac59787 --- /dev/null +++ b/Tests/LibWeb/Screenshot/expected/ordered-list-ref.html @@ -0,0 +1,9 @@ + + diff --git a/Tests/LibWeb/Screenshot/images/ordered-list-ref.png b/Tests/LibWeb/Screenshot/images/ordered-list-ref.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6b7ed183ab2a0dfb638bbf782fcc28017609f6 GIT binary patch literal 13220 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 vOurcu69YpI3kw6o4GsgTe~DWM4fCe!#~ literal 0 HcmV?d00001 diff --git a/Tests/LibWeb/Screenshot/input/ordered-list.html b/Tests/LibWeb/Screenshot/input/ordered-list.html new file mode 100644 index 00000000000..aa1ce773d6d --- /dev/null +++ b/Tests/LibWeb/Screenshot/input/ordered-list.html @@ -0,0 +1,29 @@ + +
      +
    1. 1
    2. +
    3. 2
    4. +
    5. 5
    6. +
    7. 6
    8. +
    9. 7
    10. +
    +
      +
    1. 20
    2. +
    3. 21
    4. +
    5. 5
    6. +
    7. 6
    8. +
    9. 7
    10. +
    +
      +
    1. 5
    2. +
    3. 4
    4. +
    5. 5
    6. +
    7. 4
    8. +
    9. 3
    10. +
    +
      +
    1. 20
    2. +
    3. 19
    4. +
    5. 5
    6. +
    7. 4
    8. +
    9. 3
    10. +