1use super::*;
4
5pub(crate) fn parse_selector(s: &str) -> Option<Selector> {
6 let chain = parse_selector_chain(s)?;
7 if chain.len() == 1 && chain[0].combinator.is_none() {
8 Some(Selector::Simple(chain[0].simple.clone()))
9 } else {
10 Some(Selector::Chain(chain))
11 }
12}
13
14#[inline]
15fn is_ident_byte(b: u8) -> bool {
16 b.is_ascii_alphanumeric() || b == b'-' || b == b'_'
17}
18
19pub(crate) fn parse_simple_selector(s: &str) -> Option<SimpleSelector> {
20 if s.is_empty() {
21 return None;
22 }
23
24 let mut selector = SimpleSelector::default();
25 let bytes = s.as_bytes();
26 let mut i = 0usize;
27 while i < bytes.len() {
28 match bytes[i] {
29 b'#' => {
30 i += 1;
31 let start = i;
32 while i < bytes.len() && is_ident_byte(bytes[i]) {
33 i += 1;
34 }
35 if start < i {
36 if let Some(id_str) = s.get(start..i) {
37 selector.id = Some(String::from(id_str));
38 }
39 }
40 }
41 b'.' => {
42 i += 1;
43 let start = i;
44 while i < bytes.len() && is_ident_byte(bytes[i]) {
45 i += 1;
46 }
47 if start < i {
48 if let Some(cls_str) = s.get(start..i) {
49 selector.class.push(String::from(cls_str));
50 }
51 }
52 }
53 b'*' => {
54 selector.universal = true;
55 i += 1;
56 }
57 b'[' => {
58 i += 1;
59 let inner_start = i;
60 while i < bytes.len() && bytes[i] != b']' {
61 i += 1;
62 }
63 let inner = s.get(inner_start..i).unwrap_or("");
64 if i < bytes.len() {
65 i += 1;
66 } if let Some(attr) = parse_attr_selector(inner) {
68 selector.attrs.push(attr);
69 }
70 }
71 b':' => {
72 i += 1;
73 if i < bytes.len() && bytes[i] == b':' {
74 i += 1;
75 }
76 let start = i;
77 while i < bytes.len() && is_ident_byte(bytes[i]) {
78 i += 1;
79 }
80 let name = s.get(start..i).unwrap_or("").to_ascii_lowercase();
81 let mut arg = String::new();
82 if i < bytes.len() && bytes[i] == b'(' {
83 i += 1;
84 let arg_start = i;
85 let mut depth = 1;
86 while i < bytes.len() && depth > 0 {
87 match bytes[i] {
88 b'(' => depth += 1,
89 b')' => {
90 depth -= 1;
91 if depth == 0 {
92 break;
93 }
94 }
95 _ => {}
96 }
97 i += 1;
98 }
99 arg = String::from(s.get(arg_start..i).unwrap_or(""));
100 if i < bytes.len() {
101 i += 1;
102 } }
104 if !name.is_empty() {
105 match name.as_str() {
106 "before" => selector.pseudo_element = Some(PseudoElement::Before),
107 "after" => selector.pseudo_element = Some(PseudoElement::After),
108 "marker" => selector.pseudo_element = Some(PseudoElement::Marker),
109 "placeholder" => selector.pseudo_element = Some(PseudoElement::Placeholder),
110 "first-line" => selector.pseudo_element = Some(PseudoElement::FirstLine),
111 "first-letter" => selector.pseudo_element = Some(PseudoElement::FirstLetter),
112 _ => {
113 let pseudo = match name.as_str() {
114 "first-child" => PseudoClass::FirstChild,
115 "last-child" => PseudoClass::LastChild,
116 "nth-child" => {
117 let (a, b, sel) = parse_nth_of(&arg);
118 PseudoClass::NthChild(a, b, sel.map(alloc::boxed::Box::new))
119 }
120 "nth-last-child" => {
121 let (a, b, sel) = parse_nth_of(&arg);
122 PseudoClass::NthLastChild(
123 a,
124 b,
125 sel.map(alloc::boxed::Box::new),
126 )
127 }
128 "not" => match parse_simple_selector(arg.trim()) {
129 Some(inner) => PseudoClass::Not(alloc::boxed::Box::new(inner)),
130 None => PseudoClass::Other(name),
131 },
132 "is" | "where" | "matches" | "any" => {
133 let is_where = name == "where";
134 let list: Vec<SimpleSelector> = split_top_level_commas(&arg)
135 .iter()
136 .filter_map(|s| parse_simple_selector(s.trim()))
137 .collect();
138 if list.is_empty() {
139 PseudoClass::Other(name)
140 } else if is_where {
141 PseudoClass::Where(list)
142 } else {
143 PseudoClass::Is(list)
144 }
145 }
146 "has" => {
147 let list: Vec<(HasCombinator, SimpleSelector)> =
148 split_top_level_commas(&arg)
149 .iter()
150 .filter_map(|s| {
151 let s = s.trim();
152 let (comb, rest) = match s.strip_prefix('>') {
153 Some(r) => (HasCombinator::Child, r.trim()),
154 None => match s.strip_prefix('+') {
155 Some(r) => {
156 (HasCombinator::NextSibling, r.trim())
157 }
158 None => match s.strip_prefix('~') {
159 Some(r) => (
160 HasCombinator::SubsequentSibling,
161 r.trim(),
162 ),
163 None => {
164 (HasCombinator::Descendant, s)
165 }
166 },
167 },
168 };
169 parse_simple_selector(rest).map(|sel| (comb, sel))
170 })
171 .collect();
172 if list.is_empty() {
173 PseudoClass::Other(name)
174 } else {
175 PseudoClass::Has(list)
176 }
177 }
178 "checked" => PseudoClass::Checked,
179 "disabled" => PseudoClass::Disabled,
180 "lang" => {
181 let code = arg
182 .trim()
183 .trim_matches(|c| c == '\'' || c == '"')
184 .to_ascii_lowercase();
185 if code.is_empty() {
186 PseudoClass::Other(name)
187 } else {
188 PseudoClass::Lang(code)
189 }
190 }
191 "dir" => {
192 let d = arg.trim().to_ascii_lowercase();
193 if d == "ltr" || d == "rtl" {
194 PseudoClass::Dir(d)
195 } else {
196 PseudoClass::Other(name)
197 }
198 }
199 "first-of-type" => PseudoClass::FirstOfType,
200 "last-of-type" => PseudoClass::LastOfType,
201 "nth-of-type" => {
202 let (a, b) = parse_nth(&arg);
203 PseudoClass::NthOfType(a, b)
204 }
205 "nth-last-of-type" => {
206 let (a, b) = parse_nth(&arg);
207 PseudoClass::NthLastOfType(a, b)
208 }
209 "only-of-type" => PseudoClass::OnlyOfType,
210 "only-child" => PseudoClass::OnlyChild,
211 "target" => PseudoClass::Target,
212 "empty" => PseudoClass::Empty,
213 _ => PseudoClass::Other(name),
214 };
215 selector.pseudo_classes.push(pseudo);
216 }
217 }
218 }
219 }
220 b if is_ident_byte(b) => {
221 let start = i;
222 while i < bytes.len() && is_ident_byte(bytes[i]) {
223 i += 1;
224 }
225 if start < i {
226 if let Some(tag_str) = s.get(start..i) {
227 selector.tag_name = Some(tag_str.to_ascii_lowercase());
228 }
229 }
230 }
231 _ => i += 1,
232 }
233 }
234
235 if !selector.universal
236 && selector.tag_name.is_none()
237 && selector.id.is_none()
238 && selector.class.is_empty()
239 && selector.pseudo_classes.is_empty()
240 && selector.attrs.is_empty()
241 {
242 None
243 } else {
244 Some(selector)
245 }
246}
247
248pub(crate) fn parse_attr_selector(inner: &str) -> Option<AttrSelector> {
250 let inner = inner.trim();
251 if inner.is_empty() {
252 return None;
253 }
254 for (token, op) in &[
256 ("^=", AttrOp::Prefix),
257 ("$=", AttrOp::Suffix),
258 ("*=", AttrOp::Contains),
259 ("~=", AttrOp::Word),
260 ("|=", AttrOp::Dash),
261 ("=", AttrOp::Eq),
262 ] {
263 if let Some(pos) = inner.find(token) {
264 let name = inner.get(..pos).unwrap_or("").trim().to_lowercase();
265 let mut raw = inner.get(pos + token.len()..).unwrap_or("").trim();
266 let mut case_insensitive = false;
271 for flag in [" i", " I", " s", " S"] {
272 if let Some(stripped) = raw.strip_suffix(flag) {
273 case_insensitive = flag.eq_ignore_ascii_case(" i");
274 raw = stripped.trim_end();
275 break;
276 }
277 }
278 let value = raw.trim_matches('"').trim_matches('\'').to_string();
279 if name.is_empty() {
280 return None;
281 }
282 return Some(AttrSelector {
283 name,
284 op: op.clone(),
285 value,
286 case_insensitive,
287 });
288 }
289 }
290 Some(AttrSelector {
291 name: inner.to_lowercase(),
292 op: AttrOp::Exists,
293 value: String::new(),
294 case_insensitive: false,
295 })
296}
297
298pub(crate) fn parse_nth_of(arg: &str) -> (i64, i64, Option<SimpleSelector>) {
304 let full = arg.trim();
305 let lower = full.to_lowercase();
306 match lower.find(" of ") {
307 Some(pos) => {
308 let an_b_part = full.get(..pos).unwrap_or(full);
309 let sel_part = full.get(pos + 4..).unwrap_or("");
310 let (a, b) = parse_nth(an_b_part);
311 (a, b, parse_simple_selector(sel_part.trim()))
312 }
313 None => {
314 let (a, b) = parse_nth(full);
315 (a, b, None)
316 }
317 }
318}
319
320pub(crate) fn parse_nth(arg: &str) -> (i64, i64) {
322 let full = arg.trim().to_lowercase();
323 let s = full.split(" of ").next().unwrap_or(&full).trim();
328 match s {
329 "odd" => return (2, 1),
330 "even" => return (2, 0),
331 _ => {}
332 }
333 if let Some(npos) = s.find('n') {
334 let a_str = s.get(..npos).unwrap_or("").trim();
336 let a: i64 = match a_str {
337 "" | "+" => 1,
338 "-" => -1,
339 _ => a_str.parse().unwrap_or(1),
340 };
341 let b_str: String = s
343 .get(npos + 1..)
344 .unwrap_or("")
345 .chars()
346 .filter(|c| !c.is_whitespace())
347 .collect();
348 let b: i64 = if b_str.is_empty() {
349 0
350 } else {
351 b_str.parse().unwrap_or(0)
352 };
353 (a, b)
354 } else {
355 (0, s.parse().unwrap_or(0))
357 }
358}
359
360pub(crate) fn parse_selector_chain(s: &str) -> Option<Vec<SelectorStep>> {
361 if s.is_empty() {
362 return None;
363 }
364
365 let mut tokens: Vec<(Option<Combinator>, String)> = Vec::new();
366 let chars: Vec<char> = s.chars().collect();
367 let mut i = 0usize;
368 let mut current = String::new();
369 let mut pending_combinator: Option<Combinator> = None;
370 let mut depth = 0i32; while i < chars.len() {
373 let c = chars[i];
374 if c == '[' || c == '(' {
375 depth += 1;
376 current.push(c);
377 i += 1;
378 continue;
379 }
380 if c == ']' || c == ')' {
381 depth -= 1;
382 current.push(c);
383 i += 1;
384 continue;
385 }
386 if depth > 0 {
387 current.push(c);
388 i += 1;
389 continue;
390 }
391 if c == '>' || c == '+' || c == '~' {
392 if !current.trim().is_empty() {
393 tokens.push((pending_combinator, current.trim().to_string()));
394 current.clear();
395 }
396 pending_combinator = Some(match c {
397 '>' => Combinator::Child,
398 '+' => Combinator::NextSibling,
399 _ => Combinator::SubsequentSibling,
400 });
401 i += 1;
402 while i < chars.len() && chars[i].is_ascii_whitespace() {
403 i += 1;
404 }
405 continue;
406 }
407 if c.is_ascii_whitespace() {
408 if !current.trim().is_empty() {
409 tokens.push((pending_combinator, current.trim().to_string()));
410 current.clear();
411 pending_combinator = Some(Combinator::Descendant);
412 }
413 i += 1;
414 while i < chars.len() && chars[i].is_ascii_whitespace() {
415 i += 1;
416 }
417 continue;
418 }
419 current.push(c);
420 i += 1;
421 }
422 if !current.trim().is_empty() {
423 tokens.push((pending_combinator, current.trim().to_string()));
424 }
425 if tokens.is_empty() {
426 return None;
427 }
428
429 let mut steps = Vec::new();
430 for (idx, (comb, tok)) in tokens.into_iter().enumerate() {
431 let simple = parse_simple_selector(&tok)?;
432 let combinator = if idx == 0 { None } else { comb };
433 steps.push(SelectorStep { simple, combinator });
434 }
435 Some(steps)
436}
437
438pub(crate) fn is_selector_ident_char(c: char) -> bool {
439 c.is_ascii_alphanumeric() || c == '-' || c == '_'
440}
441
442pub(crate) fn simple_specificity(sel: &SimpleSelector) -> (u32, u32, u32) {
447 let mut spec = (
448 sel.id.as_ref().map_or(0u32, |_| 1),
449 sel.class.len() as u32 + sel.attrs.len() as u32,
450 sel.tag_name.as_ref().map_or(0u32, |_| 1),
451 );
452 for pc in &sel.pseudo_classes {
453 let add = match pc {
454 PseudoClass::Not(inner) => simple_specificity(inner.as_ref()),
455 PseudoClass::Is(list) => list.iter().map(simple_specificity).max().unwrap_or((0, 0, 0)),
456 PseudoClass::Has(list) => list
457 .iter()
458 .map(|(_, sel)| simple_specificity(sel))
459 .max()
460 .unwrap_or((0, 0, 0)),
461 PseudoClass::Where(_) => (0, 0, 0),
462 _ => (0, 1, 0),
463 };
464 spec.0 += add.0;
465 spec.1 += add.1;
466 spec.2 += add.2;
467 }
468 if sel.pseudo_element.is_some() {
472 spec.2 += 1;
473 }
474 spec
475}
476
477pub(crate) fn parse_quotes_pairs(v: &str) -> Option<alloc::vec::Vec<(String, String)>> {
491 let t = v.trim();
492 if t.eq_ignore_ascii_case("none") {
493 return Some(alloc::vec::Vec::new());
494 }
495 let mut lits: alloc::vec::Vec<String> = alloc::vec::Vec::new();
496 let bytes = t.as_bytes();
497 let mut i = 0usize;
498 while i < bytes.len() {
499 if bytes[i] == b'"' || bytes[i] == b'\'' {
500 let quote = bytes[i];
501 let start = i + 1;
502 let mut j = start;
503 while j < bytes.len() && bytes[j] != quote {
504 j += 1;
505 }
506 if let Some(sub) = t.get(start..j) {
507 lits.push(String::from(sub));
508 }
509 i = j + 1;
510 } else {
511 i += 1;
512 }
513 }
514 let mut pairs: alloc::vec::Vec<(String, String)> = alloc::vec::Vec::new();
515 let mut it = lits.into_iter();
516 while let (Some(a), Some(b)) = (it.next(), it.next()) {
517 pairs.push((a, b));
518 }
519 if pairs.is_empty() {
520 None
521 } else {
522 Some(pairs)
523 }
524}
525
526pub(crate) fn unescape_css_string(s: &str) -> String {
533 let mut out = String::with_capacity(s.len());
534 let mut chars = s.chars().peekable();
535 while let Some(c) = chars.next() {
536 if c != '\\' {
537 out.push(c);
538 continue;
539 }
540 let Some(&next) = chars.peek() else {
541 break;
542 };
543 if next.is_ascii_hexdigit() {
544 let mut hex = String::with_capacity(6);
545 while hex.len() < 6 {
546 match chars.peek() {
547 Some(&h) if h.is_ascii_hexdigit() => {
548 hex.push(h);
549 chars.next();
550 }
551 _ => break,
552 }
553 }
554 if let Ok(code) = u32::from_str_radix(&hex, 16) {
555 if let Some(ch) = char::from_u32(code) {
556 out.push(ch);
557 }
558 }
559 if let Some(&w) = chars.peek() {
561 if w.is_ascii_whitespace() {
562 chars.next();
563 }
564 }
565 } else {
566 out.push(next);
567 chars.next();
568 }
569 }
570 out
571}
572
573pub(crate) fn parse_content_value(
574 v: &str,
575 attributes: &BTreeMap<String, String>,
576 counters: &BTreeMap<String, i64>,
577 quotes_pairs: Option<&[(String, String)]>,
578 quote_depth: usize,
579 counter_styles: &[CounterStyleDef],
580) -> String {
581 let t = v.trim();
582 let mut out = String::new();
583 let mut i = 0usize;
584 let bytes = t.as_bytes();
585 while i < bytes.len() {
586 if bytes[i].is_ascii_whitespace() {
588 i += 1;
589 continue;
590 }
591 if bytes[i] == b'"' || bytes[i] == b'\'' {
592 let quote = bytes[i];
593 let start = i + 1;
594 let mut j = start;
595 while j < bytes.len() && bytes[j] != quote {
596 j += 1;
597 }
598 if let Some(sub) = t.get(start..j) {
599 out.push_str(&unescape_css_string(sub));
600 }
601 i = j + 1;
602 continue;
603 }
604 if let Some(rest) = t.get(i..).and_then(|r| r.strip_prefix("attr(")) {
605 if let Some(close) = rest.find(')') {
606 if let Some(inner) = rest.get(..close) {
607 let mut parts = inner.splitn(2, ',');
613 let name = parts.next().unwrap_or("").trim();
614 let fallback = parts.next().map(|s| {
615 let s = s.trim();
616 s.trim_matches('"').trim_matches('\'')
617 });
618 match attributes.get(name) {
619 Some(val) => out.push_str(val),
620 None => {
621 if let Some(fb) = fallback {
622 out.push_str(fb);
623 }
624 }
625 }
626 }
627 i += "attr(".len() + close + 1;
628 continue;
629 }
630 }
631 if let Some(rest) = t.get(i..).and_then(|r| r.strip_prefix("counter(")) {
632 if let Some(close) = rest.find(')') {
633 if let Some(inner) = rest.get(..close) {
634 let mut parts = inner.splitn(2, ',');
638 let name = parts.next().unwrap_or("").trim();
639 let style = parts.next().map(|s| s.trim().to_lowercase());
640 let val = counters.get(name).copied().unwrap_or(0);
641 let val_u32 = val.max(0) as u32;
642 let formatted = match style.as_deref() {
643 Some("upper-roman") => super::super::web_engine::number_to_roman(val_u32, true),
644 Some("lower-roman") => super::super::web_engine::number_to_roman(val_u32, false),
645 Some("upper-alpha") | Some("upper-latin") => {
646 super::super::web_engine::number_to_alpha(val_u32, true)
647 }
648 Some("lower-alpha") | Some("lower-latin") => {
649 super::super::web_engine::number_to_alpha(val_u32, false)
650 }
651 Some(custom) => match counter_styles.iter().find(|d| d.name == custom) {
654 Some(def) => format_with_counter_style(def, val),
655 None => alloc::format!("{}", val),
656 },
657 None => alloc::format!("{}", val),
658 };
659 out.push_str(&formatted);
660 }
661 i += "counter(".len() + close + 1;
662 continue;
663 }
664 }
665 if let Some(rest) = t.get(i..).and_then(|r| r.strip_prefix("counters(")) {
666 if let Some(close) = rest.find(')') {
667 if let Some(inner) = rest.get(..close) {
668 let mut parts = inner.splitn(3, ',');
675 let name = parts.next().unwrap_or("").trim();
676 let _sep = parts.next().map(|s| {
677 s.trim()
678 .trim_matches(|c| c == '"' || c == '\'')
679 .to_string()
680 });
681 let style = parts.next().map(|s| s.trim().to_lowercase());
682 let val = counters.get(name).copied().unwrap_or(0);
683 let val_u32 = val.max(0) as u32;
684 let formatted = match style.as_deref() {
685 Some("upper-roman") => super::super::web_engine::number_to_roman(val_u32, true),
686 Some("lower-roman") => super::super::web_engine::number_to_roman(val_u32, false),
687 Some("upper-alpha") | Some("upper-latin") => {
688 super::super::web_engine::number_to_alpha(val_u32, true)
689 }
690 Some("lower-alpha") | Some("lower-latin") => {
691 super::super::web_engine::number_to_alpha(val_u32, false)
692 }
693 Some(custom) => match counter_styles.iter().find(|d| d.name == custom) {
694 Some(def) => format_with_counter_style(def, val),
695 None => alloc::format!("{}", val),
696 },
697 None => alloc::format!("{}", val),
698 };
699 out.push_str(&formatted);
700 }
701 i += "counters(".len() + close + 1;
702 continue;
703 }
704 }
705 if let Some(rest) = t.get(i..) {
711 if let Some(r2) = rest.strip_prefix("no-open-quote") {
712 let _ = r2;
713 i += "no-open-quote".len();
714 continue;
715 }
716 if let Some(r2) = rest.strip_prefix("no-close-quote") {
717 let _ = r2;
718 i += "no-close-quote".len();
719 continue;
720 }
721 if rest.starts_with("open-quote") {
722 match quotes_pairs {
723 Some(pairs) if !pairs.is_empty() => {
724 let idx = quote_depth.min(pairs.len() - 1);
725 out.push_str(&pairs[idx].0);
726 }
727 Some(_) => {} None => out.push('\u{201C}'),
729 }
730 i += "open-quote".len();
731 continue;
732 }
733 if rest.starts_with("close-quote") {
734 match quotes_pairs {
735 Some(pairs) if !pairs.is_empty() => {
736 let idx = quote_depth.min(pairs.len() - 1);
737 out.push_str(&pairs[idx].1);
738 }
739 Some(_) => {} None => out.push('\u{201D}'),
741 }
742 i += "close-quote".len();
743 continue;
744 }
745 }
746 while i < bytes.len() && !bytes[i].is_ascii_whitespace() {
749 i += 1;
750 }
751 }
752 out
753}
754
755pub(crate) fn parse_counter_decl(v: &str, default: i64) -> alloc::vec::Vec<(String, i64)> {
759 let t = v.trim();
760 if t.is_empty() || t.eq_ignore_ascii_case("none") {
761 return alloc::vec::Vec::new();
762 }
763 let tokens: alloc::vec::Vec<&str> = t.split_whitespace().collect();
764 let mut out = alloc::vec::Vec::new();
765 let mut i = 0usize;
766 while i < tokens.len() {
767 let name = tokens[i];
768 i += 1;
769 let value = if i < tokens.len() {
770 if let Ok(n) = tokens[i].parse::<i64>() {
771 i += 1;
772 n
773 } else {
774 default
775 }
776 } else {
777 default
778 };
779 out.push((name.to_string(), value));
780 }
781 out
782}
783
784pub(crate) fn apply_counter_decls(
791 specified_values: &BTreeMap<String, String>,
792 counters: &mut BTreeMap<String, i64>,
793) -> alloc::collections::BTreeSet<String> {
794 let mut reset_names = alloc::collections::BTreeSet::new();
795 if let Some(reset) = specified_values.get("counter-reset") {
796 for (name, value) in parse_counter_decl(reset, 0) {
797 counters.insert(name.clone(), value);
798 reset_names.insert(name);
799 }
800 }
801 if let Some(incr) = specified_values.get("counter-increment") {
802 for (name, value) in parse_counter_decl(incr, 1) {
803 let cur = counters.get(&name).copied().unwrap_or(0);
804 counters.insert(name, cur + value);
805 }
806 }
807 reset_names
808}
809
810pub(crate) fn selector_pseudo_element(sel: &Selector) -> Option<PseudoElement> {
813 match sel {
814 Selector::Simple(s) => s.pseudo_element,
815 Selector::Chain(steps) => steps.last().and_then(|s| s.simple.pseudo_element),
816 }
817}
818
819pub(crate) fn selector_specificity(sel: &Selector) -> (u32, u32, u32) {
820 match sel {
821 Selector::Simple(s) => simple_specificity(s),
822 Selector::Chain(steps) => {
823 let mut a = 0u32;
824 let mut b = 0u32;
825 let mut c = 0u32;
826 for step in steps {
827 let (sa, sb, sc) = simple_specificity(&step.simple);
828 a += sa;
829 b += sb;
830 c += sc;
831 }
832 (a, b, c)
833 }
834 }
835}
836
837fn is_node_disabled(node: &Node, ancestors: &[AncestorContext<'_>]) -> bool {
838 if let NodeType::Element { attributes, .. } = &node.node_type {
839 if attributes.contains_key("disabled") {
840 return true;
841 }
842 }
843 for anc in ancestors {
844 if let NodeType::Element { tag_name, attributes, .. } = &anc.node.node_type {
845 if tag_name == "fieldset" && attributes.contains_key("disabled") {
846 return true;
847 }
848 }
849 }
850 false
851}
852
853#[allow(clippy::too_many_arguments)]
854pub(crate) fn matches_simple_selector(
855 tag_name: &str,
856 id: &Option<String>,
857 classes: &Vec<String>,
858 attributes: &BTreeMap<String, String>,
859 sel: &SimpleSelector,
860 sibling_index: Option<usize>,
861 sibling_count: Option<usize>,
862 active_pseudo_states: &[&str],
863 type_index: Option<usize>,
864 type_count: Option<usize>,
865 is_empty: bool,
866 node_ptr: usize,
867 node_children: &[Node],
868 own_siblings: Option<&[Node]>,
869 is_disabled: bool,
870) -> bool {
871 if let Some(sel_tag) = &sel.tag_name {
872 if sel_tag != tag_name {
873 return false;
874 }
875 }
876 if let Some(sel_id) = &sel.id {
877 if id.as_ref() != Some(sel_id) {
878 return false;
879 }
880 }
881 for need in &sel.class {
882 if !classes.iter().any(|c| c == need) {
883 return false;
884 }
885 }
886 for attr in &sel.attrs {
887 if !attr_matches(attr, attributes) {
888 return false;
889 }
890 }
891 for pseudo in &sel.pseudo_classes {
892 match pseudo {
893 PseudoClass::FirstChild => {
894 if sibling_index != Some(0) {
895 return false;
896 }
897 }
898 PseudoClass::LastChild => match (sibling_index, sibling_count) {
899 (Some(idx), Some(count)) if idx + 1 == count => {}
900 _ => return false,
901 },
902 PseudoClass::NthChild(a, b, filter) => match filter {
903 Some(sel) => match filtered_sibling_position(own_siblings, node_ptr, sel) {
904 Some((idx, _count)) => {
905 if !nth_matches(*a, *b, (idx + 1) as i64) {
906 return false;
907 }
908 }
909 None => return false,
910 },
911 None => match sibling_index {
912 Some(idx) => {
913 if !nth_matches(*a, *b, (idx + 1) as i64) {
914 return false;
915 }
916 }
917 None => return false,
918 },
919 },
920 PseudoClass::NthLastChild(a, b, filter) => match filter {
921 Some(sel) => match filtered_sibling_position(own_siblings, node_ptr, sel) {
922 Some((idx, count)) => {
923 if !nth_matches(*a, *b, (count - idx) as i64) {
924 return false;
925 }
926 }
927 None => return false,
928 },
929 None => match (sibling_index, sibling_count) {
930 (Some(idx), Some(count)) => {
931 if !nth_matches(*a, *b, (count - idx) as i64) {
932 return false;
933 }
934 }
935 _ => return false,
936 },
937 },
938 PseudoClass::Checked => {
939 if !attributes.contains_key("checked") {
940 return false;
941 }
942 }
943 PseudoClass::Disabled => {
955 if !is_disabled {
956 return false;
957 }
958 }
959 PseudoClass::FirstOfType => {
960 if type_index != Some(0) {
961 return false;
962 }
963 }
964 PseudoClass::LastOfType => match (type_index, type_count) {
965 (Some(idx), Some(count)) if idx + 1 == count => {}
966 _ => return false,
967 },
968 PseudoClass::NthOfType(a, b) => match type_index {
969 Some(idx) => {
970 if !nth_matches(*a, *b, (idx + 1) as i64) {
971 return false;
972 }
973 }
974 None => return false,
975 },
976 PseudoClass::NthLastOfType(a, b) => match (type_index, type_count) {
977 (Some(idx), Some(count)) => {
978 if !nth_matches(*a, *b, (count - idx) as i64) {
979 return false;
980 }
981 }
982 _ => return false,
983 },
984 PseudoClass::OnlyOfType => match (type_index, type_count) {
985 (Some(0), Some(1)) => {}
986 _ => return false,
987 },
988 PseudoClass::OnlyChild => match (sibling_index, sibling_count) {
989 (Some(0), Some(1)) => {}
990 _ => return false,
991 },
992 PseudoClass::Target => {
993 let current = super::super::layout::CURRENT_TARGET_ID.lock();
994 if current.is_empty() || id.as_deref() != Some(current.as_str()) {
995 return false;
996 }
997 }
998 PseudoClass::Empty => {
999 if !is_empty {
1000 return false;
1001 }
1002 }
1003 PseudoClass::Not(inner) => {
1004 if matches_simple_selector(
1005 tag_name,
1006 id,
1007 classes,
1008 attributes,
1009 inner,
1010 sibling_index,
1011 sibling_count,
1012 active_pseudo_states,
1013 type_index,
1014 type_count,
1015 is_empty,
1016 node_ptr,
1017 node_children,
1018 own_siblings,
1019 is_disabled,
1020 ) {
1021 return false;
1022 }
1023 }
1024 PseudoClass::Is(list) | PseudoClass::Where(list) => {
1025 let any_match = list.iter().any(|inner| {
1026 matches_simple_selector(
1027 tag_name,
1028 id,
1029 classes,
1030 attributes,
1031 inner,
1032 sibling_index,
1033 sibling_count,
1034 active_pseudo_states,
1035 type_index,
1036 type_count,
1037 is_empty,
1038 node_ptr,
1039 node_children,
1040 own_siblings,
1041 is_disabled,
1042 )
1043 });
1044 if !any_match {
1045 return false;
1046 }
1047 }
1048 PseudoClass::Has(list) => {
1049 let (sibling_list, descendant_list): (
1050 Vec<&(HasCombinator, SimpleSelector)>,
1051 Vec<&(HasCombinator, SimpleSelector)>,
1052 ) = list.iter().partition(|(comb, _)| {
1053 matches!(comb, HasCombinator::NextSibling | HasCombinator::SubsequentSibling)
1054 });
1055 let sibling_ok = !sibling_list.is_empty()
1056 && own_siblings.zip(sibling_index).is_some_and(|(sibs, my_idx)| {
1057 sibling_list.iter().any(|(comb, sel)| {
1058 has_sibling_match(sibs, my_idx, *comb == HasCombinator::NextSibling, sel)
1059 })
1060 });
1061 let descendant_ok = !descendant_list.is_empty()
1062 && {
1063 let owned: Vec<(bool, SimpleSelector)> = descendant_list
1064 .iter()
1065 .map(|(comb, sel)| (*comb == HasCombinator::Child, sel.clone()))
1066 .collect();
1067 has_descendant_match(node_children, &owned)
1068 };
1069 if !sibling_ok && !descendant_ok {
1070 return false;
1071 }
1072 }
1073 PseudoClass::Dir(target) => {
1074 let effective = attributes
1075 .get("dir")
1076 .map(|v| v.trim().to_lowercase())
1077 .filter(|v| v == "ltr" || v == "rtl")
1078 .unwrap_or_else(|| String::from("ltr"));
1079 if effective != *target {
1080 return false;
1081 }
1082 }
1083 PseudoClass::Lang(target) => {
1084 let own_lang = attributes.get("lang").map(|v| v.to_lowercase());
1085 let effective = match own_lang {
1086 Some(l) if !l.is_empty() => l,
1087 _ => super::super::layout::DOCUMENT_LANG.lock().clone(),
1088 };
1089 let matched = !effective.is_empty()
1090 && (effective == *target
1091 || effective.starts_with(&alloc::format!("{}-", target)));
1092 if !matched {
1093 return false;
1094 }
1095 }
1096 PseudoClass::Other(p) => match p.as_str() {
1097 "checked" => {
1100 if !attributes.contains_key("checked") {
1101 return false;
1102 }
1103 }
1104 "disabled" => {
1105 if !is_disabled {
1106 return false;
1107 }
1108 }
1109 "enabled" => {
1113 if is_disabled {
1114 return false;
1115 }
1116 }
1117 "required" => {
1118 if !attributes.contains_key("required") {
1119 return false;
1120 }
1121 }
1122 "optional" => {
1124 if attributes.contains_key("required") {
1125 return false;
1126 }
1127 }
1128 "valid" | "invalid" | "user-valid" | "user-invalid" => {
1151 let value = attributes.get("value").map(|s| s.trim()).unwrap_or("");
1152 let ty = attributes.get("type").map(|s| s.as_str()).unwrap_or("");
1153 let required_empty = if !attributes.contains_key("required") {
1154 false
1155 } else if tag_name == "input" && ty == "checkbox" {
1156 !attributes.contains_key("checked")
1157 } else if tag_name == "input" && ty == "radio" {
1158 let group_checked = attributes.get("name").is_some_and(|nm| {
1159 own_siblings.is_some_and(|sibs| {
1160 sibs.iter().any(|sib| {
1161 if let NodeType::Element {
1162 tag_name: st,
1163 attributes: sa,
1164 ..
1165 } = &sib.node_type
1166 {
1167 st == "input"
1168 && sa.get("type").map(|t| t == "radio").unwrap_or(false)
1169 && sa.get("name") == Some(nm)
1170 && sa.contains_key("checked")
1171 } else {
1172 false
1173 }
1174 })
1175 })
1176 });
1177 !(attributes.contains_key("checked") || group_checked)
1178 } else {
1179 value.is_empty()
1180 };
1181 let min = attributes.get("min").and_then(|v| v.parse::<f64>().ok());
1182 let max = attributes.get("max").and_then(|v| v.parse::<f64>().ok());
1183 let out_of_range = if min.is_some() || max.is_some() {
1184 value.parse::<f64>().ok().map(|v| {
1185 !(min.is_none_or(|m| v >= m) && max.is_none_or(|m| v <= m))
1186 })
1187 } else {
1188 None
1189 }
1190 .unwrap_or(false);
1191 let is_invalid = required_empty || out_of_range;
1192 let is_user_variant = p.as_str().starts_with("user-");
1193 if is_user_variant && !attributes.contains_key("_user_interacted") {
1194 return false;
1195 }
1196 let want_valid = p.as_str() == "valid" || p.as_str() == "user-valid";
1197 if is_invalid == want_valid {
1198 return false;
1199 }
1200 }
1201 "readonly" | "read-only" => {
1202 if !attributes.contains_key("readonly") {
1203 return false;
1204 }
1205 }
1206 "read-write" => {
1210 if attributes.contains_key("readonly") {
1211 return false;
1212 }
1213 }
1214 "indeterminate" => {
1221 let is_indeterminate_progress =
1222 tag_name == "progress" && !attributes.contains_key("value");
1223 let matched = attributes.contains_key("indeterminate")
1224 || attributes.contains_key("_indeterminate")
1225 || is_indeterminate_progress;
1226 if !matched {
1227 return false;
1228 }
1229 }
1230 "focus-visible" => {
1233 if !active_pseudo_states.contains(&"focus") {
1234 return false;
1235 }
1236 }
1237 "focus-within" => {
1241 if !super::super::layout::FOCUS_WITHIN_PTRS.lock().contains(&node_ptr) {
1242 return false;
1243 }
1244 }
1245 "link" => {
1250 if !(tag_name == "a" || tag_name == "area") || !attributes.contains_key("href")
1251 {
1252 return false;
1253 }
1254 }
1255 "visited" => {
1256 return false;
1257 }
1258 "defined" => {}
1266 "fullscreen" => {
1270 if !attributes.contains_key("_fullscreen") {
1271 return false;
1272 }
1273 }
1274 "modal" => {
1280 if !attributes.contains_key("_fullscreen") && !attributes.contains_key("_modal")
1281 {
1282 return false;
1283 }
1284 }
1285 "popover-open" => {
1292 if !attributes.contains_key("_popover_open") {
1293 return false;
1294 }
1295 }
1296 "root" => {
1298 if tag_name != "html" {
1299 return false;
1300 }
1301 }
1302 "placeholder-shown" => {
1307 let has_placeholder = attributes
1308 .get("placeholder")
1309 .map(|p| !p.is_empty())
1310 .unwrap_or(false);
1311 let value_empty = attributes
1312 .get("value")
1313 .map(|v| v.is_empty())
1314 .unwrap_or(true);
1315 if !has_placeholder || !value_empty {
1316 return false;
1317 }
1318 }
1319 "in-range" | "out-of-range" => {
1325 let min = attributes.get("min").and_then(|v| v.parse::<f64>().ok());
1326 let max = attributes.get("max").and_then(|v| v.parse::<f64>().ok());
1327 if min.is_none() && max.is_none() {
1328 return false;
1329 }
1330 let value = attributes.get("value").and_then(|v| v.parse::<f64>().ok());
1331 let in_range = match value {
1332 Some(v) => min.is_none_or(|m| v >= m) && max.is_none_or(|m| v <= m),
1333 None => true,
1335 };
1336 let want_in_range = p.as_str() == "in-range";
1337 if in_range != want_in_range {
1338 return false;
1339 }
1340 }
1341 "default" => {
1348 let is_checkable = attributes
1349 .get("type")
1350 .map(|t| t == "checkbox" || t == "radio")
1351 .unwrap_or(false);
1352 let matched = if tag_name == "option" {
1353 attributes.contains_key("selected")
1354 } else if tag_name == "input" && is_checkable {
1355 attributes.contains_key("checked")
1356 } else {
1357 false
1358 };
1359 if !matched {
1360 return false;
1361 }
1362 }
1363 _ => {
1364 if !active_pseudo_states.contains(&p.as_str()) {
1365 return false;
1366 }
1367 }
1368 },
1369 }
1370 }
1371 true
1372}
1373
1374pub(crate) fn type_sibling_position(
1379 node: &Node,
1380 ancestors: &[AncestorContext<'_>],
1381 sibling_index: Option<usize>,
1382) -> (Option<usize>, Option<usize>) {
1383 let NodeType::Element { tag_name, .. } = &node.node_type else {
1384 return (None, None);
1385 };
1386 let Some(parent_ctx) = ancestors.last() else {
1387 return (Some(0), Some(1));
1388 };
1389 let Some(my_idx) = sibling_index else {
1390 return (None, None);
1391 };
1392 let mut type_idx = None;
1393 let mut type_count = 0usize;
1394 for (i, child) in parent_ctx.node.children.iter().enumerate() {
1395 if let NodeType::Element { tag_name: t, .. } = &child.node_type {
1396 if t == tag_name {
1397 if i == my_idx {
1398 type_idx = Some(type_count);
1399 }
1400 type_count += 1;
1401 }
1402 }
1403 }
1404 (type_idx, Some(type_count))
1405}
1406
1407pub(crate) fn nth_matches(a: i64, b: i64, position: i64) -> bool {
1409 if a == 0 {
1410 return position == b;
1411 }
1412 let diff = position - b;
1413 diff % a == 0 && diff / a >= 0
1414}
1415
1416pub(crate) fn attr_matches(attr: &AttrSelector, attributes: &BTreeMap<String, String>) -> bool {
1418 let val = attributes.get(&attr.name).or_else(|| {
1420 attributes
1421 .iter()
1422 .find(|(k, _)| k.to_lowercase() == attr.name)
1423 .map(|(_, v)| v)
1424 });
1425 let val = match val {
1426 Some(v) => v,
1427 None => return false, };
1429 let (val_cmp, need_cmp): (String, String) = if attr.case_insensitive {
1432 (val.to_lowercase(), attr.value.to_lowercase())
1433 } else {
1434 (val.clone(), attr.value.clone())
1435 };
1436 let val = &val_cmp;
1437 let attr_value = &need_cmp;
1438 match attr.op {
1439 AttrOp::Exists => true,
1440 AttrOp::Eq => val == attr_value,
1441 AttrOp::Prefix => !attr_value.is_empty() && val.starts_with(attr_value.as_str()),
1442 AttrOp::Suffix => !attr_value.is_empty() && val.ends_with(attr_value.as_str()),
1443 AttrOp::Contains => !attr_value.is_empty() && val.contains(attr_value.as_str()),
1444 AttrOp::Word => val.split_whitespace().any(|w| w == attr_value),
1445 AttrOp::Dash => {
1446 val == attr_value || val.starts_with(&alloc::format!("{}-", attr_value))
1447 }
1448 }
1449}
1450
1451pub(crate) struct NodeMatchContext<'a> {
1459 pub is_disabled: bool,
1460 pub type_index: Option<usize>,
1461 pub type_count: Option<usize>,
1462 pub own_siblings: Option<&'a [Node]>,
1463}
1464
1465impl<'a> NodeMatchContext<'a> {
1466 pub fn new(
1467 current: &Node,
1468 ancestors: &'a [AncestorContext<'a>],
1469 sibling_index: Option<usize>,
1470 ) -> Self {
1471 let is_disabled = is_node_disabled(current, ancestors);
1472 let (type_index, type_count) = type_sibling_position(current, ancestors, sibling_index);
1474 let own_siblings = ancestors.last().map(|a| a.node.children.as_slice());
1476 Self {
1477 is_disabled,
1478 type_index,
1479 type_count,
1480 own_siblings,
1481 }
1482 }
1483}
1484
1485pub(crate) fn matches_selector(
1486 selector: &Selector,
1487 current: &Node,
1488 ancestors: &[AncestorContext<'_>],
1489 sibling_index: Option<usize>,
1490 sibling_count: Option<usize>,
1491 active_pseudo_states: &[&str],
1492 ctx: &NodeMatchContext<'_>,
1493) -> bool {
1494 let is_disabled = ctx.is_disabled;
1495 let (type_index, type_count) = (ctx.type_index, ctx.type_count);
1496 let own_siblings = ctx.own_siblings;
1497 match selector {
1498 Selector::Simple(simple) => matches_selector_step_node(
1499 current,
1500 simple,
1501 sibling_index,
1502 sibling_count,
1503 active_pseudo_states,
1504 type_index,
1505 type_count,
1506 own_siblings,
1507 is_disabled,
1508 ),
1509 Selector::Chain(steps) => matches_selector_chain(
1510 steps,
1511 current,
1512 ancestors,
1513 sibling_index,
1514 sibling_count,
1515 active_pseudo_states,
1516 type_index,
1517 type_count,
1518 own_siblings,
1519 is_disabled,
1520 ),
1521 }
1522}
1523
1524#[allow(clippy::too_many_arguments)]
1525pub(crate) fn matches_selector_step_node(
1526 node: &Node,
1527 simple: &SimpleSelector,
1528 sibling_index: Option<usize>,
1529 sibling_count: Option<usize>,
1530 active_pseudo_states: &[&str],
1531 type_index: Option<usize>,
1532 type_count: Option<usize>,
1533 own_siblings: Option<&[Node]>,
1534 is_disabled: bool,
1535) -> bool {
1536 match &node.node_type {
1537 NodeType::Element {
1538 tag_name,
1539 classes,
1540 id,
1541 attributes,
1542 } => matches_simple_selector(
1543 tag_name,
1544 id,
1545 classes,
1546 attributes,
1547 simple,
1548 sibling_index,
1549 sibling_count,
1550 active_pseudo_states,
1551 type_index,
1552 type_count,
1553 node.children.is_empty(),
1554 node as *const Node as usize,
1555 &node.children,
1556 own_siblings,
1557 is_disabled,
1558 ),
1559 NodeType::Text(_) => false,
1560 }
1561}
1562
1563pub(crate) fn has_descendant_match(children: &[Node], list: &[(bool, SimpleSelector)]) -> bool {
1571 for child in children {
1572 if let NodeType::Element {
1573 tag_name,
1574 id,
1575 classes,
1576 attributes,
1577 } = &child.node_type
1578 {
1579 for (_, sel) in list {
1580 let is_disabled = attributes.contains_key("disabled");
1581 if matches_simple_selector(
1582 tag_name,
1583 id,
1584 classes,
1585 attributes,
1586 sel,
1587 None,
1588 None,
1589 &[],
1590 None,
1591 None,
1592 child.children.is_empty(),
1593 child as *const Node as usize,
1594 &child.children,
1595 None,
1596 is_disabled,
1597 ) {
1598 return true;
1599 }
1600 }
1601 }
1602 let deeper_list: Vec<(bool, SimpleSelector)> = list
1604 .iter()
1605 .filter(|(direct_only, _)| !direct_only)
1606 .cloned()
1607 .collect();
1608 if !deeper_list.is_empty() && has_descendant_match(&child.children, &deeper_list) {
1609 return true;
1610 }
1611 }
1612 false
1613}
1614
1615pub(crate) fn has_sibling_match(siblings: &[Node], my_idx: usize, adjacent: bool, sel: &SimpleSelector) -> bool {
1619 for sib in siblings.iter().skip(my_idx + 1) {
1620 if let NodeType::Element {
1621 tag_name,
1622 id,
1623 classes,
1624 attributes,
1625 } = &sib.node_type
1626 {
1627 let is_disabled = attributes.contains_key("disabled");
1628 if matches_simple_selector(
1629 tag_name,
1630 id,
1631 classes,
1632 attributes,
1633 sel,
1634 None,
1635 None,
1636 &[],
1637 None,
1638 None,
1639 sib.children.is_empty(),
1640 sib as *const Node as usize,
1641 &sib.children,
1642 None,
1643 is_disabled,
1644 ) {
1645 return true;
1646 }
1647 if adjacent {
1648 return false;
1649 }
1650 }
1651 }
1652 false
1653}
1654
1655pub(crate) fn filtered_sibling_position(
1659 own_siblings: Option<&[Node]>,
1660 node_ptr: usize,
1661 sel: &SimpleSelector,
1662) -> Option<(usize, usize)> {
1663 let siblings = own_siblings?;
1664 let mut my_pos = None;
1665 let mut count = 0usize;
1666 for sib in siblings {
1667 if let NodeType::Element {
1668 tag_name,
1669 id,
1670 classes,
1671 attributes,
1672 } = &sib.node_type
1673 {
1674 let is_disabled = attributes.contains_key("disabled");
1675 let is_match = matches_simple_selector(
1676 tag_name,
1677 id,
1678 classes,
1679 attributes,
1680 sel,
1681 None,
1682 None,
1683 &[],
1684 None,
1685 None,
1686 sib.children.is_empty(),
1687 sib as *const Node as usize,
1688 &sib.children,
1689 None,
1690 is_disabled,
1691 );
1692 if is_match {
1693 if sib as *const Node as usize == node_ptr {
1694 my_pos = Some(count);
1695 }
1696 count += 1;
1697 }
1698 }
1699 }
1700 my_pos.map(|idx| (idx, count))
1701}
1702
1703#[allow(clippy::too_many_arguments)]
1704pub(crate) fn matches_selector_chain(
1705 steps: &[SelectorStep],
1706 current: &Node,
1707 ancestors: &[AncestorContext<'_>],
1708 sibling_index: Option<usize>,
1709 sibling_count: Option<usize>,
1710 active_pseudo_states: &[&str],
1711 type_index: Option<usize>,
1712 type_count: Option<usize>,
1713 own_siblings: Option<&[Node]>,
1714 is_disabled: bool,
1715) -> bool {
1716 if steps.is_empty() {
1717 return false;
1718 }
1719 let mut idx = steps.len() - 1;
1720 if !matches_selector_step_node(
1721 current,
1722 &steps[idx].simple,
1723 sibling_index,
1724 sibling_count,
1725 active_pseudo_states,
1726 type_index,
1727 type_count,
1728 own_siblings,
1729 is_disabled,
1730 ) {
1731 return false;
1732 }
1733
1734 let mut anc_pos = ancestors.len();
1735 let mut cur_sib_index = sibling_index;
1737 while idx > 0 {
1738 let comb = steps[idx].combinator.unwrap_or(Combinator::Descendant);
1739 idx -= 1;
1740 match comb {
1741 Combinator::Child => {
1742 if anc_pos == 0 {
1743 return false;
1744 }
1745 anc_pos -= 1;
1746 let anc = ancestors[anc_pos];
1747 let anc_disabled = is_node_disabled(anc.node, &ancestors[0..anc_pos]);
1748 if !matches_selector_step_node(
1751 anc.node,
1752 &steps[idx].simple,
1753 anc.sibling_index,
1754 anc.sibling_count,
1755 active_pseudo_states,
1756 None,
1757 None,
1758 None,
1759 anc_disabled,
1760 ) {
1761 return false;
1762 }
1763 cur_sib_index = anc.sibling_index;
1764 }
1765 Combinator::Descendant => {
1766 let mut found = false;
1767 while anc_pos > 0 {
1768 anc_pos -= 1;
1769 let anc = ancestors[anc_pos];
1770 let anc_disabled = is_node_disabled(anc.node, &ancestors[0..anc_pos]);
1771 if matches_selector_step_node(
1772 anc.node,
1773 &steps[idx].simple,
1774 anc.sibling_index,
1775 anc.sibling_count,
1776 active_pseudo_states,
1777 None,
1778 None,
1779 None,
1780 anc_disabled,
1781 ) {
1782 found = true;
1783 cur_sib_index = anc.sibling_index;
1784 break;
1785 }
1786 }
1787 if !found {
1788 return false;
1789 }
1790 }
1791 Combinator::NextSibling | Combinator::SubsequentSibling => {
1792 if anc_pos == 0 {
1794 return false;
1795 }
1796 let parent = ancestors[anc_pos - 1].node;
1797 let ci = match cur_sib_index {
1798 Some(i) => i,
1799 None => return false,
1800 };
1801 let count = Some(parent.children.len());
1802 let adjacent = comb == Combinator::NextSibling;
1803 let mut found = false;
1804 let mut j = ci;
1805 while j > 0 {
1806 j -= 1;
1807 if matches!(parent.children[j].node_type, NodeType::Element { .. }) {
1808 let sib_disabled = is_node_disabled(&parent.children[j], &ancestors[0..anc_pos - 1]);
1809 let m = matches_selector_step_node(
1810 &parent.children[j],
1811 &steps[idx].simple,
1812 Some(j),
1813 count,
1814 active_pseudo_states,
1815 None,
1816 None,
1817 None,
1818 sib_disabled,
1819 );
1820 if m {
1821 found = true;
1822 cur_sib_index = Some(j);
1823 break;
1824 }
1825 if adjacent {
1826 break;
1828 }
1829 }
1830 }
1831 if !found {
1832 return false;
1833 }
1834 }
1835 }
1836 }
1837 true
1838}
1839