Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}