/*
 * Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
 * Copyright (c) 2020-2021, the SerenityOS developers.
 * Copyright (c) 2021-2025, Sam Atkins <sam@ladybird.org>
 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
 * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <LibWeb/CSS/Parser/ErrorReporter.h>
#include <LibWeb/CSS/Parser/Parser.h>
#include <LibWeb/Infra/Strings.h>

namespace Web::CSS::Parser {

static bool next_is_pseudo_element(TokenStream<ComponentValue>& tokens)
{
    auto& first = tokens.next_token();
    auto& second = tokens.peek_token(1);
    if (first.is(Token::Type::Colon)) {
        // Pseudo-elements
        if (second.is(Token::Type::Colon))
            return true;

        // Legacy single-colon pseudo-elements
        if (second.is(Token::Type::Ident)) {
            auto pseudo_element = pseudo_element_from_string(second.token().ident());
            if (pseudo_element.has_value() && is_legacy_single_colon_pseudo_element(pseudo_element.value()))
                return true;
        }
    }

    return false;
}

Optional<SelectorList> Parser::parse_as_selector(SelectorParsingMode parsing_mode)
{
    auto selector_list = parse_a_selector_list(m_token_stream, SelectorType::Standalone, parsing_mode);
    if (!selector_list.is_error())
        return selector_list.release_value();

    return {};
}

Optional<SelectorList> Parser::parse_as_relative_selector(SelectorParsingMode parsing_mode)
{
    auto selector_list = parse_a_selector_list(m_token_stream, SelectorType::Relative, parsing_mode);
    if (!selector_list.is_error())
        return selector_list.release_value();

    return {};
}

Optional<Selector::PseudoElementSelector> Parser::parse_as_pseudo_element_selector()
{
    auto component_values = consume_a_list_of_component_values(m_token_stream);
    TokenStream tokens { component_values };
    auto maybe_simple_selector = parse_pseudo_element_simple_selector(tokens);
    if (maybe_simple_selector.is_error())
        return {};
    if (tokens.has_next_token())
        return {};
    auto simple_selector = maybe_simple_selector.release_value();
    if (simple_selector.type != Selector::SimpleSelector::Type::PseudoElement)
        return {};
    return simple_selector.pseudo_element();
}

static NonnullRefPtr<Selector> create_invalid_selector(Selector::Combinator combinator, Vector<ComponentValue> component_values)
{
    // Trim leading and trailing whitespace
    while (!component_values.is_empty() && component_values.first().is(Token::Type::Whitespace)) {
        component_values.take_first();
    }
    while (!component_values.is_empty() && component_values.last().is(Token::Type::Whitespace)) {
        component_values.take_last();
    }

    Selector::SimpleSelector simple {
        .type = Selector::SimpleSelector::Type::Invalid,
        .value = Selector::SimpleSelector::Invalid {
            .component_values = move(component_values),
        }
    };
    Selector::CompoundSelector compound {
        .combinator = combinator,
        .simple_selectors = { move(simple) }
    };
    return Selector::create({ move(compound) });
}

static Vector<Selector::CompoundSelector> normalize_pseudo_element_transitions(Vector<Selector::CompoundSelector>&& compound_selectors)
{
    // Splits up any CompoundSelectors including pseudo-elements, so that they are separated by a PseudoElement
    // combinator, while ensuring that any pseudo-element CompoundSelectors are preceded by a CompoundSelector for
    // their originating element.
    // Any that don't have one specified explicitly receive an implicit `*` before them:
    // eg, `::before` becomes two CompoundSelectors, for the implicit `*` and for the `::before` pseudo-element.

    // If we don't have any pseudo-elements, return the original CompoundSelectors unchanged.
    bool contains_pseudo_element = false;
    for (auto const& compound_selector : compound_selectors) {
        for (auto const& simple_selector : compound_selector.simple_selectors) {
            if (simple_selector.type != Selector::SimpleSelector::Type::PseudoElement)
                continue;
            contains_pseudo_element = true;
            break;
        }
        if (contains_pseudo_element)
            break;
    }
    if (!contains_pseudo_element)
        return move(compound_selectors);

    Vector<Selector::CompoundSelector> normalized_compound_selectors;

    auto append_compound = [&](Selector::Combinator combinator, Vector<Selector::SimpleSelector>&& simple_selectors) {
        if (simple_selectors.is_empty())
            return;
        normalized_compound_selectors.append({
            .combinator = combinator,
            .simple_selectors = move(simple_selectors),
        });
    };

    for (auto& compound_selector : compound_selectors) {
        auto current_combinator = compound_selector.combinator;
        Vector<Selector::SimpleSelector> current_simple_selectors;
        current_simple_selectors.ensure_capacity(compound_selector.simple_selectors.size());

        for (auto& simple_selector : compound_selector.simple_selectors) {
            if (simple_selector.type == Selector::SimpleSelector::Type::PseudoElement) {
                if (current_simple_selectors.is_empty()) {
                    normalized_compound_selectors.append({
                        .combinator = current_combinator,
                        .is_implicit_universal_anchor = true,
                        .simple_selectors = { Selector::SimpleSelector {
                            .type = Selector::SimpleSelector::Type::Universal,
                            .value = Selector::SimpleSelector::QualifiedName {
                                .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::Any,
                                .name = Selector::SimpleSelector::Name { "*"_fly_string },
                            },
                        } },
                    });
                } else {
                    append_compound(current_combinator, move(current_simple_selectors));
                    current_simple_selectors = {};
                }
                current_combinator = Selector::Combinator::PseudoElement;
            }
            current_simple_selectors.append(move(simple_selector));
        }

        append_compound(current_combinator, move(current_simple_selectors));
    }

    return normalized_compound_selectors;
}

template<typename T>
Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorType mode, SelectorParsingMode parsing_mode)
{
    SelectorList selectors;
    for (;;) {
        auto selector_parts = consume_a_list_of_component_values(tokens, Token::Type::Comma);
        auto stream = TokenStream(selector_parts);
        auto selector = parse_complex_selector(stream, mode);
        if (selector.is_error()) {
            if (parsing_mode == SelectorParsingMode::Forgiving) {
                // Keep the invalid selector around for serialization and nesting
                auto combinator = mode == SelectorType::Standalone ? Selector::Combinator::None : Selector::Combinator::Descendant;
                selectors.append(create_invalid_selector(combinator, move(selector_parts)));
            } else {
                return selector.error();
            }
        } else {
            selectors.append(selector.release_value());
        }

        if (tokens.is_empty())
            break;

        tokens.discard_a_token();
    }

    if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
        return ParseError::SyntaxError;

    return selectors;
}
template Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<ComponentValue>&, SelectorType, SelectorParsingMode);
template Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<Token>&, SelectorType, SelectorParsingMode);

Parser::ParseErrorOr<NonnullRefPtr<Selector>> Parser::parse_complex_selector(TokenStream<ComponentValue>& tokens, SelectorType mode)
{
    Vector<Selector::CompoundSelector> raw_compound_selectors;

    auto first_combinator = parse_selector_combinator(tokens);

    switch (mode) {
    case SelectorType::Standalone: {
        // Standalone selectors can't start with a combinator.
        // Whitespace, which gets parsed as a descendant combinator, is instead treated as None.
        if (first_combinator.has_value() && first_combinator != Selector::Combinator::Descendant) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = tokens.dump_string(),
                .description = "Standalone selector starts with a combinator, which is invalid."_string,
            });
            return ParseError::SyntaxError;
        }
        first_combinator = Selector::Combinator::None;
        break;
    }
    case SelectorType::Relative:
        // Relative selectors default to starting with a descendant combinator.
        if (!first_combinator.has_value())
            first_combinator = Selector::Combinator::Descendant;
        break;
    }

    auto first_selector = TRY(parse_compound_selector(tokens));
    if (first_selector.simple_selectors.is_empty()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = tokens.dump_string(),
            .description = "Failed to parse first compound-selector."_string,
        });
        return ParseError::SyntaxError;
    }

    first_selector.combinator = first_combinator.value_or(Selector::Combinator::None);
    raw_compound_selectors.append(move(first_selector));

    while (tokens.has_next_token()) {
        auto combinator = parse_selector_combinator(tokens);
        if (!combinator.has_value())
            break;
        auto compound_selector = TRY(parse_compound_selector(tokens));
        if (compound_selector.simple_selectors.is_empty()) {
            if (tokens.has_next_token() || combinator != Selector::Combinator::Descendant) {
                ErrorReporter::the().report(InvalidSelectorError {
                    .value_string = tokens.dump_string(),
                    .description = "Compound-selector is empty."_string,
                });
                return ParseError::SyntaxError;
            }
            break;
        }

        compound_selector.combinator = combinator.release_value();
        raw_compound_selectors.append(move(compound_selector));
    }

    if (raw_compound_selectors.is_empty()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = tokens.dump_string(),
            .description = "Selector contains no compound-selectors."_string,
        });
        return ParseError::SyntaxError;
    }

    if (tokens.has_next_token()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = tokens.dump_string(),
            .description = "Not all tokens were consumed."_string,
        });
        return ParseError::SyntaxError;
    }

    auto parsed_selector = Selector::create(normalize_pseudo_element_transitions(move(raw_compound_selectors)));

    auto pseudo_element_count = 0;
    Optional<PseudoElement> first_pseudo_element;
    Optional<PseudoElement> second_pseudo_element;
    bool saw_pseudo_element_transition = false;
    for (auto const& compound_selector : parsed_selector->compound_selectors()) {
        if (saw_pseudo_element_transition && compound_selector.combinator != Selector::Combinator::PseudoElement) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = parsed_selector->serialize(),
                .description = "Pseudo-elements cannot be followed by a non-pseudo-element combinator."_string,
            });
            return ParseError::SyntaxError;
        }
        if (compound_selector.combinator == Selector::Combinator::PseudoElement)
            saw_pseudo_element_transition = true;

        if (!compound_selector.simple_selectors.is_empty()
            && compound_selector.simple_selectors.first().type == Selector::SimpleSelector::Type::PseudoElement) {
            ++pseudo_element_count;
            auto pseudo_element = compound_selector.simple_selectors.first().pseudo_element().type();
            if (!first_pseudo_element.has_value())
                first_pseudo_element = pseudo_element;
            else if (!second_pseudo_element.has_value())
                second_pseudo_element = pseudo_element;
        }
    }

    if (pseudo_element_count > 1) {
        // FIXME: Other pseudo-elements can also be chained (e.g. ::highlight()::before).
        //        For now, only ::part() followed by one other pseudo-element is supported.
        bool is_valid_chain = first_pseudo_element.value() == PseudoElement::Part
            && second_pseudo_element.value() != PseudoElement::Part
            && pseudo_element_count == 2;
        if (!is_valid_chain) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = parsed_selector->serialize(),
                .description = "Pseudo-element chaining is not yet supported for this combination."_string,
            });
            return ParseError::SyntaxError;
        }
    }

    return parsed_selector;
}

Parser::ParseErrorOr<Selector::CompoundSelector> Parser::parse_compound_selector(TokenStream<ComponentValue>& tokens)
{
    Vector<Selector::SimpleSelector> simple_selectors;

    while (tokens.has_next_token()) {
        auto component = TRY(parse_simple_selector(tokens));
        if (!component.has_value())
            break;
        if (component->type == Selector::SimpleSelector::Type::TagName && !simple_selectors.is_empty()) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = tokens.dump_string(),
                .description = "Tag-name selectors can only go at the beginning of a compound selector."_string,
            });
            return ParseError::SyntaxError;
        }
        simple_selectors.append(component.release_value());
    }

    return Selector::CompoundSelector {
        .combinator = Selector::Combinator::None,
        .simple_selectors = move(simple_selectors),
    };
}

Optional<Selector::Combinator> Parser::parse_selector_combinator(TokenStream<ComponentValue>& tokens)
{
    auto transaction = tokens.begin_transaction();
    bool had_initial_whitespace = tokens.next_token().is(Token::Type::Whitespace);
    tokens.discard_whitespace();

    auto const& next = tokens.next_token();

    auto consume_single_delim_combinator = [&](auto combinator) {
        tokens.discard_a_token();
        tokens.discard_whitespace();
        transaction.commit();
        return combinator;
    };

    if (next.is_delim('>'))
        return consume_single_delim_combinator(Selector::Combinator::ImmediateChild);

    if (next.is_delim('+'))
        return consume_single_delim_combinator(Selector::Combinator::NextSibling);

    if (next.is_delim('~'))
        return consume_single_delim_combinator(Selector::Combinator::SubsequentSibling);

    if (next.is_delim('|') && tokens.peek_token(1).is_delim('|')) {
        tokens.discard_a_token(); // |
        tokens.discard_a_token(); // |
        tokens.discard_whitespace();
        transaction.commit();
        return Selector::Combinator::Column;
    }

    if (had_initial_whitespace) {
        transaction.commit();
        return Selector::Combinator::Descendant;
    }

    return {};
}

Optional<Selector::SimpleSelector::QualifiedName> Parser::parse_selector_qualified_name(TokenStream<ComponentValue>& tokens, AllowWildcardName allow_wildcard_name)
{
    auto is_name = [](ComponentValue const& token) {
        return token.is_delim('*') || token.is(Token::Type::Ident);
    };
    auto get_name = [](ComponentValue const& token) {
        if (token.is_delim('*'))
            return "*"_fly_string;
        return token.token().ident();
    };

    // There are 3 possibilities here:
    // (Where <name> and <namespace> are either an <ident> or a `*` delim)
    // 1) `|<name>`
    // 2) `<namespace>|<name>`
    // 3) `<name>`
    // Whitespace is forbidden between any of these parts. https://www.w3.org/TR/selectors-4/#white-space

    auto transaction = tokens.begin_transaction();

    auto const& first_token = tokens.consume_a_token();
    if (first_token.is_delim('|')) {
        // Case 1: `|<name>`
        if (is_name(tokens.next_token())) {
            auto const& name_token = tokens.consume_a_token();

            if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*'))
                return {};

            transaction.commit();
            return Selector::SimpleSelector::QualifiedName {
                .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::None,
                .name = get_name(name_token),
            };
        }
        return {};
    }

    if (!is_name(first_token))
        return {};

    if (tokens.next_token().is_delim('|') && is_name(tokens.peek_token(1))) {
        // Case 2: `<namespace>|<name>`
        tokens.discard_a_token(); // `|`
        auto namespace_ = get_name(first_token);
        auto name = get_name(tokens.consume_a_token());

        if (allow_wildcard_name == AllowWildcardName::No && name == "*"sv)
            return {};

        auto namespace_type = namespace_ == "*"sv
            ? Selector::SimpleSelector::QualifiedName::NamespaceType::Any
            : Selector::SimpleSelector::QualifiedName::NamespaceType::Named;

        // https://www.w3.org/TR/selectors-4/#invalid
        // a simple selector containing an undeclared namespace prefix is invalid
        if (namespace_type == Selector::SimpleSelector::QualifiedName::NamespaceType::Named && !m_declared_namespaces.contains(namespace_))
            return {};

        transaction.commit();
        return Selector::SimpleSelector::QualifiedName {
            .namespace_type = namespace_type,
            .namespace_ = namespace_,
            .name = name,
        };
    }

    // Case 3: `<name>`
    auto& name_token = first_token;
    if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*'))
        return {};

    transaction.commit();
    return Selector::SimpleSelector::QualifiedName {
        .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::Default,
        .name = get_name(name_token),
    };
}

Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_attribute_simple_selector(ComponentValue const& first_value)
{
    auto attribute_tokens = TokenStream { first_value.block().value };

    attribute_tokens.discard_whitespace();

    if (!attribute_tokens.has_next_token()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = first_value.to_string(),
            .description = "Attribute selector is empty."_string,
        });
        return ParseError::SyntaxError;
    }

    auto maybe_qualified_name = parse_selector_qualified_name(attribute_tokens, AllowWildcardName::No);
    if (!maybe_qualified_name.has_value()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = first_value.to_string(),
            .description = MUST(String::formatted("Expected qualified-name, got: '{}'.", attribute_tokens.next_token().to_debug_string())),
        });
        return ParseError::SyntaxError;
    }
    auto qualified_name = maybe_qualified_name.release_value();

    Selector::SimpleSelector simple_selector {
        .type = Selector::SimpleSelector::Type::Attribute,
        .value = Selector::SimpleSelector::Attribute {
            .match_type = Selector::SimpleSelector::Attribute::MatchType::HasAttribute,
            .qualified_name = qualified_name,
            .case_type = Selector::SimpleSelector::Attribute::CaseType::DefaultMatch,
        }
    };

    attribute_tokens.discard_whitespace();
    if (!attribute_tokens.has_next_token())
        return simple_selector;

    auto parse_attribute_match_type = [&first_value](auto& tokens) -> ParseErrorOr<Selector::SimpleSelector::Attribute::MatchType> {
        // This is one of: `=`, `~=`, `*=`, `|=`, `^=`, `$=`
        auto transaction = tokens.begin_transaction();

        auto const& first_delim = tokens.consume_a_token();
        if (!first_delim.is(Token::Type::Delim)) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = first_value.to_string(),
                .description = MUST(String::formatted("Expected delim for attribute comparison, got: '{}'.", first_delim.to_debug_string())),
            });
            return ParseError::SyntaxError;
        }

        if (first_delim.token().delim() == '=') {
            transaction.commit();
            return Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch;
        }

        if (!tokens.has_next_token()) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = first_value.to_string(),
                .description = "Attribute selector ended part way through a match type."_string,
            });
            return ParseError::SyntaxError;
        }

        auto const& second_delim = tokens.consume_a_token();
        if (!second_delim.is_delim('=')) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = first_value.to_string(),
                .description = MUST(String::formatted("Expected a double delim for attribute comparison, got: '{}{}'.", first_delim.to_debug_string(), second_delim.to_debug_string())),
            });
            return ParseError::SyntaxError;
        }
        switch (first_delim.token().delim()) {
        case '~':
            transaction.commit();
            return Selector::SimpleSelector::Attribute::MatchType::ContainsWord;
        case '*':
            transaction.commit();
            return Selector::SimpleSelector::Attribute::MatchType::ContainsString;
        case '|':
            transaction.commit();
            return Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment;
        case '^':
            transaction.commit();
            return Selector::SimpleSelector::Attribute::MatchType::StartsWithString;
        case '$':
            transaction.commit();
            return Selector::SimpleSelector::Attribute::MatchType::EndsWithString;
        default:
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = first_value.to_string(),
                .description = MUST(String::formatted("Invalid attribute selector match type `{:c}=`", first_delim.token().delim())),
            });
            return ParseError::SyntaxError;
        }
    };

    simple_selector.attribute().match_type = TRY(parse_attribute_match_type(attribute_tokens));

    attribute_tokens.discard_whitespace();
    if (!attribute_tokens.has_next_token()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = first_value.to_string(),
            .description = "Attribute selector ended without a value to match."_string,
        });
        return ParseError::SyntaxError;
    }

    auto const& value_part = attribute_tokens.consume_a_token();
    if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = first_value.to_string(),
            .description = MUST(String::formatted("Expected a string or ident for the value to match attribute against, got: '{}'.", value_part.to_debug_string())),
        });
        return ParseError::SyntaxError;
    }
    auto const& value_string = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string();
    simple_selector.attribute().value = value_string.to_string();

    attribute_tokens.discard_whitespace();
    // Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
    if (attribute_tokens.has_next_token()) {
        auto const& case_sensitivity_part = attribute_tokens.consume_a_token();
        if (case_sensitivity_part.is(Token::Type::Ident)) {
            auto case_sensitivity = case_sensitivity_part.token().ident();
            if (case_sensitivity.equals_ignoring_ascii_case("i"sv)) {
                simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseInsensitiveMatch;
            } else if (case_sensitivity.equals_ignoring_ascii_case("s"sv)) {
                simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseSensitiveMatch;
            } else {
                ErrorReporter::the().report(InvalidSelectorError {
                    .value_string = first_value.to_string(),
                    .description = MUST(String::formatted("Expected a \"i\" or \"s\" attribute selector case sensitivity identifier, got: '{}'.", case_sensitivity_part.to_debug_string())),
                });
                return ParseError::SyntaxError;
            }
        } else {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = first_value.to_string(),
                .description = MUST(String::formatted("Expected an attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string())),
            });
            return ParseError::SyntaxError;
        }
    }

    attribute_tokens.discard_whitespace();

    if (attribute_tokens.has_next_token()) {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = first_value.to_string(),
            .description = "Trailing tokens in attribute selector."_string,
        });
        return ParseError::SyntaxError;
    }

    return simple_selector;
}

Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_class_simple_selector(TokenStream<ComponentValue>& tokens)
{
    auto peek_token_ends_selector = [&]() -> bool {
        auto const& value = tokens.next_token();
        return value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma);
    };

    if (peek_token_ends_selector())
        return ParseError::SyntaxError;

    if (!tokens.consume_a_token().is(Token::Type::Colon))
        return ParseError::SyntaxError;

    if (peek_token_ends_selector())
        return ParseError::SyntaxError;

    if (tokens.next_token().is(Token::Type::Ident)) {
        auto pseudo_name = tokens.consume_a_token().token().ident();

        if (auto pseudo_class = pseudo_class_from_string(pseudo_name); pseudo_class.has_value()) {
            if (!pseudo_class_metadata(pseudo_class.value()).is_valid_as_identifier) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted(":{}", pseudo_name)),
                    .value_string = pseudo_name.to_string(),
                    .description = "Only valid as a function."_string,
                });
                return ParseError::SyntaxError;
            }

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector { .type = pseudo_class.value() }
            };
        }

        if (has_ignored_vendor_prefix(pseudo_name))
            return ParseError::IncludesIgnoredVendorPrefix;

        ErrorReporter::the().report(UnknownPseudoClassOrElementError {
            .name = MUST(String::formatted(":{}", pseudo_name)),
        });
        return ParseError::SyntaxError;
    }

    if (tokens.next_token().is_function()) {
        auto parse_an_plus_b_selector = [this](auto pseudo_class, Vector<ComponentValue> const& function_values, bool allow_of = false) -> ParseErrorOr<Selector::SimpleSelector> {
            TokenStream tokens { function_values };
            auto an_plus_b_pattern = parse_a_n_plus_b_pattern(tokens);
            if (!an_plus_b_pattern.has_value()) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted(":{}", pseudo_class_name(pseudo_class))),
                    .value_string = tokens.dump_string(),
                    .description = "Invalid An+B format."_string,
                });
                return ParseError::SyntaxError;
            }

            tokens.discard_whitespace();
            if (!tokens.has_next_token()) {
                return Selector::SimpleSelector {
                    .type = Selector::SimpleSelector::Type::PseudoClass,
                    .value = Selector::SimpleSelector::PseudoClassSelector {
                        .type = pseudo_class,
                        .an_plus_b_pattern = an_plus_b_pattern.release_value() }
                };
            }

            if (!allow_of)
                return ParseError::SyntaxError;

            // Parse the `of <selector-list>` syntax
            auto const& maybe_of = tokens.consume_a_token();
            if (!maybe_of.is_ident("of"sv))
                return ParseError::SyntaxError;

            tokens.discard_whitespace();
            auto selector_list = TRY(parse_a_selector_list(tokens, SelectorType::Standalone));

            tokens.discard_whitespace();
            if (tokens.has_next_token())
                return ParseError::SyntaxError;

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .an_plus_b_pattern = an_plus_b_pattern.release_value(),
                    .argument_selector_list = move(selector_list) }
            };
        };

        auto const& pseudo_function = tokens.consume_a_token().function();
        auto maybe_pseudo_class = pseudo_class_from_string(pseudo_function.name);
        if (!maybe_pseudo_class.has_value()) {
            ErrorReporter::the().report(UnknownPseudoClassOrElementError {
                .name = MUST(String::formatted(":{}", pseudo_function.name)),
            });
            return ParseError::SyntaxError;
        }
        auto pseudo_class = maybe_pseudo_class.value();
        auto metadata = pseudo_class_metadata(pseudo_class);

        if (!metadata.is_valid_as_function) {
            ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                .name = MUST(String::formatted(":{}", pseudo_function.name)),
                .value_string = pseudo_function.name.to_string(),
                .description = "Not valid as a function."_string,
            });
            return ParseError::SyntaxError;
        }

        if (pseudo_function.value.is_empty()) {
            ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                .name = MUST(String::formatted(":{}", pseudo_function.name)),
                .value_string = pseudo_function.name.to_string(),
                .description = "Missing arguments."_string,
            });
            return ParseError::SyntaxError;
        }

        // "The :has() pseudo-class cannot be nested; :has() is not valid within :has()."
        // https://drafts.csswg.org/selectors/#relational
        if (pseudo_class == PseudoClass::Has && m_pseudo_class_context.contains_slow(PseudoClass::Has)) {
            ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                .name = MUST(String::formatted(":{}", pseudo_function.name)),
                .value_string = pseudo_function.name.to_string(),
                .description = ":has() is not allowed inside :has()."_string,
            });
            return ParseError::SyntaxError;
        }

        m_pseudo_class_context.append(pseudo_class);
        ScopeGuard guard = [&] { m_pseudo_class_context.take_last(); };

        switch (metadata.parameter_type) {
        case PseudoClassMetadata::ParameterType::ANPlusB:
            return parse_an_plus_b_selector(pseudo_class, pseudo_function.value, false);
        case PseudoClassMetadata::ParameterType::ANPlusBOf:
            return parse_an_plus_b_selector(pseudo_class, pseudo_function.value, true);
        case PseudoClassMetadata::ParameterType::CompoundSelector: {
            auto function_token_stream = TokenStream(pseudo_function.value);
            auto compound_selector_or_error = parse_compound_selector(function_token_stream);
            if (compound_selector_or_error.is_error()) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted(":{}", pseudo_function.name)),
                    .value_string = pseudo_function.name.to_string(),
                    .description = "Failed to parse argument as a compound selector."_string,
                });
                return ParseError::SyntaxError;
            }
            function_token_stream.discard_whitespace();
            if (function_token_stream.has_next_token()) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted("::{}", pseudo_function.name)),
                    .value_string = pseudo_function.name.to_string(),
                    .description = "Trailing tokens after compound selector argument."_string,
                });
                return ParseError::SyntaxError;
            }

            auto compound_selector = compound_selector_or_error.release_value();
            compound_selector.combinator = Selector::Combinator::None;

            auto selector = Selector::create(Vector { move(compound_selector) });

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .argument_selector_list = { move(selector) } }
            };
        }
        case PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList:
        case PseudoClassMetadata::ParameterType::ForgivingSelectorList: {
            auto function_token_stream = TokenStream(pseudo_function.value);
            auto selector_type = metadata.parameter_type == PseudoClassMetadata::ParameterType::ForgivingSelectorList
                ? SelectorType::Standalone
                : SelectorType::Relative;
            // NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
            auto argument_selector_list = MUST(parse_a_selector_list(function_token_stream, selector_type, SelectorParsingMode::Forgiving));

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .is_forgiving = true,
                    .argument_selector_list = move(argument_selector_list) }
            };
        }
        case PseudoClassMetadata::ParameterType::Ident: {
            auto function_token_stream = TokenStream(pseudo_function.value);
            function_token_stream.discard_whitespace();
            auto const& maybe_ident_token = function_token_stream.consume_a_token();
            function_token_stream.discard_whitespace();
            if (!maybe_ident_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted(":{}", pseudo_function.name)),
                    .value_string = pseudo_function.name.to_string(),
                    .description = "Failed to parse argument as an ident."_string,
                });
                return ParseError::SyntaxError;
            }

            auto& ident = maybe_ident_token.token().ident();

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .ident = Selector::SimpleSelector::PseudoClassSelector::Ident {
                        .keyword = keyword_from_string(ident).value_or(Keyword::Invalid),
                        .string_value = ident,
                    },
                }
            };
        }
        case PseudoClassMetadata::ParameterType::LanguageRanges: {
            Vector<FlyString> languages;
            auto function_token_stream = TokenStream(pseudo_function.value);
            auto language_token_lists = parse_a_comma_separated_list_of_component_values(function_token_stream);

            for (auto const& language_token_list : language_token_lists) {
                auto language_token_stream = TokenStream(language_token_list);
                language_token_stream.discard_whitespace();
                auto const& language_token = language_token_stream.consume_a_token();
                if (!(language_token.is(Token::Type::Ident) || language_token.is(Token::Type::String))) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted(":{}", pseudo_function.name)),
                        .value_string = pseudo_function.name.to_string(),
                        .description = "Failed to parse argument as a language range: Not a string/ident."_string,
                    });
                    return ParseError::SyntaxError;
                }

                auto language_string = language_token.is(Token::Type::String) ? language_token.token().string() : language_token.token().ident();
                languages.append(language_string);

                language_token_stream.discard_whitespace();
                if (language_token_stream.has_next_token()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted(":{}", pseudo_function.name)),
                        .value_string = pseudo_function.name.to_string(),
                        .description = "Failed to parse argument as a language range: Has trailing tokens."_string,
                    });
                    return ParseError::SyntaxError;
                }
            }

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .languages = move(languages) }
            };
        }
        case PseudoClassMetadata::ParameterType::LevelList: {
            // https://drafts.csswg.org/selectors-5/#heading-functional-pseudo
            // :heading() = :heading( <level># )
            // where <level> is a <number-token> with its type flag set to "integer".
            Vector<i64> levels;
            auto function_token_stream = TokenStream(pseudo_function.value);
            auto level_lists = parse_a_comma_separated_list_of_component_values(function_token_stream);

            for (auto const& level_tokens : level_lists) {
                TokenStream level_token_stream { level_tokens };
                level_token_stream.discard_whitespace();
                auto& maybe_integer = level_token_stream.consume_a_token();
                level_token_stream.discard_whitespace();

                if (!maybe_integer.is(Token::Type::Number) || !maybe_integer.token().is_integer()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted(":{}", pseudo_function.name)),
                        .value_string = pseudo_function.name.to_string(),
                        .description = "Failed to parse argument as a <level>: Not an <integer> literal."_string,
                    });
                    return ParseError::SyntaxError;
                }

                if (level_token_stream.has_next_token()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted(":{}", pseudo_function.name)),
                        .value_string = pseudo_function.name.to_string(),
                        .description = "Failed to parse argument as a <level>: Has trailing tokens."_string,
                    });
                    return ParseError::SyntaxError;
                }

                levels.append(maybe_integer.token().to_integer());
            }

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .levels = move(levels),
                }
            };
        }
        case PseudoClassMetadata::ParameterType::RelativeSelectorList:
        case PseudoClassMetadata::ParameterType::SelectorList: {
            auto function_token_stream = TokenStream(pseudo_function.value);
            auto selector_type = metadata.parameter_type == PseudoClassMetadata::ParameterType::SelectorList
                ? SelectorType::Standalone
                : SelectorType::Relative;
            auto not_selector = TRY(parse_a_selector_list(function_token_stream, selector_type));

            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoClass,
                .value = Selector::SimpleSelector::PseudoClassSelector {
                    .type = pseudo_class,
                    .argument_selector_list = move(not_selector) }
            };
        }
        case PseudoClassMetadata::ParameterType::None:
            // `None` means this is not a function-type pseudo-class, so this state should be impossible.
            VERIFY_NOT_REACHED();
        }
    }
    ErrorReporter::the().report(InvalidSelectorError {
        .value_string = tokens.next_token().to_string(),
        .description = MUST(String::formatted("Pseudo-class should be an ident or function, got: '{}'", tokens.next_token().to_debug_string())),
    });
    return ParseError::SyntaxError;
}

Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_element_simple_selector(TokenStream<ComponentValue>& tokens)
{
    auto peek_token_ends_selector = [&]() -> bool {
        auto const& value = tokens.next_token();
        return value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma);
    };

    if (peek_token_ends_selector())
        return ParseError::SyntaxError;

    if (!tokens.consume_a_token().is(Token::Type::Colon))
        return ParseError::SyntaxError;
    // A few pseudo-elements are allowed to have a single colon, like a pseudo-class.
    bool const started_with_double_colon = tokens.next_token().is(Token::Type::Colon);
    if (started_with_double_colon)
        tokens.discard_a_token(); // :

    auto const& name_token = tokens.consume_a_token();
    bool is_function = false;
    FlyString pseudo_name;

    if (name_token.is(Token::Type::Ident)) {
        pseudo_name = name_token.token().ident();
    } else if (name_token.is_function()) {
        pseudo_name = name_token.function().name;
        is_function = true;
    } else {
        ErrorReporter::the().report(InvalidSelectorError {
            .value_string = name_token.to_string(),
            .description = MUST(String::formatted("Pseudo-element should be an ident or function, got: '{}'", name_token.to_debug_string())),
        });
        return ParseError::SyntaxError;
    }

    bool is_aliased_pseudo = false;
    auto pseudo_element = pseudo_element_from_string(pseudo_name);
    if (!pseudo_element.has_value()) {
        pseudo_element = aliased_pseudo_element_from_string(pseudo_name);
        is_aliased_pseudo = pseudo_element.has_value();
    }

    if (pseudo_element.has_value()) {
        // :has() is fussy about pseudo-elements inside it
        if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(*pseudo_element)) {
            return ParseError::SyntaxError;
        }

        // Support legacy single-colon syntax for some older pseudo-elements.
        if (!started_with_double_colon) {
            if (is_legacy_single_colon_pseudo_element(pseudo_element.value())) {
                return Selector::SimpleSelector {
                    .type = Selector::SimpleSelector::Type::PseudoElement,
                    .value = Selector::PseudoElementSelector { pseudo_element.value() }
                };
            }
            ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                .name = MUST(String::formatted(":{}", pseudo_name)),
                .value_string = name_token.to_string(),
                .description = "This is not a legacy pseudo-element."_string,
            });
            return ParseError::SyntaxError;
        }

        auto metadata = pseudo_element_metadata(*pseudo_element);

        Selector::PseudoElementSelector::Value value = Empty {};
        if (is_function) {
            if (!metadata.is_valid_as_function) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted("::{}", pseudo_name)),
                    .value_string = name_token.to_string(),
                    .description = "Not valid as a function."_string,
                });
                return ParseError::SyntaxError;
            }

            // Parse arguments
            TokenStream function_tokens { name_token.function().value };
            function_tokens.discard_whitespace();

            switch (metadata.parameter_type) {
            case PseudoElementMetadata::ParameterType::None:
                if (function_tokens.has_next_token()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted("::{}", pseudo_name)),
                        .value_string = name_token.to_string(),
                        .description = "Should have no arguments."_string,
                    });
                    return ParseError::SyntaxError;
                }
                break;
            case PseudoElementMetadata::ParameterType::CompoundSelector: {
                auto compound_selector_or_error = parse_compound_selector(function_tokens);
                if (compound_selector_or_error.is_error()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted("::{}", pseudo_name)),
                        .value_string = name_token.to_string(),
                        .description = "Failed to parse argument as a compound selector."_string,
                    });
                    return ParseError::SyntaxError;
                }
                function_tokens.discard_whitespace();
                if (function_tokens.has_next_token()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted("::{}", pseudo_name)),
                        .value_string = name_token.to_string(),
                        .description = "Trailing tokens after compound selector argument."_string,
                    });
                    return ParseError::SyntaxError;
                }

                auto compound_selector = compound_selector_or_error.release_value();
                compound_selector.combinator = Selector::Combinator::None;
                value = Selector::create(Vector { move(compound_selector) });
                break;
            }
            case PseudoElementMetadata::ParameterType::IdentList: {
                // <ident>+
                Selector::PseudoElementSelector::IdentList idents;
                while (function_tokens.has_next_token()) {
                    if (!function_tokens.next_token().is(Token::Type::Ident)) {
                        ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                            .name = MUST(String::formatted("::{}", pseudo_name)),
                            .value_string = name_token.to_string(),
                            .description = "Contains invalid <ident>."_string,
                        });
                        return ParseError::SyntaxError;
                    }
                    idents.append(function_tokens.consume_a_token().token().ident());
                    function_tokens.discard_whitespace();
                }
                value = move(idents);
                break;
            }
            case PseudoElementMetadata::ParameterType::PTNameSelector: {
                // <pt-name-selector> = '*' | <custom-ident>
                // https://drafts.csswg.org/css-view-transitions-1/#typedef-pt-name-selector
                if (function_tokens.next_token().is_delim('*')) {
                    function_tokens.discard_a_token(); // *
                    value = Selector::PseudoElementSelector::PTNameSelector { .is_universal = true };
                } else if (auto custom_ident = parse_custom_ident(function_tokens, {}); custom_ident.has_value()) {
                    value = Selector::PseudoElementSelector::PTNameSelector { .value = custom_ident.release_value() };
                } else {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted("::{}", pseudo_name)),
                        .value_string = name_token.to_string(),
                        .description = MUST(String::formatted("Invalid <pt-name-selector> - expected `*` or `<custom-ident>`, got `{}`", function_tokens.next_token().to_debug_string())),
                    });
                    return ParseError::SyntaxError;
                }
                function_tokens.discard_whitespace();
                if (function_tokens.has_next_token()) {
                    ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                        .name = MUST(String::formatted("::{}", pseudo_name)),
                        .value_string = name_token.to_string(),
                        .description = "Invalid <pt-name-selector> - trailing tokens."_string,
                    });
                    return ParseError::SyntaxError;
                }
                break;
            }
            }

        } else {
            if (!metadata.is_valid_as_identifier) {
                ErrorReporter::the().report(InvalidPseudoClassOrElementError {
                    .name = MUST(String::formatted("::{}", pseudo_name)),
                    .value_string = name_token.to_string(),
                    .description = "Only valid as a function."_string,
                });
                return ParseError::SyntaxError;
            }
        }

        // NB: Aliased pseudo-elements behave like their target pseudo-element, but serialize as themselves. So store
        //     their name like we do for unknown -webkit pseudos below.
        if (is_aliased_pseudo) {
            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::PseudoElement,
                .value = Selector::PseudoElementSelector { pseudo_element.release_value(), pseudo_name.to_string().to_ascii_lowercase(), move(value) }
            };
        }

        return Selector::SimpleSelector {
            .type = Selector::SimpleSelector::Type::PseudoElement,
            .value = Selector::PseudoElementSelector { pseudo_element.release_value(), move(value) }
        };
    }

    // https://drafts.csswg.org/selectors-4/#compat
    // All other pseudo-elements whose names begin with the string “-webkit-” (matched ASCII case-insensitively)
    // and that are not functional notations must be treated as valid at parse time. (That is, ::-webkit-asdf is
    // valid at parse time, but ::-webkit-jkl() is not.) If they’re not otherwise recognized and supported, they
    // must be treated as matching nothing, and are unknown -webkit- pseudo-elements.
    if (!is_function && pseudo_name.starts_with_bytes("-webkit-"sv, CaseSensitivity::CaseInsensitive)) {
        // NB: :has() only allows a limited set of pseudo-elements inside it, which doesn't include unknown ones.
        if (m_pseudo_class_context.contains_slow(PseudoClass::Has))
            return ParseError::SyntaxError;

        return Selector::SimpleSelector {
            .type = Selector::SimpleSelector::Type::PseudoElement,
            // Unknown -webkit- pseudo-elements must be serialized in ASCII lowercase.
            .value = Selector::PseudoElementSelector { PseudoElement::UnknownWebKit, pseudo_name.to_string().to_ascii_lowercase() },
        };
    }

    if (has_ignored_vendor_prefix(pseudo_name))
        return ParseError::IncludesIgnoredVendorPrefix;

    ErrorReporter::the().report(UnknownPseudoClassOrElementError {
        .name = MUST(String::formatted("::{}", pseudo_name)),
    });
    return ParseError::SyntaxError;
}

Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_selector(TokenStream<ComponentValue>& tokens)
{
    auto peek_token_ends_selector = [&]() -> bool {
        auto const& value = tokens.next_token();
        return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
    };

    if (peek_token_ends_selector())
        return Optional<Selector::SimpleSelector> {};

    // Handle universal and tag-name types together, since both can be namespaced
    if (auto qualified_name = parse_selector_qualified_name(tokens, AllowWildcardName::Yes); qualified_name.has_value()) {
        if (qualified_name->name.name == "*"sv) {
            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::Universal,
                .value = qualified_name.release_value(),
            };
        }
        return Selector::SimpleSelector {
            .type = Selector::SimpleSelector::Type::TagName,
            .value = qualified_name.release_value(),
        };
    }

    if (next_is_pseudo_element(tokens))
        return TRY(parse_pseudo_element_simple_selector(tokens));

    if (tokens.next_token().is(Token::Type::Colon))
        return TRY(parse_pseudo_class_simple_selector(tokens));

    if (tokens.next_token().is(Token::Type::Delim)
        && first_is_one_of(static_cast<char>(tokens.next_token().token().delim()), '>', '+', '~', '|')) {
        // Whitespace is not required between the compound-selector and a combinator.
        // So, if we see a combinator, return that this compound-selector is done, instead of a syntax error.
        return Optional<Selector::SimpleSelector> {};
    }

    auto const& first_value = tokens.consume_a_token();

    if (first_value.is(Token::Type::Delim)) {
        u32 delim = first_value.token().delim();
        switch (delim) {
        case '*':
            // Handled already
            VERIFY_NOT_REACHED();
        case '&':
            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::Nesting,
            };
        case '.': {
            if (peek_token_ends_selector())
                return ParseError::SyntaxError;

            auto const& class_name_value = tokens.consume_a_token();
            if (!class_name_value.is(Token::Type::Ident)) {
                ErrorReporter::the().report(InvalidSelectorError {
                    .value_string = tokens.dump_string(),
                    .description = MUST(String::formatted("Expected an ident after '.', got: {}", class_name_value.to_debug_string())),
                });
                return ParseError::SyntaxError;
            }
            return Selector::SimpleSelector {
                .type = Selector::SimpleSelector::Type::Class,
                .value = Selector::SimpleSelector::Name { class_name_value.token().ident() }
            };
        }
        default:
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = tokens.dump_string(),
                .description = MUST(String::formatted("Unrecognized delimiter: {}", first_value.token().to_string())),
            });
            return ParseError::SyntaxError;
        }
    }

    if (first_value.is(Token::Type::Hash)) {
        if (first_value.token().hash_type() != Token::HashType::Id) {
            ErrorReporter::the().report(InvalidSelectorError {
                .value_string = tokens.dump_string(),
                .description = MUST(String::formatted("Hash token is not an id: {}", first_value.to_debug_string())),
            });
            return ParseError::SyntaxError;
        }
        return Selector::SimpleSelector {
            .type = Selector::SimpleSelector::Type::Id,
            .value = Selector::SimpleSelector::Name { first_value.token().hash_value() }
        };
    }

    if (first_value.is_block() && first_value.block().is_square())
        return TRY(parse_attribute_simple_selector(first_value));

    ErrorReporter::the().report(InvalidSelectorError {
        .value_string = tokens.dump_string(),
        .description = MUST(String::formatted("Invalid start of a simple selector: {}", first_value.to_debug_string())),
    });
    return ParseError::SyntaxError;
}

// https://drafts.csswg.org/css-syntax-3/#anb-microsyntax
Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_pattern(TokenStream<ComponentValue>& values)
{
    auto transaction = values.begin_transaction();

    auto is_sign = [](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Delim) && (value.token().delim() == '+' || value.token().delim() == '-');
    };

    auto is_series_of_1_or_more_digits = [](StringView string) -> bool {
        if (string.is_empty())
            return false;
        for (char c : string) {
            if (!is_ascii_digit(c))
                return false;
        }
        return true;
    };

    // <n-dimension> is a <dimension-token> with its type flag set to "integer", and a unit that is an ASCII
    // case-insensitive match for "n"
    auto is_n_dimension = [](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Dimension)
            && value.token().is_integer()
            && value.token().dimension_unit().equals_ignoring_ascii_case("n"sv);
    };

    // <ndash-dimension> is a <dimension-token> with its type flag set to "integer", and a unit that is an ASCII
    // case-insensitive match for "n-"
    auto is_ndash_dimension = [](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Dimension)
            && value.token().is_integer()
            && value.token().dimension_unit().equals_ignoring_ascii_case("n-"sv);
    };

    // <ndashdigit-dimension> is a <dimension-token> with its type flag set to "integer", and a unit that is an ASCII
    // case-insensitive match for "n-*", where "*" is a series of one or more digits
    auto is_ndashdigit_dimension = [&](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Dimension)
            && value.token().is_integer()
            && value.token().dimension_unit().starts_with_bytes("n-"sv, CaseSensitivity::CaseInsensitive)
            && is_series_of_1_or_more_digits(value.token().dimension_unit().bytes_as_string_view().substring_view(2));
    };

    // <ndashdigit-ident> is an <ident-token> whose value is an ASCII case-insensitive match for "n-*", where "*" is a
    // series of one or more digits
    auto is_ndashdigit_ident = [&](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Ident)
            && value.token().ident().starts_with_bytes("n-"sv, CaseSensitivity::CaseInsensitive)
            && is_series_of_1_or_more_digits(value.token().ident().bytes_as_string_view().substring_view(2));
    };

    // <dashndashdigit-ident> is an <ident-token> whose value is an ASCII case-insensitive match for "-n-*", where "*"
    // is a series of one or more digits
    auto is_dashndashdigit_ident = [&](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Ident)
            && value.token().ident().starts_with_bytes("-n-"sv, CaseSensitivity::CaseInsensitive)
            && is_series_of_1_or_more_digits(value.token().ident().bytes_as_string_view().substring_view(3));
    };

    // <integer> is a <number-token> with its type flag set to "integer"
    auto is_integer = [](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Number) && value.token().is_integer();
    };

    // <signed-integer> is a <number-token> with its type flag set to "integer", and a sign character
    auto is_signed_integer = [](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Number) && value.token().is_integer_with_explicit_sign();
    };

    // <signless-integer> is a <number-token> with its type flag set to "integer", and no sign character
    auto is_signless_integer = [](ComponentValue const& value) -> bool {
        return value.is(Token::Type::Number)
            && value.token().is_integer()
            && !value.token().is_integer_with_explicit_sign();
    };

    values.discard_whitespace();

    // odd | even
    if (values.next_token().is_ident("odd"sv)) {
        values.discard_a_token(); // odd
        transaction.commit();
        return Selector::SimpleSelector::ANPlusBPattern { 2, 1 };
    }
    if (values.next_token().is_ident("even"sv)) {
        values.discard_a_token(); // even
        transaction.commit();
        return Selector::SimpleSelector::ANPlusBPattern { 2, 0 };
    }

    // <integer>
    if (is_integer(values.next_token())) {
        int b = values.consume_a_token().token().to_integer();
        transaction.commit();
        return Selector::SimpleSelector::ANPlusBPattern { 0, b };
    }

    // <n-dimension>
    // <n-dimension> <signed-integer>
    // <n-dimension> ['+' | '-'] <signless-integer>
    if (is_n_dimension(values.next_token())) {
        int a = values.consume_a_token().token().dimension_value_int();
        values.discard_whitespace();

        // <n-dimension> <signed-integer>
        if (is_signed_integer(values.next_token())) {
            int b = values.consume_a_token().token().to_integer();
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { a, b };
        }

        // <n-dimension> ['+' | '-'] <signless-integer>
        {
            auto child_transaction = transaction.create_child();
            auto const& second_value = values.consume_a_token();
            values.discard_whitespace();
            auto const& third_value = values.consume_a_token();

            if (is_sign(second_value) && is_signless_integer(third_value)) {
                int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
                child_transaction.commit();
                return Selector::SimpleSelector::ANPlusBPattern { a, b };
            }
        }

        // <n-dimension>
        transaction.commit();
        return Selector::SimpleSelector::ANPlusBPattern { a, 0 };
    }

    // <ndash-dimension> <signless-integer>
    if (is_ndash_dimension(values.next_token())) {
        values.discard_whitespace();
        auto const& first_value = values.consume_a_token();
        auto const& second_value = values.consume_a_token();
        if (is_signless_integer(second_value)) {
            int a = first_value.token().dimension_value_int();
            int b = -second_value.token().to_integer();
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { a, b };
        }

        return {};
    }

    // <ndashdigit-dimension>
    if (is_ndashdigit_dimension(values.next_token())) {
        auto const& dimension = values.consume_a_token().token();
        int a = dimension.dimension_value_int();
        auto maybe_b = dimension.dimension_unit().bytes_as_string_view().substring_view(1).to_number<int>();
        if (maybe_b.has_value()) {
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { a, maybe_b.value() };
        }

        return {};
    }

    // <dashndashdigit-ident>
    if (is_dashndashdigit_ident(values.next_token())) {
        auto maybe_b = values.consume_a_token().token().ident().bytes_as_string_view().substring_view(2).to_number<int>();
        if (maybe_b.has_value()) {
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { -1, maybe_b.value() };
        }

        return {};
    }

    // -n
    // -n <signed-integer>
    // -n ['+' | '-'] <signless-integer>
    if (values.next_token().is_ident("-n"sv)) {
        values.discard_a_token(); // -n
        values.discard_whitespace();

        // -n <signed-integer>
        if (is_signed_integer(values.next_token())) {
            int b = values.consume_a_token().token().to_integer();
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { -1, b };
        }

        // -n ['+' | '-'] <signless-integer>
        {
            auto child_transaction = transaction.create_child();
            auto const& second_value = values.consume_a_token();
            values.discard_whitespace();
            auto const& third_value = values.consume_a_token();

            if (is_sign(second_value) && is_signless_integer(third_value)) {
                int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
                child_transaction.commit();
                return Selector::SimpleSelector::ANPlusBPattern { -1, b };
            }
        }

        // -n
        transaction.commit();
        return Selector::SimpleSelector::ANPlusBPattern { -1, 0 };
    }
    // -n- <signless-integer>
    if (values.next_token().is_ident("-n-"sv)) {
        values.discard_a_token(); // -n-
        values.discard_whitespace();
        auto const& second_value = values.consume_a_token();
        if (is_signless_integer(second_value)) {
            int b = -second_value.token().to_integer();
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { -1, b };
        }

        return {};
    }

    // All that's left now are these:
    // '+'?† n
    // '+'?† n <signed-integer>
    // '+'?† n ['+' | '-'] <signless-integer>
    // '+'?† n- <signless-integer>
    // '+'?† <ndashdigit-ident>
    // In all of these cases, the + is optional, and has no effect.
    // So, we consume the + if it's there.
    if (values.next_token().is_delim('+')) {
        values.discard_a_token(); // +
        // We do *not* skip whitespace here.
    }

    auto const& first_after_plus = values.consume_a_token();
    // '+'?† n
    // '+'?† n <signed-integer>
    // '+'?† n ['+' | '-'] <signless-integer>
    if (first_after_plus.is_ident("n"sv)) {
        values.discard_whitespace();

        // '+'?† n <signed-integer>
        if (is_signed_integer(values.next_token())) {
            int b = values.consume_a_token().token().to_integer();
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { 1, b };
        }

        // '+'?† n ['+' | '-'] <signless-integer>
        {
            auto child_transaction = transaction.create_child();
            auto const& second_value = values.consume_a_token();
            values.discard_whitespace();
            auto const& third_value = values.consume_a_token();

            if (is_sign(second_value) && is_signless_integer(third_value)) {
                int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
                child_transaction.commit();
                return Selector::SimpleSelector::ANPlusBPattern { 1, b };
            }
        }

        // '+'?† n
        transaction.commit();
        return Selector::SimpleSelector::ANPlusBPattern { 1, 0 };
    }

    // '+'?† n- <signless-integer>
    if (first_after_plus.is_ident("n-"sv)) {
        values.discard_whitespace();
        auto const& second_value = values.consume_a_token();
        if (is_signless_integer(second_value)) {
            int b = -second_value.token().to_integer();
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { 1, b };
        }

        return {};
    }

    // '+'?† <ndashdigit-ident>
    if (is_ndashdigit_ident(first_after_plus)) {
        auto maybe_b = first_after_plus.token().ident().bytes_as_string_view().substring_view(1).to_number<int>();
        if (maybe_b.has_value()) {
            transaction.commit();
            return Selector::SimpleSelector::ANPlusBPattern { 1, maybe_b.value() };
        }

        return {};
    }

    return {};
}

Optional<PageSelectorList> Parser::parse_as_page_selector_list()
{
    auto selector_list = parse_a_page_selector_list(m_token_stream);
    if (!selector_list.is_error())
        return selector_list.release_value();
    return {};
}

template<typename T>
Parser::ParseErrorOr<PageSelectorList> Parser::parse_a_page_selector_list(TokenStream<T>& tokens)
{
    // https://drafts.csswg.org/css-page-3/#syntax-page-selector
    // <page-selector-list> = <page-selector>#
    // <page-selector> = [ <ident-token>? <pseudo-page>* ]!
    // <pseudo-page> = : [ left | right | first | blank ]

    PageSelectorList selector_list;

    tokens.discard_whitespace();

    while (tokens.has_next_token()) {
        // First optional ident
        Optional<FlyString> maybe_ident;
        if (tokens.next_token().is(Token::Type::Ident))
            maybe_ident = static_cast<Token>(tokens.consume_a_token()).ident();

        // Then an optional series of pseudo-classes
        Vector<PagePseudoClass> pseudo_classes;
        while (tokens.next_token().is(Token::Type::Colon)) {
            tokens.discard_a_token(); // :
            if (!tokens.next_token().is(Token::Type::Ident)) {
                ErrorReporter::the().report(InvalidSelectorError {
                    .rule_name = "@page"_fly_string,
                    .value_string = tokens.dump_string(),
                    .description = "Pseudo-classes must be idents."_string,
                });
                return ParseError::SyntaxError;
            }
            auto pseudo_class_name = static_cast<Token>(tokens.consume_a_token()).ident();
            if (auto pseudo_class = page_pseudo_class_from_string(pseudo_class_name); pseudo_class.has_value()) {
                pseudo_classes.append(*pseudo_class);
            } else {
                ErrorReporter::the().report(UnknownPseudoClassOrElementError {
                    .rule_name = "@page"_fly_string,
                    .name = MUST(String::formatted(":{}", pseudo_class_name)),
                });
                return ParseError::SyntaxError;
            }
        }

        if (!maybe_ident.has_value() && pseudo_classes.is_empty()) {
            // Nothing parsed
            ErrorReporter::the().report(InvalidSelectorError {
                .rule_name = "@page"_fly_string,
                .value_string = tokens.dump_string(),
                .description = "Is empty."_string,
            });
            return ParseError::SyntaxError;
        }

        selector_list.empend(move(maybe_ident), move(pseudo_classes));

        tokens.discard_whitespace();

        if (tokens.next_token().is(Token::Type::Comma)) {
            tokens.discard_a_token(); // ,
            tokens.discard_whitespace();
            if (!tokens.has_next_token()) {
                ErrorReporter::the().report(InvalidSelectorError {
                    .rule_name = "@page"_fly_string,
                    .value_string = tokens.dump_string(),
                    .description = "Trailing comma."_string,
                });
                return ParseError::SyntaxError;
            }

        } else if (tokens.has_next_token()) {
            ErrorReporter::the().report(InvalidSelectorError {
                .rule_name = "@page"_fly_string,
                .value_string = tokens.dump_string(),
                .description = "Trailing tokens."_string,
            });
            return ParseError::SyntaxError;
        }
    }

    return selector_list;
}
template Parser::ParseErrorOr<PageSelectorList> Parser::parse_a_page_selector_list(TokenStream<ComponentValue>&);
template Parser::ParseErrorOr<PageSelectorList> Parser::parse_a_page_selector_list(TokenStream<Token>&);

}
