From 92bcb5a2879276961353d57a2f94774ed6bdd269 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 15 Jul 2024 17:46:35 +0100 Subject: [PATCH] WIP: Counters --- .../Libraries/LibWeb/CSS/StyleProperties.cpp | 13 +++++++++---- .../Libraries/LibWeb/CSS/StyleProperties.h | 2 +- .../CSS/StyleValues/CounterStyleValue.cpp | 19 +++++++++++++++++++ .../CSS/StyleValues/CounterStyleValue.h | 3 +++ Userland/Libraries/LibWeb/DOM/Element.cpp | 11 ++++++++--- Userland/Libraries/LibWeb/DOM/Element.h | 1 + .../Libraries/LibWeb/Layout/TreeBuilder.cpp | 2 +- 7 files changed, 42 insertions(+), 9 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp index ebebe3f7b2240..66347e489221e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2018-2023, Andreas Kling - * Copyright (c) 2021-2023, Sam Atkins + * Copyright (c) 2021-2024, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -646,7 +647,7 @@ Optional StyleProperties::clear() const return value_id_to_clear(value->to_identifier()); } -StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(u32 initial_quote_nesting_level) const +StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(DOM::Element& element, u32 initial_quote_nesting_level) const { auto value = property(CSS::PropertyID::Content); auto quotes_data = quotes(); @@ -709,8 +710,10 @@ StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(u32 in dbgln("`{}` is not supported in `content` (yet?)", item->to_string()); break; } + } else if (item->is_counter()) { + builder.append(item->as_counter().resolve(element.ensure_counters_set())); } else { - // TODO: Implement counters, images, and other things. + // TODO: Implement images, and other things. dbgln("`{}` is not supported in `content` (yet?)", item->to_string()); } } @@ -722,8 +725,10 @@ StyleProperties::ContentDataAndQuoteNestingLevel StyleProperties::content(u32 in for (auto const& item : content_style_value.alt_text()->values()) { if (item->is_string()) { alt_text_builder.append(item->as_string().string_value()); + } else if (item->is_counter()) { + alt_text_builder.append(item->as_counter().resolve(element.ensure_counters_set())); } else { - // TODO: Implement counters + dbgln("`{}` is not supported in `content` alt-text (yet?)", item->to_string()); } } content_data.alt_text = MUST(alt_text_builder.to_string()); diff --git a/Userland/Libraries/LibWeb/CSS/StyleProperties.h b/Userland/Libraries/LibWeb/CSS/StyleProperties.h index cb3ae3d3c837a..16eec76cf3bc9 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Userland/Libraries/LibWeb/CSS/StyleProperties.h @@ -85,7 +85,7 @@ class StyleProperties : public RefCounted { CSS::ContentData content_data; u32 final_quote_nesting_level { 0 }; }; - ContentDataAndQuoteNestingLevel content(u32 initial_quote_nesting_level) const; + ContentDataAndQuoteNestingLevel content(DOM::Element&, u32 initial_quote_nesting_level) const; Optional cursor() const; Optional white_space() const; Optional line_style(CSS::PropertyID) const; diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.cpp index adef158fa2bd9..794cbff0ed830 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.cpp @@ -22,6 +22,25 @@ CounterStyleValue::CounterStyleValue(CounterFunction function, ValueComparingNon CounterStyleValue::~CounterStyleValue() = default; +String CounterStyleValue::resolve(CountersSet& counters_set) const +{ + // counter( , ? ) + if (m_properties.function == CounterFunction::Counter) { + StringBuilder stb; + auto counter = counters_set.last_counter_with_name(m_properties.counter_name->to_string()); + if (!counter.has_value()) { + // "If no counter named exists on an element where counter() or counters() is used, + // one is first instantiated with a starting value of 0." + counters_set. + } + + return stb.to_string_without_validation(); + } + // counters( , , ? ) + StringBuilder stb; + return stb.to_string_without_validation(); +} + // https://drafts.csswg.org/cssom-1/#ref-for-typedef-counter String CounterStyleValue::to_string() const { diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.h index be2eea0b38673..a27e67c149479 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/CounterStyleValue.h @@ -6,6 +6,7 @@ #pragma once +#include "LibWeb/CSS/CountersSet.h" #include namespace Web::CSS { @@ -33,6 +34,8 @@ class CounterStyleValue : public StyleValueWithDefaultOperators Element::counters_set() return *m_counters_set; } +CSS::CountersSet& Element::ensure_counters_set() +{ + if (!m_counters_set) + m_counters_set = make(); + return *m_counters_set; +} + // https://drafts.csswg.org/css-lists-3/#instantiate-counter void Element::instantiate_a_counter(FlyString name, bool reversed, Optional value) { // 1. Let counters be element’s CSS counters set. // NOTE: We use nullptr for an empty CountersSet, so must ensure it exists here. - if (!has_non_empty_counters_set()) - m_counters_set = make(); - auto& counters = *m_counters_set; + auto& counters = ensure_counters_set(); // 2. Let innermost counter be the last counter in counters with the name name. // If innermost counter’s originating element is element or a previous sibling of element, diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 5321a00dd4745..2a26b135e1654 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -393,6 +393,7 @@ class Element bool has_non_empty_counters_set() const { return m_counters_set; } Optional counters_set(); + CSS::CountersSet& ensure_counters_set(); void instantiate_a_counter(FlyString name, bool reversed, Optional); void inherit_counters(); diff --git a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp index 5d8126f6e9e4a..35431ed054903 100644 --- a/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/TreeBuilder.cpp @@ -198,7 +198,7 @@ void TreeBuilder::create_pseudo_element_if_needed(DOM::Element& element, CSS::Se return; auto initial_quote_nesting_level = m_quote_nesting_level; - auto [pseudo_element_content, final_quote_nesting_level] = pseudo_element_style->content(initial_quote_nesting_level); + auto [pseudo_element_content, final_quote_nesting_level] = pseudo_element_style->content(element, initial_quote_nesting_level); m_quote_nesting_level = final_quote_nesting_level; auto pseudo_element_display = pseudo_element_style->display(); // ::before and ::after only exist if they have content. `content: normal` computes to `none` for them.