From 879f18d3bc09a23c3399ee080a39fc6ff85be3b3 Mon Sep 17 00:00:00 2001
From: Sam Atkins <atkinssj@gmail.com>
Date: Fri, 19 Jul 2024 16:09:42 +0100
Subject: [PATCH] LibWeb: Prevent elements with no layout box from modifying
 counters

---
 .../expected/css-counters/hidden-elements.txt | 67 +++++++++++++++++++
 .../input/css-counters/hidden-elements.html   |  8 +++
 Userland/Libraries/LibWeb/DOM/Element.cpp     |  7 ++
 3 files changed, 82 insertions(+)
 create mode 100644 Tests/LibWeb/Layout/expected/css-counters/hidden-elements.txt
 create mode 100644 Tests/LibWeb/Layout/input/css-counters/hidden-elements.html

diff --git a/Tests/LibWeb/Layout/expected/css-counters/hidden-elements.txt b/Tests/LibWeb/Layout/expected/css-counters/hidden-elements.txt
new file mode 100644
index 0000000000000..905f73be978e3
--- /dev/null
+++ b/Tests/LibWeb/Layout/expected/css-counters/hidden-elements.txt
@@ -0,0 +1,67 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
+    BlockContainer <body> at (8,16) content-size 784x149 children: not-inline
+      BlockContainer <p> at (8,16) content-size 784x17 children: inline
+        frag 0 from TextNode start: 0, length: 1, rect: [26,16 14.265625x17] baseline: 13.296875
+            "A"
+        InlineNode <(anonymous)>
+          frag 0 from TextNode start: 0, length: 3, rect: [8,16 18.125x17] baseline: 13.296875
+              "1: "
+          TextNode <#text>
+        TextNode <#text>
+      BlockContainer <p> at (8,49) content-size 784x17 children: inline
+        frag 0 from TextNode start: 0, length: 1, rect: [29,49 9.34375x17] baseline: 13.296875
+            "B"
+        InlineNode <(anonymous)>
+          frag 0 from TextNode start: 0, length: 3, rect: [8,49 20.59375x17] baseline: 13.296875
+              "2: "
+          TextNode <#text>
+        TextNode <#text>
+      BlockContainer <p> at (8,82) content-size 784x17 children: inline
+        frag 0 from TextNode start: 0, length: 1, rect: [29,82 10.3125x17] baseline: 13.296875
+            "C"
+        InlineNode <(anonymous)>
+          frag 0 from TextNode start: 0, length: 3, rect: [8,82 20.875x17] baseline: 13.296875
+              "3: "
+          TextNode <#text>
+        TextNode <#text>
+      BlockContainer <p> at (8,115) content-size 784x17 children: inline
+        frag 0 from TextNode start: 0, length: 1, rect: [28,115 11.140625x17] baseline: 13.296875
+            "D"
+        InlineNode <(anonymous)>
+          frag 0 from TextNode start: 0, length: 3, rect: [8,115 19.53125x17] baseline: 13.296875
+              "4: "
+          TextNode <#text>
+        TextNode <#text>
+      BlockContainer <p> at (8,148) content-size 784x17 children: inline
+        frag 0 from TextNode start: 0, length: 1, rect: [28,148 11.859375x17] baseline: 13.296875
+            "E"
+        InlineNode <(anonymous)>
+          frag 0 from TextNode start: 0, length: 3, rect: [8,148 20.234375x17] baseline: 13.296875
+              "5: "
+          TextNode <#text>
+        TextNode <#text>
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
+    PaintableWithLines (BlockContainer<BODY>) [8,16 784x149]
+      PaintableWithLines (BlockContainer<P>) [8,16 784x17]
+        InlinePaintable (InlineNode(anonymous))
+          TextPaintable (TextNode<#text>)
+        TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<P>) [8,49 784x17]
+        InlinePaintable (InlineNode(anonymous))
+          TextPaintable (TextNode<#text>)
+        TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<P>) [8,82 784x17]
+        InlinePaintable (InlineNode(anonymous))
+          TextPaintable (TextNode<#text>)
+        TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<P>) [8,115 784x17]
+        InlinePaintable (InlineNode(anonymous))
+          TextPaintable (TextNode<#text>)
+        TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<P>) [8,148 784x17]
+        InlinePaintable (InlineNode(anonymous))
+          TextPaintable (TextNode<#text>)
+        TextPaintable (TextNode<#text>)
diff --git a/Tests/LibWeb/Layout/input/css-counters/hidden-elements.html b/Tests/LibWeb/Layout/input/css-counters/hidden-elements.html
new file mode 100644
index 0000000000000..b5e3372757e58
--- /dev/null
+++ b/Tests/LibWeb/Layout/input/css-counters/hidden-elements.html
@@ -0,0 +1,8 @@
+<style>
+p {
+    counter-increment: a;
+}
+p::before {
+    content: counter(a) ": ";
+}
+</style><p>A<p>B<p>C<p style="display:none">SECRET<p>D<p>E
diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp
index dfb1898148c8a..565f7099767d3 100644
--- a/Userland/Libraries/LibWeb/DOM/Element.cpp
+++ b/Userland/Libraries/LibWeb/DOM/Element.cpp
@@ -2724,6 +2724,13 @@ void Element::resolve_counters(CSS::StyleProperties& style)
     // 1. Existing counters are inherited from previous elements.
     inherit_counters();
 
+    // https://drafts.csswg.org/css-lists-3/#counters-without-boxes
+    // An element that does not generate a box (for example, an element with display set to none,
+    // or a pseudo-element with content set to none) cannot set, reset, or increment a counter.
+    // The counter properties are still valid on such an element, but they must have no effect.
+    if (style.display().is_none())
+        return;
+
     // 2. New counters are instantiated (counter-reset).
     auto counter_reset = style.counter_data(CSS::PropertyID::CounterReset);
     for (auto const& counter : counter_reset)