/* * Copyright (c) 2018-2020, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #include "Selector.h" #include #include #include namespace Web::CSS { Selector::Selector(Vector&& component_lists) : m_complex_selectors(move(component_lists)) { } Selector::~Selector() { } u32 Selector::specificity() const { unsigned ids = 0; unsigned tag_names = 0; unsigned classes = 0; for (auto& list : m_complex_selectors) { for (auto& simple_selector : list.compound_selector) { switch (simple_selector.type) { case SimpleSelector::Type::Id: ++ids; break; case SimpleSelector::Type::Class: ++classes; break; case SimpleSelector::Type::TagName: ++tag_names; break; default: break; } } } return ids * 0x10000 + classes * 0x100 + tag_names; } Selector::SimpleSelector::NthChildPattern Selector::SimpleSelector::NthChildPattern::parse(const StringView& args) { CSS::Selector::SimpleSelector::NthChildPattern pattern; if (args.equals_ignoring_case("odd")) { pattern.step_size = 2; pattern.offset = 1; } else if (args.equals_ignoring_case("even")) { pattern.step_size = 2; } else { const auto consume_int = [](GenericLexer& lexer) -> Optional { return AK::StringUtils::convert_to_int(lexer.consume_while([](char c) -> bool { return isdigit(c) || c == '+' || c == '-'; })); }; // Try to match any of following patterns: // 1. An+B // 2. An // 3. B // ...where "A" is "step_size", "B" is "offset" and rest are literals. // "A" can be omitted, in that case "A" = 1. // "A" may have "+" or "-" sign, "B" always must be predated by sign for pattern (1). int step_size_or_offset = 0; GenericLexer lexer { args }; // "When a=1, or a=-1, the 1 may be omitted from the rule." if (lexer.consume_specific("n") || lexer.consume_specific("+n")) { step_size_or_offset = +1; lexer.retreat(); } else if (lexer.consume_specific("-n")) { step_size_or_offset = -1; lexer.retreat(); } else { const auto value = consume_int(lexer); if (!value.has_value()) return {}; step_size_or_offset = value.value(); } if (lexer.consume_specific("n")) { lexer.ignore_while(isspace); if (lexer.next_is('+') || lexer.next_is('-')) { const auto sign = lexer.next_is('+') ? 1 : -1; lexer.ignore(); lexer.ignore_while(isspace); // "An+B" pattern const auto offset = consume_int(lexer); if (!offset.has_value()) return {}; pattern.step_size = step_size_or_offset; pattern.offset = sign * offset.value(); } else { // "An" pattern pattern.step_size = step_size_or_offset; } } else { // "B" pattern pattern.offset = step_size_or_offset; } if (lexer.remaining().length() > 0) return {}; } return pattern; } }