Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/CSS/Parser/TokenStream.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020-2021, the SerenityOS developers.
3
 * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#pragma once
9
10
#include <AK/Format.h>
11
#include <AK/Vector.h>
12
#include <LibWeb/CSS/Parser/ComponentValue.h>
13
#include <LibWeb/CSS/Parser/Tokenizer.h>
14
15
namespace Web::CSS::Parser {
16
17
// https://drafts.csswg.org/css-syntax/#css-token-stream
18
template<typename T>
19
class TokenStream {
20
public:
21
    class StateTransaction {
22
    public:
23
        explicit StateTransaction(TokenStream<T>& token_stream)
24
0
            : m_token_stream(token_stream)
25
0
            , m_saved_index(token_stream.m_index)
26
0
        {
27
0
        }
28
29
        ~StateTransaction()
30
0
        {
31
0
            if (!m_commit)
32
0
                m_token_stream.m_index = m_saved_index;
33
0
        }
34
35
0
        StateTransaction create_child() { return StateTransaction(*this); }
36
37
        void commit()
38
0
        {
39
0
            m_commit = true;
40
0
            if (m_parent)
41
0
                m_parent->commit();
42
0
        }
43
44
    private:
45
        explicit StateTransaction(StateTransaction& parent)
46
0
            : m_parent(&parent)
47
0
            , m_token_stream(parent.m_token_stream)
48
0
            , m_saved_index(parent.m_token_stream.m_index)
49
0
        {
50
0
        }
51
52
        StateTransaction* m_parent { nullptr };
53
        TokenStream<T>& m_token_stream;
54
        size_t m_saved_index { 0 };
55
        bool m_commit { false };
56
    };
57
58
    explicit TokenStream(Span<T const> tokens)
59
0
        : m_tokens(tokens)
60
0
        , m_eof(make_eof())
61
0
    {
62
0
    }
63
64
    explicit TokenStream(Vector<T> const& tokens)
65
0
        : m_tokens(tokens.span())
66
0
        , m_eof(make_eof())
67
0
    {
68
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::TokenStream(AK::Vector<Web::CSS::Parser::ComponentValue, 0ul> const&)
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::TokenStream(AK::Vector<Web::CSS::Parser::Token, 0ul> const&)
69
70
    static TokenStream<T> of_single_token(T const& token)
71
0
    {
72
0
        return TokenStream(Span<T const> { &token, 1 });
73
0
    }
74
75
    TokenStream(TokenStream<T> const&) = delete;
76
    TokenStream(TokenStream<T>&&) = default;
77
78
    // https://drafts.csswg.org/css-syntax/#token-stream-next-token
79
    [[nodiscard]] T const& next_token() const
80
0
    {
81
        // The item of tokens at index.
82
        // If that index would be out-of-bounds past the end of the list, it’s instead an <eof-token>.
83
0
        if (m_index < m_tokens.size())
84
0
            return m_tokens[m_index];
85
0
        return m_eof;
86
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::next_token() const
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::next_token() const
87
88
    // https://drafts.csswg.org/css-syntax/#token-stream-empty
89
    [[nodiscard]] bool is_empty() const
90
0
    {
91
        // A token stream is empty if the next token is an <eof-token>.
92
0
        return next_token().is(Token::Type::EndOfFile);
93
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::is_empty() const
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::is_empty() const
94
95
    // https://drafts.csswg.org/css-syntax/#token-stream-consume-a-token
96
    [[nodiscard]] T const& consume_a_token()
97
0
    {
98
        // Let token be the next token. Increment index, then return token.
99
0
        auto& token = next_token();
100
0
        ++m_index;
101
0
        return token;
102
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::consume_a_token()
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::consume_a_token()
103
104
    // https://drafts.csswg.org/css-syntax/#token-stream-discard-a-token
105
    void discard_a_token()
106
0
    {
107
        // If the token stream is not empty, increment index.
108
0
        if (!is_empty())
109
0
            ++m_index;
110
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::discard_a_token()
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::discard_a_token()
111
112
    // https://drafts.csswg.org/css-syntax/#token-stream-mark
113
    void mark()
114
0
    {
115
        // Append index to marked indexes.
116
0
        m_marked_indexes.append(m_index);
117
0
    }
118
119
    // https://drafts.csswg.org/css-syntax/#token-stream-restore-a-mark
120
    void restore_a_mark()
121
0
    {
122
        // Pop from marked indexes, and set index to the popped value.
123
0
        m_index = m_marked_indexes.take_last();
124
0
    }
125
126
    // https://drafts.csswg.org/css-syntax/#token-stream-discard-a-mark
127
    void discard_a_mark()
128
0
    {
129
        // Pop from marked indexes, and do nothing with the popped value.
130
0
        m_marked_indexes.take_last();
131
0
    }
132
133
    // https://drafts.csswg.org/css-syntax/#token-stream-discard-whitespace
134
    void discard_whitespace()
135
0
    {
136
        // While the next token is a <whitespace-token>, discard a token.
137
0
        while (next_token().is(Token::Type::Whitespace))
138
0
            discard_a_token();
139
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::discard_whitespace()
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::discard_whitespace()
140
141
    bool has_next_token()
142
0
    {
143
0
        return !is_empty();
144
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::has_next_token()
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::has_next_token()
145
146
    // Deprecated, used in older versions of the spec.
147
    T const& current_token()
148
    {
149
        if (m_index < 1 || (m_index - 1) >= m_tokens.size())
150
            return m_eof;
151
152
        return m_tokens.at(m_index - 1);
153
    }
154
155
    // Deprecated
156
    T const& peek_token(size_t offset = 0)
157
0
    {
158
0
        if (remaining_token_count() <= offset)
159
0
            return m_eof;
160
161
0
        return m_tokens.at(m_index + offset);
162
0
    }
163
164
    // Deprecated, was used in older versions of the spec.
165
    void reconsume_current_input_token()
166
0
    {
167
0
        if (m_index > 0)
168
0
            --m_index;
169
0
    }
170
171
0
    StateTransaction begin_transaction() { return StateTransaction(*this); }
172
173
    size_t remaining_token_count() const
174
0
    {
175
0
        if (m_tokens.size() > m_index)
176
0
            return m_tokens.size() - m_index;
177
0
        return 0;
178
0
    }
179
180
    void dump_all_tokens()
181
    {
182
        dbgln("Dumping all tokens:");
183
        for (size_t i = 0; i < m_tokens.size(); ++i) {
184
            auto& token = m_tokens[i];
185
            if (i == m_index)
186
                dbgln("-> {}", token.to_debug_string());
187
            else
188
                dbgln("   {}", token.to_debug_string());
189
        }
190
    }
191
192
    void copy_state(Badge<Parser>, TokenStream<T> const& other)
193
0
    {
194
0
        m_index = other.m_index;
195
0
    }
196
197
private:
198
    // https://drafts.csswg.org/css-syntax/#token-stream-tokens
199
    Span<T const> m_tokens;
200
201
    // https://drafts.csswg.org/css-syntax/#token-stream-index
202
    size_t m_index { 0 };
203
204
    // https://drafts.csswg.org/css-syntax/#token-stream-marked-indexes
205
    Vector<size_t> m_marked_indexes;
206
207
    T make_eof()
208
0
    {
209
0
        if constexpr (IsSame<T, Token>) {
210
0
            return Tokenizer::create_eof_token();
211
0
        }
212
0
        if constexpr (IsSame<T, ComponentValue>) {
213
0
            return ComponentValue(Tokenizer::create_eof_token());
214
0
        }
215
0
    }
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::ComponentValue>::make_eof()
Unexecuted instantiation: Web::CSS::Parser::TokenStream<Web::CSS::Parser::Token>::make_eof()
216
217
    T m_eof;
218
};
219
220
}