/src/serenity/Userland/Libraries/LibWeb/CSS/CountersSet.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2024, Sam Atkins <sam@ladybird.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include "CountersSet.h" |
8 | | #include <LibWeb/DOM/Element.h> |
9 | | #include <LibWeb/DOM/Node.h> |
10 | | |
11 | | namespace Web::CSS { |
12 | | |
13 | | // https://drafts.csswg.org/css-lists-3/#instantiate-counter |
14 | | Counter& CountersSet::instantiate_a_counter(FlyString name, i32 originating_element_id, bool reversed, Optional<CounterValue> value) |
15 | 0 | { |
16 | | // 1. Let counters be element’s CSS counters set. |
17 | 0 | auto* element = DOM::Node::from_unique_id(originating_element_id); |
18 | | |
19 | | // 2. Let innermost counter be the last counter in counters with the name name. |
20 | | // If innermost counter’s originating element is element or a previous sibling of element, |
21 | | // remove innermost counter from counters. |
22 | 0 | auto innermost_counter = last_counter_with_name(name); |
23 | 0 | if (innermost_counter.has_value()) { |
24 | 0 | auto* originating_node = DOM::Node::from_unique_id(innermost_counter->originating_element_id); |
25 | 0 | VERIFY(originating_node); |
26 | 0 | auto& innermost_element = verify_cast<DOM::Element>(*originating_node); |
27 | |
|
28 | 0 | if (&innermost_element == element |
29 | 0 | || (innermost_element.parent() == element->parent() && innermost_element.is_before(*element))) { |
30 | |
|
31 | 0 | m_counters.remove_first_matching([&innermost_counter](auto& it) { |
32 | 0 | return it.name == innermost_counter->name |
33 | 0 | && it.originating_element_id == innermost_counter->originating_element_id; |
34 | 0 | }); |
35 | 0 | } |
36 | 0 | } |
37 | | |
38 | | // 3. Append a new counter to counters with name name, originating element element, |
39 | | // reversed being reversed, and initial value value (if given) |
40 | 0 | m_counters.append({ |
41 | 0 | .name = move(name), |
42 | 0 | .originating_element_id = originating_element_id, |
43 | 0 | .reversed = reversed, |
44 | 0 | .value = value, |
45 | 0 | }); |
46 | |
|
47 | 0 | return m_counters.last(); |
48 | 0 | } |
49 | | |
50 | | // https://drafts.csswg.org/css-lists-3/#propdef-counter-set |
51 | | void CountersSet::set_a_counter(FlyString name, i32 originating_element_id, CounterValue value) |
52 | 0 | { |
53 | 0 | if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) { |
54 | 0 | existing_counter->value = value; |
55 | 0 | return; |
56 | 0 | } |
57 | | |
58 | | // If there is not currently a counter of the given name on the element, the element instantiates |
59 | | // a new counter of the given name with a starting value of 0 before setting or incrementing its value. |
60 | | // https://drafts.csswg.org/css-lists-3/#valdef-counter-set-counter-name-integer |
61 | 0 | auto& counter = instantiate_a_counter(name, originating_element_id, false, 0); |
62 | 0 | counter.value = value; |
63 | 0 | } |
64 | | |
65 | | // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment |
66 | | void CountersSet::increment_a_counter(FlyString name, i32 originating_element_id, CounterValue amount) |
67 | 0 | { |
68 | 0 | if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) { |
69 | | // FIXME: How should we handle existing counters with no value? Can that happen? |
70 | 0 | VERIFY(existing_counter->value.has_value()); |
71 | 0 | existing_counter->value->saturating_add(amount.value()); |
72 | 0 | return; |
73 | 0 | } |
74 | | |
75 | | // If there is not currently a counter of the given name on the element, the element instantiates |
76 | | // a new counter of the given name with a starting value of 0 before setting or incrementing its value. |
77 | | // https://drafts.csswg.org/css-lists-3/#valdef-counter-set-counter-name-integer |
78 | 0 | auto& counter = instantiate_a_counter(name, originating_element_id, false, 0); |
79 | 0 | counter.value->saturating_add(amount.value()); |
80 | 0 | } |
81 | | |
82 | | Optional<Counter&> CountersSet::last_counter_with_name(FlyString const& name) |
83 | 0 | { |
84 | 0 | for (auto& counter : m_counters.in_reverse()) { |
85 | 0 | if (counter.name == name) |
86 | 0 | return counter; |
87 | 0 | } |
88 | 0 | return {}; |
89 | 0 | } |
90 | | |
91 | | Optional<Counter&> CountersSet::counter_with_same_name_and_creator(FlyString const& name, i32 originating_element_id) |
92 | 0 | { |
93 | 0 | return m_counters.first_matching([&](auto& it) { |
94 | 0 | return it.name == name && it.originating_element_id == originating_element_id; |
95 | 0 | }); |
96 | 0 | } |
97 | | |
98 | | void CountersSet::append_copy(Counter const& counter) |
99 | 0 | { |
100 | 0 | m_counters.append(counter); |
101 | 0 | } |
102 | | |
103 | | } |