diff --git a/Base/res/html/misc/lists.html b/Base/res/html/misc/lists.html
index 19e7fb67c2..4af53801bf 100644
--- a/Base/res/html/misc/lists.html
+++ b/Base/res/html/misc/lists.html
@@ -106,6 +106,21 @@
Third
+ with start offset
+
+ - Item 20
+ - Item 21
+ - Item 22
+
+
+ with different order
+
+ - Item 1
+ - Item 5
+ - Item 2
+ - Item 3
+
+
list-style: decimal
- First
@@ -118,8 +133,8 @@
- First
- Second
- Third
- - Another Entry
- - Another Entry
+ - Another Entry
+ - Another Entry
- Another Entry
- Another Entry
- Another Entry
@@ -132,8 +147,8 @@
- First
- Second
- Third
- - Another Entry
- - Another Entry
+ - Another Entry
+ - Another Entry
- Another Entry
- Another Entry
- Another Entry
@@ -166,8 +181,8 @@
- First
- Second
- Third
- - Another Entry
- - Another Entry
+ - Another Entry
+ - Another Entry
- Another Entry
- Another Entry
- Another Entry
@@ -200,8 +215,8 @@
- First
- Second
- Third
- - Another Entry
- - Another Entry
+ - Another Entry
+ - Another Entry
- Another Entry
- Another Entry
- Another Entry
@@ -223,8 +238,8 @@
- First
- Second
- Third
- - Another Entry
- - Another Entry
+ - Another Entry
+ - Another Entry
- Another Entry
- Another Entry
- Another Entry
@@ -246,8 +261,8 @@
- First
- Second
- Third
- - Another Entry
- - Another Entry
+ - Another Entry
+ - Another Entry
- Another Entry
- Another Entry
- Another Entry
diff --git a/Tests/LibWeb/Layout/expected/ordered-list.txt b/Tests/LibWeb/Layout/expected/ordered-list.txt
new file mode 100644
index 0000000000..3dfec01758
--- /dev/null
+++ b/Tests/LibWeb/Layout/expected/ordered-list.txt
@@ -0,0 +1,107 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+ BlockContainer at (0,0) content-size 800x600 [BFC] children: not-inline
+ BlockContainer at (8,16) content-size 784x138.28125 children: not-inline
+ BlockContainer at (48,16) content-size 744x52.40625 children: not-inline
+ BlockContainer <(anonymous)> at (48,16) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox - at (48,16) content-size 744x17.46875 children: inline
+ line 0 width: 58.78125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 7, rect: [48,16 58.78125x17.46875]
+ "Item 20"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (13.25,16.234375) content-size 22.75x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,33.46875) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox
- at (48,33.46875) content-size 744x17.46875 children: inline
+ line 0 width: 55.53125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 7, rect: [48,33.46875 55.53125x17.46875]
+ "Item 21"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (16.5,33.703125) content-size 19.5x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,50.9375) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox
- at (48,50.9375) content-size 744x17.46875 children: inline
+ line 0 width: 58, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 7, rect: [48,50.9375 58x17.46875]
+ "Item 22"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (14.03125,51.171875) content-size 21.96875x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,68.40625) content-size 744x0 children: inline
+ TextNode <#text>
+ BlockContainer <(anonymous)> at (8,84.40625) content-size 784x0 children: inline
+ TextNode <#text>
+ BlockContainer
at (48,84.40625) content-size 744x69.875 children: not-inline
+ BlockContainer <(anonymous)> at (48,84.40625) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox - at (48,84.40625) content-size 744x17.46875 children: inline
+ line 0 width: 46.71875, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 6, rect: [48,84.40625 46.71875x17.46875]
+ "Item 1"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (25.3125,84.640625) content-size 10.6875x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,101.875) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox
- at (48,101.875) content-size 744x17.46875 children: inline
+ line 0 width: 48.828125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 6, rect: [48,101.875 48.828125x17.46875]
+ "Item 5"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (23.203125,102.109375) content-size 12.796875x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,119.34375) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox
- at (48,119.34375) content-size 744x17.46875 children: inline
+ line 0 width: 49.109375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 6, rect: [48,119.34375 49.109375x17.46875]
+ "Item 6"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (22.921875,119.578125) content-size 13.078125x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,136.8125) content-size 744x0 children: inline
+ TextNode <#text>
+ ListItemBox
- at (48,136.8125) content-size 744x17.46875 children: inline
+ line 0 width: 49.09375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
+ frag 0 from TextNode start: 0, length: 6, rect: [48,136.8125 49.09375x17.46875]
+ "Item 7"
+ TextNode <#text>
+ ListItemMarkerBox <(anonymous)> at (22.9375,137.046875) content-size 13.0625x17 children: not-inline
+ BlockContainer <(anonymous)> at (48,154.28125) content-size 744x0 children: inline
+ TextNode <#text>
+ BlockContainer <(anonymous)> at (8,170.28125) content-size 784x0 children: inline
+ TextNode <#text>
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+ PaintableWithLines (BlockContainer) [0,0 800x600]
+ PaintableWithLines (BlockContainer) [8,16 784x138.28125] overflow: [8,16 784x154.28125]
+ PaintableWithLines (BlockContainer
) [8,16 784x52.40625]
+ PaintableWithLines (BlockContainer(anonymous)) [48,16 744x0]
+ PaintableWithLines (ListItemBox- ) [48,16 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [13.25,16.234375 22.75x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,33.46875 744x0]
+ PaintableWithLines (ListItemBox
- ) [48,33.46875 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [16.5,33.703125 19.5x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,50.9375 744x0]
+ PaintableWithLines (ListItemBox
- ) [48,50.9375 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [14.03125,51.171875 21.96875x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,68.40625 744x0]
+ PaintableWithLines (BlockContainer(anonymous)) [8,84.40625 784x0]
+ PaintableWithLines (BlockContainer
) [8,84.40625 784x69.875]
+ PaintableWithLines (BlockContainer(anonymous)) [48,84.40625 744x0]
+ PaintableWithLines (ListItemBox- ) [48,84.40625 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [25.3125,84.640625 10.6875x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,101.875 744x0]
+ PaintableWithLines (ListItemBox
- ) [48,101.875 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [23.203125,102.109375 12.796875x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,119.34375 744x0]
+ PaintableWithLines (ListItemBox
- ) [48,119.34375 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [22.921875,119.578125 13.078125x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,136.8125 744x0]
+ PaintableWithLines (ListItemBox
- ) [48,136.8125 744x17.46875]
+ TextPaintable (TextNode<#text>)
+ MarkerPaintable (ListItemMarkerBox(anonymous)) [22.9375,137.046875 13.0625x17]
+ PaintableWithLines (BlockContainer(anonymous)) [48,154.28125 744x0]
+ PaintableWithLines (BlockContainer(anonymous)) [8,170.28125 784x0]
diff --git a/Tests/LibWeb/Layout/input/ordered-list.html b/Tests/LibWeb/Layout/input/ordered-list.html
new file mode 100644
index 0000000000..003d250d38
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/ordered-list.html
@@ -0,0 +1,11 @@
+
+ - Item 20
+ - Item 21
+ - Item 22
+
+
+ - Item 1
+ - Item 5
+ - Item 6
+ - Item 7
+
diff --git a/Userland/Libraries/LibWeb/HTML/AttributeNames.h b/Userland/Libraries/LibWeb/HTML/AttributeNames.h
index fdb9f0eb37..ed081927df 100644
--- a/Userland/Libraries/LibWeb/HTML/AttributeNames.h
+++ b/Userland/Libraries/LibWeb/HTML/AttributeNames.h
@@ -223,6 +223,7 @@ namespace AttributeNames {
__ENUMERATE_HTML_ATTRIBUTE(srclang) \
__ENUMERATE_HTML_ATTRIBUTE(srcset) \
__ENUMERATE_HTML_ATTRIBUTE(standby) \
+ __ENUMERATE_HTML_ATTRIBUTE(start) \
__ENUMERATE_HTML_ATTRIBUTE(step) \
__ENUMERATE_HTML_ATTRIBUTE(style) \
__ENUMERATE_HTML_ATTRIBUTE(summary) \
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h
index 50aaf76e24..d7a5a00c26 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.h
@@ -20,6 +20,12 @@ public:
// https://www.w3.org/TR/html-aria/#el-li
virtual Optional default_role() const override { return ARIA::Role::listitem; }
+ i32 value() { return get_attribute(AttributeNames::value).value_or("0"_string).to_number().value_or(0); }
+ void set_value(i32 value)
+ {
+ set_attribute(AttributeNames::value, MUST(String::number(value))).release_value_but_fixme_should_propagate_errors();
+ }
+
private:
HTMLLIElement(DOM::Document&, DOM::QualifiedName);
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl
index 8f2799825b..31412f0eab 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl
+++ b/Userland/Libraries/LibWeb/HTML/HTMLLIElement.idl
@@ -6,7 +6,7 @@ interface HTMLLIElement : HTMLElement {
[HTMLConstructor] constructor();
- // FIXME: [CEReactions] attribute long value;
+ [CEReactions] attribute long value;
// Obsolete
[CEReactions, Reflect] attribute DOMString type;
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h
index a4af74b0b7..b09347beb7 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.h
@@ -20,6 +20,12 @@ public:
// https://www.w3.org/TR/html-aria/#el-ol
virtual Optional default_role() const override { return ARIA::Role::list; }
+ i32 start() { return get_attribute(AttributeNames::start).value_or("1"_string).to_number().value_or(1); }
+ void set_start(i32 start)
+ {
+ set_attribute(AttributeNames::start, MUST(String::number(start))).release_value_but_fixme_should_propagate_errors();
+ }
+
private:
HTMLOListElement(DOM::Document&, DOM::QualifiedName);
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl
index a59842bbec..bde11929ac 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOListElement.idl
@@ -7,7 +7,7 @@ interface HTMLOListElement : HTMLElement {
[HTMLConstructor] constructor();
[CEReactions, Reflect] attribute boolean reversed;
- // FIXME: [CEReactions] attribute long start;
+ [CEReactions] attribute long start;
[CEReactions, Reflect] attribute DOMString type;
// Obsolete
diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
index e836aad686..c13a8f1d1f 100644
--- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
+++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp
@@ -20,6 +20,8 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
@@ -251,6 +253,30 @@ 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;
+}
+
ErrorOr TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::Context& context)
{
JS::GCPtr layout_node;
@@ -349,9 +375,8 @@ ErrorOr TreeBuilder::create_layout_tree(DOM::Node& dom_node, TreeBuilder::
if (is(*layout_node)) {
auto& element = static_cast(dom_node);
- int child_index = layout_node->parent()->index_of_child(*layout_node).value();
auto marker_style = TRY(style_computer.compute_style(element, CSS::Selector::PseudoElement::Marker));
- auto list_item_marker = document.heap().allocate_without_realm(document, layout_node->computed_values().list_style_type(), layout_node->computed_values().list_style_position(), child_index + 1, *marker_style);
+ auto list_item_marker = document.heap().allocate_without_realm(document, layout_node->computed_values().list_style_type(), layout_node->computed_values().list_style_position(), calculate_list_item_index(dom_node), *marker_style);
static_cast(*layout_node).set_marker(list_item_marker);
element.set_pseudo_element_node({}, CSS::Selector::PseudoElement::Marker, list_item_marker);
layout_node->append_child(*list_item_marker);
diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h
index 8c82746e39..291aec79e5 100644
--- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.h
+++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.h
@@ -25,6 +25,8 @@ private:
bool has_svg_root = false;
};
+ i32 calculate_list_item_index(DOM::Node&);
+
ErrorOr create_layout_tree(DOM::Node&, Context&);
void push_parent(Layout::NodeWithStyle& node) { m_ancestor_stack.append(node); }