Serenity Operating System
at master 310 lines 10 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#pragma once 9 10#include <AK/FlyString.h> 11#include <AK/RefCounted.h> 12#include <AK/String.h> 13#include <AK/Vector.h> 14 15namespace Web::CSS { 16 17using SelectorList = Vector<NonnullRefPtr<class Selector>>; 18 19// This is a <complex-selector> in the spec. https://www.w3.org/TR/selectors-4/#complex 20class Selector : public RefCounted<Selector> { 21public: 22 enum class PseudoElement { 23 Before, 24 After, 25 FirstLine, 26 FirstLetter, 27 Marker, 28 ProgressValue, 29 ProgressBar, 30 Placeholder, 31 32 // Keep this last. 33 PseudoElementCount, 34 }; 35 36 struct SimpleSelector { 37 enum class Type { 38 Universal, 39 TagName, 40 Id, 41 Class, 42 Attribute, 43 PseudoClass, 44 PseudoElement, 45 }; 46 47 struct ANPlusBPattern { 48 int step_size { 0 }; // "A" 49 int offset = { 0 }; // "B" 50 51 // https://www.w3.org/TR/css-syntax-3/#serializing-anb 52 ErrorOr<String> serialize() const 53 { 54 // 1. If A is zero, return the serialization of B. 55 if (step_size == 0) { 56 return String::formatted("{}", offset); 57 } 58 59 // 2. Otherwise, let result initially be an empty string. 60 StringBuilder result; 61 62 // 3. 63 // - A is 1: Append "n" to result. 64 if (step_size == 1) 65 TRY(result.try_append('n')); 66 // - A is -1: Append "-n" to result. 67 else if (step_size == -1) 68 TRY(result.try_append("-n"sv)); 69 // - A is non-zero: Serialize A and append it to result, then append "n" to result. 70 else if (step_size != 0) 71 TRY(result.try_appendff("{}n", step_size)); 72 73 // 4. 74 // - B is greater than zero: Append "+" to result, then append the serialization of B to result. 75 if (offset > 0) 76 TRY(result.try_appendff("+{}", offset)); 77 // - B is less than zero: Append the serialization of B to result. 78 if (offset < 0) 79 TRY(result.try_appendff("{}", offset)); 80 81 // 5. Return result. 82 return result.to_string(); 83 } 84 }; 85 86 struct PseudoClass { 87 enum class Type { 88 Link, 89 Visited, 90 Hover, 91 Focus, 92 FocusWithin, 93 FirstChild, 94 LastChild, 95 OnlyChild, 96 NthChild, 97 NthLastChild, 98 Empty, 99 Root, 100 FirstOfType, 101 LastOfType, 102 OnlyOfType, 103 NthOfType, 104 NthLastOfType, 105 Disabled, 106 Enabled, 107 Checked, 108 Is, 109 Not, 110 Where, 111 Active, 112 Lang, 113 }; 114 Type type; 115 116 // FIXME: We don't need this field on every single SimpleSelector, but it's also annoying to malloc it somewhere. 117 // Only used when "pseudo_class" is "NthChild" or "NthLastChild". 118 ANPlusBPattern nth_child_pattern {}; 119 120 SelectorList argument_selector_list {}; 121 122 // Used for :lang(en-gb,dk) 123 Vector<FlyString> languages {}; 124 }; 125 126 struct Attribute { 127 enum class MatchType { 128 HasAttribute, 129 ExactValueMatch, 130 ContainsWord, // [att~=val] 131 ContainsString, // [att*=val] 132 StartsWithSegment, // [att|=val] 133 StartsWithString, // [att^=val] 134 EndsWithString, // [att$=val] 135 }; 136 enum class CaseType { 137 DefaultMatch, 138 CaseSensitiveMatch, 139 CaseInsensitiveMatch, 140 }; 141 MatchType match_type; 142 FlyString name {}; 143 String value {}; 144 CaseType case_type; 145 }; 146 147 struct Name { 148 Name(FlyString n) 149 : name(move(n)) 150 , lowercase_name(name.to_string().to_lowercase().release_value_but_fixme_should_propagate_errors()) 151 { 152 } 153 154 FlyString name; 155 FlyString lowercase_name; 156 }; 157 158 Type type; 159 Variant<Empty, Attribute, PseudoClass, PseudoElement, Name> value {}; 160 161 Attribute const& attribute() const { return value.get<Attribute>(); } 162 Attribute& attribute() { return value.get<Attribute>(); } 163 PseudoClass const& pseudo_class() const { return value.get<PseudoClass>(); } 164 PseudoClass& pseudo_class() { return value.get<PseudoClass>(); } 165 PseudoElement const& pseudo_element() const { return value.get<PseudoElement>(); } 166 PseudoElement& pseudo_element() { return value.get<PseudoElement>(); } 167 168 FlyString const& name() const { return value.get<Name>().name; } 169 FlyString& name() { return value.get<Name>().name; } 170 FlyString const& lowercase_name() const { return value.get<Name>().lowercase_name; } 171 FlyString& lowercase_name() { return value.get<Name>().lowercase_name; } 172 173 ErrorOr<String> serialize() const; 174 }; 175 176 enum class Combinator { 177 None, 178 ImmediateChild, // > 179 Descendant, // <whitespace> 180 NextSibling, // + 181 SubsequentSibling, // ~ 182 Column, // || 183 }; 184 185 struct CompoundSelector { 186 // Spec-wise, the <combinator> is not part of a <compound-selector>, 187 // but it is more understandable to put them together. 188 Combinator combinator { Combinator::None }; 189 Vector<SimpleSelector> simple_selectors; 190 }; 191 192 static NonnullRefPtr<Selector> create(Vector<CompoundSelector>&& compound_selectors) 193 { 194 return adopt_ref(*new Selector(move(compound_selectors))); 195 } 196 197 ~Selector() = default; 198 199 Vector<CompoundSelector> const& compound_selectors() const { return m_compound_selectors; } 200 Optional<PseudoElement> pseudo_element() const { return m_pseudo_element; } 201 u32 specificity() const; 202 ErrorOr<String> serialize() const; 203 204private: 205 explicit Selector(Vector<CompoundSelector>&&); 206 207 Vector<CompoundSelector> m_compound_selectors; 208 mutable Optional<u32> m_specificity; 209 Optional<Selector::PseudoElement> m_pseudo_element; 210}; 211 212constexpr StringView pseudo_element_name(Selector::PseudoElement pseudo_element) 213{ 214 switch (pseudo_element) { 215 case Selector::PseudoElement::Before: 216 return "before"sv; 217 case Selector::PseudoElement::After: 218 return "after"sv; 219 case Selector::PseudoElement::FirstLine: 220 return "first-line"sv; 221 case Selector::PseudoElement::FirstLetter: 222 return "first-letter"sv; 223 case Selector::PseudoElement::Marker: 224 return "marker"sv; 225 case Selector::PseudoElement::ProgressBar: 226 return "-webkit-progress-bar"sv; 227 case Selector::PseudoElement::ProgressValue: 228 return "-webkit-progress-value"sv; 229 case Selector::PseudoElement::Placeholder: 230 return "placeholder"sv; 231 case Selector::PseudoElement::PseudoElementCount: 232 break; 233 } 234 VERIFY_NOT_REACHED(); 235} 236 237Optional<Selector::PseudoElement> pseudo_element_from_string(StringView); 238 239constexpr StringView pseudo_class_name(Selector::SimpleSelector::PseudoClass::Type pseudo_class) 240{ 241 switch (pseudo_class) { 242 case Selector::SimpleSelector::PseudoClass::Type::Link: 243 return "link"sv; 244 case Selector::SimpleSelector::PseudoClass::Type::Visited: 245 return "visited"sv; 246 case Selector::SimpleSelector::PseudoClass::Type::Hover: 247 return "hover"sv; 248 case Selector::SimpleSelector::PseudoClass::Type::Focus: 249 return "focus"sv; 250 case Selector::SimpleSelector::PseudoClass::Type::FocusWithin: 251 return "focus-within"sv; 252 case Selector::SimpleSelector::PseudoClass::Type::FirstChild: 253 return "first-child"sv; 254 case Selector::SimpleSelector::PseudoClass::Type::LastChild: 255 return "last-child"sv; 256 case Selector::SimpleSelector::PseudoClass::Type::OnlyChild: 257 return "only-child"sv; 258 case Selector::SimpleSelector::PseudoClass::Type::Empty: 259 return "empty"sv; 260 case Selector::SimpleSelector::PseudoClass::Type::Root: 261 return "root"sv; 262 case Selector::SimpleSelector::PseudoClass::Type::FirstOfType: 263 return "first-of-type"sv; 264 case Selector::SimpleSelector::PseudoClass::Type::LastOfType: 265 return "last-of-type"sv; 266 case Selector::SimpleSelector::PseudoClass::Type::OnlyOfType: 267 return "only-of-type"sv; 268 case Selector::SimpleSelector::PseudoClass::Type::NthOfType: 269 return "nth-of-type"sv; 270 case Selector::SimpleSelector::PseudoClass::Type::NthLastOfType: 271 return "nth-last-of-type"sv; 272 case Selector::SimpleSelector::PseudoClass::Type::Disabled: 273 return "disabled"sv; 274 case Selector::SimpleSelector::PseudoClass::Type::Enabled: 275 return "enabled"sv; 276 case Selector::SimpleSelector::PseudoClass::Type::Checked: 277 return "checked"sv; 278 case Selector::SimpleSelector::PseudoClass::Type::Active: 279 return "active"sv; 280 case Selector::SimpleSelector::PseudoClass::Type::NthChild: 281 return "nth-child"sv; 282 case Selector::SimpleSelector::PseudoClass::Type::NthLastChild: 283 return "nth-last-child"sv; 284 case Selector::SimpleSelector::PseudoClass::Type::Is: 285 return "is"sv; 286 case Selector::SimpleSelector::PseudoClass::Type::Not: 287 return "not"sv; 288 case Selector::SimpleSelector::PseudoClass::Type::Where: 289 return "where"sv; 290 case Selector::SimpleSelector::PseudoClass::Type::Lang: 291 return "lang"sv; 292 } 293 VERIFY_NOT_REACHED(); 294} 295 296ErrorOr<String> serialize_a_group_of_selectors(Vector<NonnullRefPtr<Selector>> const& selectors); 297 298} 299 300namespace AK { 301 302template<> 303struct Formatter<Web::CSS::Selector> : Formatter<StringView> { 304 ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Selector const& selector) 305 { 306 return Formatter<StringView>::format(builder, TRY(selector.serialize())); 307 } 308}; 309 310}