/src/serenity/Userland/Libraries/LibWeb/CSS/Selector.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include "Selector.h" |
9 | | #include <AK/GenericShorthands.h> |
10 | | #include <LibWeb/CSS/Serialize.h> |
11 | | |
12 | | namespace Web::CSS { |
13 | | |
14 | | Selector::Selector(Vector<CompoundSelector>&& compound_selectors) |
15 | 0 | : m_compound_selectors(move(compound_selectors)) |
16 | 0 | { |
17 | | // FIXME: This assumes that only one pseudo-element is allowed in a selector, and that it appears at the end. |
18 | | // This is not true in Selectors-4! |
19 | 0 | if (!m_compound_selectors.is_empty()) { |
20 | 0 | for (auto const& simple_selector : m_compound_selectors.last().simple_selectors) { |
21 | 0 | if (simple_selector.type == SimpleSelector::Type::PseudoElement) { |
22 | 0 | m_pseudo_element = simple_selector.pseudo_element(); |
23 | 0 | break; |
24 | 0 | } |
25 | 0 | } |
26 | 0 | } |
27 | | |
28 | | // https://drafts.csswg.org/css-nesting-1/#contain-the-nesting-selector |
29 | | // "A selector is said to contain the nesting selector if, when it was parsed as any type of selector, |
30 | | // a <delim-token> with the value "&" (U+0026 AMPERSAND) was encountered." |
31 | 0 | for (auto const& compound_selector : m_compound_selectors) { |
32 | 0 | for (auto const& simple_selector : compound_selector.simple_selectors) { |
33 | 0 | if (simple_selector.type == SimpleSelector::Type::Nesting) { |
34 | 0 | m_contains_the_nesting_selector = true; |
35 | 0 | break; |
36 | 0 | } |
37 | 0 | if (simple_selector.type == SimpleSelector::Type::PseudoClass) { |
38 | 0 | for (auto const& child_selector : simple_selector.pseudo_class().argument_selector_list) { |
39 | 0 | if (child_selector->contains_the_nesting_selector()) { |
40 | 0 | m_contains_the_nesting_selector = true; |
41 | 0 | break; |
42 | 0 | } |
43 | 0 | } |
44 | 0 | if (m_contains_the_nesting_selector) |
45 | 0 | break; |
46 | 0 | } |
47 | 0 | } |
48 | 0 | if (m_contains_the_nesting_selector) |
49 | 0 | break; |
50 | 0 | } |
51 | |
|
52 | 0 | collect_ancestor_hashes(); |
53 | 0 | } |
54 | | |
55 | | void Selector::collect_ancestor_hashes() |
56 | 0 | { |
57 | 0 | size_t next_hash_index = 0; |
58 | 0 | auto append_unique_hash = [&](u32 hash) -> bool { |
59 | 0 | if (next_hash_index >= m_ancestor_hashes.size()) |
60 | 0 | return true; |
61 | 0 | for (size_t i = 0; i < next_hash_index; ++i) { |
62 | 0 | if (m_ancestor_hashes[i] == hash) |
63 | 0 | return false; |
64 | 0 | } |
65 | 0 | m_ancestor_hashes[next_hash_index++] = hash; |
66 | 0 | return false; |
67 | 0 | }; |
68 | |
|
69 | 0 | auto last_combinator = m_compound_selectors.last().combinator; |
70 | 0 | for (ssize_t compound_selector_index = static_cast<ssize_t>(m_compound_selectors.size()) - 2; compound_selector_index >= 0; --compound_selector_index) { |
71 | 0 | auto const& compound_selector = m_compound_selectors[compound_selector_index]; |
72 | 0 | if (last_combinator == Combinator::Descendant || last_combinator == Combinator::ImmediateChild) { |
73 | 0 | for (auto const& simple_selector : compound_selector.simple_selectors) { |
74 | 0 | switch (simple_selector.type) { |
75 | 0 | case SimpleSelector::Type::Id: |
76 | 0 | case SimpleSelector::Type::Class: |
77 | 0 | if (append_unique_hash(simple_selector.name().hash())) |
78 | 0 | return; |
79 | 0 | break; |
80 | 0 | case SimpleSelector::Type::TagName: |
81 | 0 | if (append_unique_hash(simple_selector.qualified_name().name.name.hash())) |
82 | 0 | return; |
83 | 0 | break; |
84 | 0 | case SimpleSelector::Type::Attribute: |
85 | 0 | if (append_unique_hash(simple_selector.attribute().qualified_name.name.name.hash())) |
86 | 0 | return; |
87 | 0 | break; |
88 | 0 | default: |
89 | 0 | break; |
90 | 0 | } |
91 | 0 | } |
92 | 0 | } |
93 | 0 | last_combinator = compound_selector.combinator; |
94 | 0 | } |
95 | | |
96 | 0 | for (size_t i = next_hash_index; i < m_ancestor_hashes.size(); ++i) |
97 | 0 | m_ancestor_hashes[i] = 0; |
98 | 0 | } |
99 | | |
100 | | // https://www.w3.org/TR/selectors-4/#specificity-rules |
101 | | u32 Selector::specificity() const |
102 | 0 | { |
103 | 0 | if (m_specificity.has_value()) |
104 | 0 | return *m_specificity; |
105 | | |
106 | 0 | constexpr u32 ids_shift = 16; |
107 | 0 | constexpr u32 classes_shift = 8; |
108 | 0 | constexpr u32 tag_names_shift = 0; |
109 | 0 | constexpr u32 ids_mask = 0xff << ids_shift; |
110 | 0 | constexpr u32 classes_mask = 0xff << classes_shift; |
111 | 0 | constexpr u32 tag_names_mask = 0xff << tag_names_shift; |
112 | |
|
113 | 0 | u32 ids = 0; |
114 | 0 | u32 classes = 0; |
115 | 0 | u32 tag_names = 0; |
116 | |
|
117 | 0 | auto count_specificity_of_most_complex_selector = [&](auto& selector_list) { |
118 | 0 | u32 max_selector_list_argument_specificity = 0; |
119 | 0 | for (auto const& complex_selector : selector_list) { |
120 | 0 | max_selector_list_argument_specificity = max(max_selector_list_argument_specificity, complex_selector->specificity()); |
121 | 0 | } |
122 | |
|
123 | 0 | u32 child_ids = (max_selector_list_argument_specificity & ids_mask) >> ids_shift; |
124 | 0 | u32 child_classes = (max_selector_list_argument_specificity & classes_mask) >> classes_shift; |
125 | 0 | u32 child_tag_names = (max_selector_list_argument_specificity & tag_names_mask) >> tag_names_shift; |
126 | |
|
127 | 0 | ids += child_ids; |
128 | 0 | classes += child_classes; |
129 | 0 | tag_names += child_tag_names; |
130 | 0 | }; |
131 | |
|
132 | 0 | for (auto& list : m_compound_selectors) { |
133 | 0 | for (auto& simple_selector : list.simple_selectors) { |
134 | 0 | switch (simple_selector.type) { |
135 | 0 | case SimpleSelector::Type::Id: |
136 | | // count the number of ID selectors in the selector (= A) |
137 | 0 | ++ids; |
138 | 0 | break; |
139 | 0 | case SimpleSelector::Type::Class: |
140 | 0 | case SimpleSelector::Type::Attribute: |
141 | | // count the number of class selectors, attributes selectors, and pseudo-classes in the selector (= B) |
142 | 0 | ++classes; |
143 | 0 | break; |
144 | 0 | case SimpleSelector::Type::PseudoClass: { |
145 | 0 | auto& pseudo_class = simple_selector.pseudo_class(); |
146 | 0 | switch (pseudo_class.type) { |
147 | 0 | case PseudoClass::Has: |
148 | 0 | case PseudoClass::Is: |
149 | 0 | case PseudoClass::Not: { |
150 | | // The specificity of an :is(), :not(), or :has() pseudo-class is replaced by the |
151 | | // specificity of the most specific complex selector in its selector list argument. |
152 | 0 | count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list); |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | case PseudoClass::NthChild: |
156 | 0 | case PseudoClass::NthLastChild: { |
157 | | // Analogously, the specificity of an :nth-child() or :nth-last-child() selector |
158 | | // is the specificity of the pseudo class itself (counting as one pseudo-class selector) |
159 | | // plus the specificity of the most specific complex selector in its selector list argument (if any). |
160 | 0 | ++classes; |
161 | 0 | count_specificity_of_most_complex_selector(pseudo_class.argument_selector_list); |
162 | 0 | break; |
163 | 0 | } |
164 | 0 | case PseudoClass::Where: |
165 | | // The specificity of a :where() pseudo-class is replaced by zero. |
166 | 0 | break; |
167 | 0 | default: |
168 | 0 | ++classes; |
169 | 0 | break; |
170 | 0 | } |
171 | 0 | break; |
172 | 0 | } |
173 | 0 | case SimpleSelector::Type::TagName: |
174 | 0 | case SimpleSelector::Type::PseudoElement: |
175 | | // count the number of type selectors and pseudo-elements in the selector (= C) |
176 | 0 | ++tag_names; |
177 | 0 | break; |
178 | 0 | case SimpleSelector::Type::Universal: |
179 | | // ignore the universal selector |
180 | 0 | break; |
181 | 0 | case SimpleSelector::Type::Nesting: |
182 | | // We should have replaced this already |
183 | 0 | VERIFY_NOT_REACHED(); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | } |
187 | | |
188 | | // Due to storage limitations, implementations may have limitations on the size of A, B, or C. |
189 | | // If so, values higher than the limit must be clamped to that limit, and not overflow. |
190 | 0 | m_specificity = (min(ids, 0xff) << ids_shift) |
191 | 0 | + (min(classes, 0xff) << classes_shift) |
192 | 0 | + (min(tag_names, 0xff) << tag_names_shift); |
193 | |
|
194 | 0 | return *m_specificity; |
195 | 0 | } |
196 | | |
197 | | // https://www.w3.org/TR/cssom/#serialize-a-simple-selector |
198 | | String Selector::SimpleSelector::serialize() const |
199 | 0 | { |
200 | 0 | StringBuilder s; |
201 | 0 | switch (type) { |
202 | 0 | case Selector::SimpleSelector::Type::TagName: |
203 | 0 | case Selector::SimpleSelector::Type::Universal: { |
204 | 0 | auto qualified_name = this->qualified_name(); |
205 | | // 1. If the namespace prefix maps to a namespace that is not the default namespace and is not the null |
206 | | // namespace (not in a namespace) append the serialization of the namespace prefix as an identifier, |
207 | | // followed by a "|" (U+007C) to s. |
208 | 0 | if (qualified_name.namespace_type == QualifiedName::NamespaceType::Named) { |
209 | 0 | serialize_an_identifier(s, qualified_name.namespace_); |
210 | 0 | s.append('|'); |
211 | 0 | } |
212 | | |
213 | | // 2. If the namespace prefix maps to a namespace that is the null namespace (not in a namespace) |
214 | | // append "|" (U+007C) to s. |
215 | 0 | if (qualified_name.namespace_type == QualifiedName::NamespaceType::None) |
216 | 0 | s.append('|'); |
217 | | |
218 | | // 3. If this is a type selector append the serialization of the element name as an identifier to s. |
219 | 0 | if (type == Selector::SimpleSelector::Type::TagName) |
220 | 0 | serialize_an_identifier(s, qualified_name.name.name); |
221 | | |
222 | | // 4. If this is a universal selector append "*" (U+002A) to s. |
223 | 0 | if (type == Selector::SimpleSelector::Type::Universal) |
224 | 0 | s.append('*'); |
225 | |
|
226 | 0 | break; |
227 | 0 | } |
228 | 0 | case Selector::SimpleSelector::Type::Attribute: { |
229 | 0 | auto& attribute = this->attribute(); |
230 | | |
231 | | // 1. Append "[" (U+005B) to s. |
232 | 0 | s.append('['); |
233 | | |
234 | | // 2. If the namespace prefix maps to a namespace that is not the null namespace (not in a namespace) |
235 | | // append the serialization of the namespace prefix as an identifier, followed by a "|" (U+007C) to s. |
236 | 0 | if (attribute.qualified_name.namespace_type == QualifiedName::NamespaceType::Named) { |
237 | 0 | serialize_an_identifier(s, attribute.qualified_name.namespace_); |
238 | 0 | s.append('|'); |
239 | 0 | } |
240 | | |
241 | | // 3. Append the serialization of the attribute name as an identifier to s. |
242 | 0 | serialize_an_identifier(s, attribute.qualified_name.name.name); |
243 | | |
244 | | // 4. If there is an attribute value specified, append "=", "~=", "|=", "^=", "$=", or "*=" as appropriate (depending on the type of attribute selector), |
245 | | // followed by the serialization of the attribute value as a string, to s. |
246 | 0 | if (!attribute.value.is_empty()) { |
247 | 0 | switch (attribute.match_type) { |
248 | 0 | case Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch: |
249 | 0 | s.append("="sv); |
250 | 0 | break; |
251 | 0 | case Selector::SimpleSelector::Attribute::MatchType::ContainsWord: |
252 | 0 | s.append("~="sv); |
253 | 0 | break; |
254 | 0 | case Selector::SimpleSelector::Attribute::MatchType::ContainsString: |
255 | 0 | s.append("*="sv); |
256 | 0 | break; |
257 | 0 | case Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment: |
258 | 0 | s.append("|="sv); |
259 | 0 | break; |
260 | 0 | case Selector::SimpleSelector::Attribute::MatchType::StartsWithString: |
261 | 0 | s.append("^="sv); |
262 | 0 | break; |
263 | 0 | case Selector::SimpleSelector::Attribute::MatchType::EndsWithString: |
264 | 0 | s.append("$="sv); |
265 | 0 | break; |
266 | 0 | default: |
267 | 0 | break; |
268 | 0 | } |
269 | | |
270 | 0 | serialize_a_string(s, attribute.value); |
271 | 0 | } |
272 | | |
273 | | // 5. If the attribute selector has the case-insensitivity flag present, append " i" (U+0020 U+0069) to s. |
274 | | // If the attribute selector has the case-insensitivity flag present, append " s" (U+0020 U+0073) to s. |
275 | | // (the line just above is an addition to CSS OM to match Selectors Level 4 last draft) |
276 | 0 | switch (attribute.case_type) { |
277 | 0 | case Selector::SimpleSelector::Attribute::CaseType::CaseInsensitiveMatch: |
278 | 0 | s.append(" i"sv); |
279 | 0 | break; |
280 | 0 | case Selector::SimpleSelector::Attribute::CaseType::CaseSensitiveMatch: |
281 | 0 | s.append(" s"sv); |
282 | 0 | break; |
283 | 0 | default: |
284 | 0 | break; |
285 | 0 | } |
286 | | |
287 | | // 6. Append "]" (U+005D) to s. |
288 | 0 | s.append(']'); |
289 | 0 | break; |
290 | 0 | } |
291 | | |
292 | 0 | case Selector::SimpleSelector::Type::Class: |
293 | | // Append a "." (U+002E), followed by the serialization of the class name as an identifier to s. |
294 | 0 | s.append('.'); |
295 | 0 | serialize_an_identifier(s, name()); |
296 | 0 | break; |
297 | | |
298 | 0 | case Selector::SimpleSelector::Type::Id: |
299 | | // Append a "#" (U+0023), followed by the serialization of the ID as an identifier to s. |
300 | 0 | s.append('#'); |
301 | 0 | serialize_an_identifier(s, name()); |
302 | 0 | break; |
303 | | |
304 | 0 | case Selector::SimpleSelector::Type::PseudoClass: { |
305 | 0 | auto& pseudo_class = this->pseudo_class(); |
306 | |
|
307 | 0 | auto metadata = pseudo_class_metadata(pseudo_class.type); |
308 | | // HACK: `:host()` has both a function and a non-function form, so handle that first. |
309 | | // It's also not in the spec. |
310 | 0 | if (pseudo_class.type == PseudoClass::Host) { |
311 | 0 | if (pseudo_class.argument_selector_list.is_empty()) { |
312 | 0 | s.append(':'); |
313 | 0 | s.append(pseudo_class_name(pseudo_class.type)); |
314 | 0 | } else { |
315 | 0 | s.append(':'); |
316 | 0 | s.append(pseudo_class_name(pseudo_class.type)); |
317 | 0 | s.append('('); |
318 | 0 | s.append(serialize_a_group_of_selectors(pseudo_class.argument_selector_list)); |
319 | 0 | s.append(')'); |
320 | 0 | } |
321 | 0 | } |
322 | | // If the pseudo-class does not accept arguments append ":" (U+003A), followed by the name of the pseudo-class, to s. |
323 | 0 | else if (metadata.is_valid_as_identifier) { |
324 | 0 | s.append(':'); |
325 | 0 | s.append(pseudo_class_name(pseudo_class.type)); |
326 | 0 | } |
327 | | // Otherwise, append ":" (U+003A), followed by the name of the pseudo-class, followed by "(" (U+0028), |
328 | | // followed by the value of the pseudo-class argument(s) determined as per below, followed by ")" (U+0029), to s. |
329 | 0 | else { |
330 | 0 | s.append(':'); |
331 | 0 | s.append(pseudo_class_name(pseudo_class.type)); |
332 | 0 | s.append('('); |
333 | 0 | if (pseudo_class.type == PseudoClass::NthChild |
334 | 0 | || pseudo_class.type == PseudoClass::NthLastChild |
335 | 0 | || pseudo_class.type == PseudoClass::NthOfType |
336 | 0 | || pseudo_class.type == PseudoClass::NthLastOfType) { |
337 | | // The result of serializing the value using the rules to serialize an <an+b> value. |
338 | 0 | s.append(pseudo_class.nth_child_pattern.serialize()); |
339 | 0 | } else if (pseudo_class.type == PseudoClass::Not |
340 | 0 | || pseudo_class.type == PseudoClass::Is |
341 | 0 | || pseudo_class.type == PseudoClass::Where) { |
342 | | // The result of serializing the value using the rules for serializing a group of selectors. |
343 | | // NOTE: `:is()` and `:where()` aren't in the spec for this yet, but it should be! |
344 | 0 | s.append(serialize_a_group_of_selectors(pseudo_class.argument_selector_list)); |
345 | 0 | } else if (pseudo_class.type == PseudoClass::Lang) { |
346 | | // The serialization of a comma-separated list of each argument’s serialization as a string, preserving relative order. |
347 | 0 | s.join(", "sv, pseudo_class.languages); |
348 | 0 | } |
349 | 0 | s.append(')'); |
350 | 0 | } |
351 | 0 | break; |
352 | 0 | } |
353 | 0 | case Selector::SimpleSelector::Type::PseudoElement: |
354 | | // Note: Pseudo-elements are dealt with in Selector::serialize() |
355 | 0 | break; |
356 | 0 | case Type::Nesting: |
357 | | // AD-HOC: Not in spec yet. |
358 | 0 | s.append('&'); |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | return MUST(s.to_string()); |
362 | 0 | } |
363 | | |
364 | | // https://www.w3.org/TR/cssom/#serialize-a-selector |
365 | | String Selector::serialize() const |
366 | 0 | { |
367 | 0 | StringBuilder s; |
368 | | |
369 | | // To serialize a selector let s be the empty string, run the steps below for each part of the chain of the selector, and finally return s: |
370 | 0 | for (size_t i = 0; i < compound_selectors().size(); ++i) { |
371 | 0 | auto const& compound_selector = compound_selectors()[i]; |
372 | | // 1. If there is only one simple selector in the compound selectors which is a universal selector, append the result of serializing the universal selector to s. |
373 | 0 | if (compound_selector.simple_selectors.size() == 1 |
374 | 0 | && compound_selector.simple_selectors.first().type == Selector::SimpleSelector::Type::Universal) { |
375 | 0 | s.append(compound_selector.simple_selectors.first().serialize()); |
376 | 0 | } |
377 | | // 2. Otherwise, for each simple selector in the compound selectors that is not a universal selector |
378 | | // of which the namespace prefix maps to a namespace that is not the default namespace |
379 | | // serialize the simple selector and append the result to s. |
380 | 0 | else { |
381 | 0 | for (auto& simple_selector : compound_selector.simple_selectors) { |
382 | 0 | if (simple_selector.type == SimpleSelector::Type::Universal) { |
383 | 0 | auto qualified_name = simple_selector.qualified_name(); |
384 | 0 | if (qualified_name.namespace_type == SimpleSelector::QualifiedName::NamespaceType::Default) |
385 | 0 | continue; |
386 | | // FIXME: I *think* if we have a namespace prefix that happens to equal the same as the default namespace, |
387 | | // we also should skip it. But we don't have access to that here. eg: |
388 | | // <style> |
389 | | // @namespace "http://example"; |
390 | | // @namespace foo "http://example"; |
391 | | // foo|*.bar { } /* This would skip the `foo|*` when serializing. */ |
392 | | // </style> |
393 | 0 | } |
394 | 0 | s.append(simple_selector.serialize()); |
395 | 0 | } |
396 | 0 | } |
397 | | |
398 | | // 3. If this is not the last part of the chain of the selector append a single SPACE (U+0020), |
399 | | // followed by the combinator ">", "+", "~", ">>", "||", as appropriate, followed by another |
400 | | // single SPACE (U+0020) if the combinator was not whitespace, to s. |
401 | 0 | if (i != compound_selectors().size() - 1) { |
402 | 0 | s.append(' '); |
403 | | // Note: The combinator that appears between parts `i` and `i+1` appears with the `i+1` selector, |
404 | | // so we have to check that one. |
405 | 0 | switch (compound_selectors()[i + 1].combinator) { |
406 | 0 | case Selector::Combinator::ImmediateChild: |
407 | 0 | s.append("> "sv); |
408 | 0 | break; |
409 | 0 | case Selector::Combinator::NextSibling: |
410 | 0 | s.append("+ "sv); |
411 | 0 | break; |
412 | 0 | case Selector::Combinator::SubsequentSibling: |
413 | 0 | s.append("~ "sv); |
414 | 0 | break; |
415 | 0 | case Selector::Combinator::Column: |
416 | 0 | s.append("|| "sv); |
417 | 0 | break; |
418 | 0 | default: |
419 | 0 | break; |
420 | 0 | } |
421 | 0 | } else { |
422 | | // 4. If this is the last part of the chain of the selector and there is a pseudo-element, |
423 | | // append "::" followed by the name of the pseudo-element, to s. |
424 | 0 | if (compound_selector.simple_selectors.last().type == Selector::SimpleSelector::Type::PseudoElement) { |
425 | 0 | s.append("::"sv); |
426 | 0 | s.append(compound_selector.simple_selectors.last().pseudo_element().name()); |
427 | 0 | } |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | 0 | return MUST(s.to_string()); |
432 | 0 | } |
433 | | |
434 | | // https://www.w3.org/TR/cssom/#serialize-a-group-of-selectors |
435 | | String serialize_a_group_of_selectors(SelectorList const& selectors) |
436 | 0 | { |
437 | | // To serialize a group of selectors serialize each selector in the group of selectors and then serialize a comma-separated list of these serializations. |
438 | 0 | return MUST(String::join(", "sv, selectors)); |
439 | 0 | } |
440 | | |
441 | | StringView Selector::PseudoElement::name(Selector::PseudoElement::Type pseudo_element) |
442 | 0 | { |
443 | 0 | switch (pseudo_element) { |
444 | 0 | case Selector::PseudoElement::Type::Before: |
445 | 0 | return "before"sv; |
446 | 0 | case Selector::PseudoElement::Type::After: |
447 | 0 | return "after"sv; |
448 | 0 | case Selector::PseudoElement::Type::FirstLine: |
449 | 0 | return "first-line"sv; |
450 | 0 | case Selector::PseudoElement::Type::FirstLetter: |
451 | 0 | return "first-letter"sv; |
452 | 0 | case Selector::PseudoElement::Type::Marker: |
453 | 0 | return "marker"sv; |
454 | 0 | case Selector::PseudoElement::Type::MeterBar: |
455 | 0 | return "-webkit-meter-bar"sv; |
456 | 0 | case Selector::PseudoElement::Type::MeterEvenLessGoodValue: |
457 | 0 | return "-webkit-meter-even-less-good-value"sv; |
458 | 0 | case Selector::PseudoElement::Type::MeterOptimumValue: |
459 | 0 | return "-webkit-meter-optimum-value"sv; |
460 | 0 | case Selector::PseudoElement::Type::MeterSuboptimumValue: |
461 | 0 | return "-webkit-meter-suboptimum-value"sv; |
462 | 0 | case Selector::PseudoElement::Type::ProgressBar: |
463 | 0 | return "-webkit-progress-bar"sv; |
464 | 0 | case Selector::PseudoElement::Type::ProgressValue: |
465 | 0 | return "-webkit-progress-value"sv; |
466 | 0 | case Selector::PseudoElement::Type::Placeholder: |
467 | 0 | return "placeholder"sv; |
468 | 0 | case Selector::PseudoElement::Type::Selection: |
469 | 0 | return "selection"sv; |
470 | 0 | case Selector::PseudoElement::Type::SliderRunnableTrack: |
471 | 0 | return "-webkit-slider-runnable-track"sv; |
472 | 0 | case Selector::PseudoElement::Type::SliderThumb: |
473 | 0 | return "-webkit-slider-thumb"sv; |
474 | 0 | case Selector::PseudoElement::Type::Backdrop: |
475 | 0 | return "backdrop"sv; |
476 | 0 | case Selector::PseudoElement::Type::KnownPseudoElementCount: |
477 | 0 | break; |
478 | 0 | case Selector::PseudoElement::Type::UnknownWebKit: |
479 | 0 | VERIFY_NOT_REACHED(); |
480 | 0 | } |
481 | 0 | VERIFY_NOT_REACHED(); |
482 | 0 | } |
483 | | |
484 | | Optional<Selector::PseudoElement> Selector::PseudoElement::from_string(FlyString const& name) |
485 | 0 | { |
486 | 0 | if (name.equals_ignoring_ascii_case("after"sv)) { |
487 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::After }; |
488 | 0 | } else if (name.equals_ignoring_ascii_case("before"sv)) { |
489 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::Before }; |
490 | 0 | } else if (name.equals_ignoring_ascii_case("first-letter"sv)) { |
491 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::FirstLetter }; |
492 | 0 | } else if (name.equals_ignoring_ascii_case("first-line"sv)) { |
493 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::FirstLine }; |
494 | 0 | } else if (name.equals_ignoring_ascii_case("marker"sv)) { |
495 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::Marker }; |
496 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-meter-bar"sv)) { |
497 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::MeterBar }; |
498 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-meter-even-less-good-value"sv)) { |
499 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::MeterEvenLessGoodValue }; |
500 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-meter-optimum-value"sv)) { |
501 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::MeterOptimumValue }; |
502 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-meter-suboptimum-value"sv)) { |
503 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::MeterSuboptimumValue }; |
504 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-progress-bar"sv)) { |
505 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::ProgressBar }; |
506 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-progress-value"sv)) { |
507 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::ProgressValue }; |
508 | 0 | } else if (name.equals_ignoring_ascii_case("placeholder"sv)) { |
509 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::Placeholder }; |
510 | 0 | } else if (name.equals_ignoring_ascii_case("selection"sv)) { |
511 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::Selection }; |
512 | 0 | } else if (name.equals_ignoring_ascii_case("backdrop"sv)) { |
513 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::Backdrop }; |
514 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-slider-runnable-track"sv)) { |
515 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::SliderRunnableTrack }; |
516 | 0 | } else if (name.equals_ignoring_ascii_case("-webkit-slider-thumb"sv)) { |
517 | 0 | return Selector::PseudoElement { Selector::PseudoElement::Type::SliderThumb }; |
518 | 0 | } |
519 | 0 | return {}; |
520 | 0 | } |
521 | | |
522 | | NonnullRefPtr<Selector> Selector::relative_to(SimpleSelector const& parent) const |
523 | 0 | { |
524 | | // To make us relative to the parent, prepend it to the list of compound selectors, |
525 | | // and ensure the next compound selector starts with a combinator. |
526 | 0 | Vector<CompoundSelector> copied_compound_selectors; |
527 | 0 | copied_compound_selectors.ensure_capacity(compound_selectors().size() + 1); |
528 | 0 | copied_compound_selectors.empend(CompoundSelector { .simple_selectors = { parent } }); |
529 | |
|
530 | 0 | bool first = true; |
531 | 0 | for (auto compound_selector : compound_selectors()) { |
532 | 0 | if (first) { |
533 | 0 | if (compound_selector.combinator == Combinator::None) |
534 | 0 | compound_selector.combinator = Combinator::Descendant; |
535 | 0 | first = false; |
536 | 0 | } |
537 | |
|
538 | 0 | copied_compound_selectors.append(move(compound_selector)); |
539 | 0 | } |
540 | |
|
541 | 0 | return Selector::create(move(copied_compound_selectors)); |
542 | 0 | } |
543 | | |
544 | | NonnullRefPtr<Selector> Selector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const |
545 | 0 | { |
546 | 0 | if (!contains_the_nesting_selector()) |
547 | 0 | return *this; |
548 | | |
549 | 0 | Vector<CompoundSelector> absolutized_compound_selectors; |
550 | 0 | absolutized_compound_selectors.ensure_capacity(m_compound_selectors.size()); |
551 | 0 | for (auto const& compound_selector : m_compound_selectors) |
552 | 0 | absolutized_compound_selectors.append(compound_selector.absolutized(selector_for_nesting)); |
553 | |
|
554 | 0 | return Selector::create(move(absolutized_compound_selectors)); |
555 | 0 | } |
556 | | |
557 | | Selector::CompoundSelector Selector::CompoundSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const |
558 | 0 | { |
559 | | // TODO: Cache if it contains the nesting selector? |
560 | |
|
561 | 0 | Vector<SimpleSelector> absolutized_simple_selectors; |
562 | 0 | absolutized_simple_selectors.ensure_capacity(simple_selectors.size()); |
563 | 0 | for (auto const& simple_selector : simple_selectors) |
564 | 0 | absolutized_simple_selectors.append(simple_selector.absolutized(selector_for_nesting)); |
565 | |
|
566 | 0 | return CompoundSelector { |
567 | 0 | .combinator = this->combinator, |
568 | 0 | .simple_selectors = absolutized_simple_selectors, |
569 | 0 | }; |
570 | 0 | } |
571 | | |
572 | | Selector::SimpleSelector Selector::SimpleSelector::absolutized(Selector::SimpleSelector const& selector_for_nesting) const |
573 | 0 | { |
574 | 0 | switch (type) { |
575 | 0 | case Type::Nesting: |
576 | | // Nesting selectors get replaced directly. |
577 | 0 | return selector_for_nesting; |
578 | | |
579 | 0 | case Type::PseudoClass: { |
580 | | // Pseudo-classes may contain other selectors, so we need to absolutize them. |
581 | | // Copy the PseudoClassSelector, and then replace its argument selector list. |
582 | 0 | auto pseudo_class = this->pseudo_class(); |
583 | 0 | if (!pseudo_class.argument_selector_list.is_empty()) { |
584 | 0 | SelectorList new_selector_list; |
585 | 0 | new_selector_list.ensure_capacity(pseudo_class.argument_selector_list.size()); |
586 | 0 | for (auto const& argument_selector : pseudo_class.argument_selector_list) |
587 | 0 | new_selector_list.append(argument_selector->absolutized(selector_for_nesting)); |
588 | 0 | pseudo_class.argument_selector_list = move(new_selector_list); |
589 | 0 | } |
590 | 0 | return SimpleSelector { |
591 | 0 | .type = Type::PseudoClass, |
592 | 0 | .value = move(pseudo_class), |
593 | 0 | }; |
594 | 0 | } |
595 | | |
596 | 0 | case Type::Universal: |
597 | 0 | case Type::TagName: |
598 | 0 | case Type::Id: |
599 | 0 | case Type::Class: |
600 | 0 | case Type::Attribute: |
601 | 0 | case Type::PseudoElement: |
602 | | // Everything else isn't affected |
603 | 0 | return *this; |
604 | 0 | } |
605 | | |
606 | 0 | VERIFY_NOT_REACHED(); |
607 | 0 | } |
608 | | |
609 | | } |