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 "selection" => selector.pseudo_element = Some(PseudoElement::Selection),
113 _ => {
114 let pseudo = match name.as_str() {
115 "first-child" => PseudoClass::FirstChild,
116 "last-child" => PseudoClass::LastChild,
117 "nth-child" => {
118 let (a, b, sel) = parse_nth_of(&arg);
119 PseudoClass::NthChild(a, b, sel.map(alloc::boxed::Box::new))
120 }
121 "nth-last-child" => {
122 let (a, b, sel) = parse_nth_of(&arg);
123 PseudoClass::NthLastChild(
124 a,
125 b,
126 sel.map(alloc::boxed::Box::new),
127 )
128 }
129 "not" => match parse_simple_selector(arg.trim()) {
130 Some(inner) => PseudoClass::Not(alloc::boxed::Box::new(inner)),
131 None => PseudoClass::Other(name),
132 },
133 "is" | "where" | "matches" | "any" => {
134 let is_where = name == "where";
135 let list: Vec<SimpleSelector> = split_top_level_commas(&arg)
136 .iter()
137 .filter_map(|s| parse_simple_selector(s.trim()))
138 .collect();
139 if list.is_empty() {
140 PseudoClass::Other(name)
141 } else if is_where {
142 PseudoClass::Where(list)
143 } else {
144 PseudoClass::Is(list)
145 }
146 }
147 "has" => {
148 let list: Vec<(HasCombinator, SimpleSelector)> =
149 split_top_level_commas(&arg)
150 .iter()
151 .filter_map(|s| {
152 let s = s.trim();
153 let (comb, rest) = match s.strip_prefix('>') {
154 Some(r) => (HasCombinator::Child, r.trim()),
155 None => match s.strip_prefix('+') {
156 Some(r) => {
157 (HasCombinator::NextSibling, r.trim())
158 }
159 None => match s.strip_prefix('~') {
160 Some(r) => (
161 HasCombinator::SubsequentSibling,
162 r.trim(),
163 ),
164 None => {
165 (HasCombinator::Descendant, s)
166 }
167 },
168 },
169 };
170 parse_simple_selector(rest).map(|sel| (comb, sel))
171 })
172 .collect();
173 if list.is_empty() {
174 PseudoClass::Other(name)
175 } else {
176 PseudoClass::Has(list)
177 }
178 }
179 "checked" => PseudoClass::Checked,
180 "disabled" => PseudoClass::Disabled,
181 "lang" => {
182 let code = arg
183 .trim()
184 .trim_matches(|c| c == '\'' || c == '"')
185 .to_ascii_lowercase();
186 if code.is_empty() {
187 PseudoClass::Other(name)
188 } else {
189 PseudoClass::Lang(code)
190 }
191 }
192 "dir" => {
193 let d = arg.trim().to_ascii_lowercase();
194 if d == "ltr" || d == "rtl" {
195 PseudoClass::Dir(d)
196 } else {
197 PseudoClass::Other(name)
198 }
199 }
200 "first-of-type" => PseudoClass::FirstOfType,
201 "last-of-type" => PseudoClass::LastOfType,
202 "nth-of-type" => {
203 let (a, b) = parse_nth(&arg);
204 PseudoClass::NthOfType(a, b)
205 }
206 "nth-last-of-type" => {
207 let (a, b) = parse_nth(&arg);
208 PseudoClass::NthLastOfType(a, b)
209 }
210 "only-of-type" => PseudoClass::OnlyOfType,
211 "only-child" => PseudoClass::OnlyChild,
212 "target" => PseudoClass::Target,
213 "empty" => PseudoClass::Empty,
214 _ => PseudoClass::Other(name),
215 };
216 selector.pseudo_classes.push(pseudo);
217 }
218 }
219 }
220 }
221 b if is_ident_byte(b) => {
222 let start = i;
223 while i < bytes.len() && is_ident_byte(bytes[i]) {
224 i += 1;
225 }
226 if start < i {
227 if let Some(tag_str) = s.get(start..i) {
228 selector.tag_name = Some(tag_str.to_ascii_lowercase());
229 }
230 }
231 }
232 _ => i += 1,
233 }
234 }
235
236 if !selector.universal
237 && selector.tag_name.is_none()
238 && selector.id.is_none()
239 && selector.class.is_empty()
240 && selector.pseudo_classes.is_empty()
241 && selector.attrs.is_empty()
242 && selector.pseudo_element.is_none()
243 {
244 None
245 } else {
246 Some(selector)
247 }
248}
249
250pub(crate) fn parse_attr_selector(inner: &str) -> Option<AttrSelector> {
252 let inner = inner.trim();
253 if inner.is_empty() {
254 return None;
255 }
256 for (token, op) in &[
258 ("^=", AttrOp::Prefix),
259 ("$=", AttrOp::Suffix),
260 ("*=", AttrOp::Contains),
261 ("~=", AttrOp::Word),
262 ("|=", AttrOp::Dash),
263 ("=", AttrOp::Eq),
264 ] {
265 if let Some(pos) = inner.find(token) {
266 let name = inner.get(..pos).unwrap_or("").trim().to_lowercase();
267 let mut raw = inner.get(pos + token.len()..).unwrap_or("").trim();
268 let mut case_insensitive = false;
273 for flag in [" i", " I", " s", " S"] {
274 if let Some(stripped) = raw.strip_suffix(flag) {
275 case_insensitive = flag.eq_ignore_ascii_case(" i");
276 raw = stripped.trim_end();
277 break;
278 }
279 }
280 let value = raw.trim_matches('"').trim_matches('\'').to_string();
281 if name.is_empty() {
282 return None;
283 }
284 return Some(AttrSelector {
285 name,
286 op: op.clone(),
287 value,
288 case_insensitive,
289 });
290 }
291 }
292 Some(AttrSelector {
293 name: inner.to_lowercase(),
294 op: AttrOp::Exists,
295 value: String::new(),
296 case_insensitive: false,
297 })
298}
299
300pub(crate) fn parse_nth_of(arg: &str) -> (i64, i64, Option<SimpleSelector>) {
306 let full = arg.trim();
307 let lower = full.to_lowercase();
308 match lower.find(" of ") {
309 Some(pos) => {
310 let an_b_part = full.get(..pos).unwrap_or(full);
311 let sel_part = full.get(pos + 4..).unwrap_or("");
312 let (a, b) = parse_nth(an_b_part);
313 (a, b, parse_simple_selector(sel_part.trim()))
314 }
315 None => {
316 let (a, b) = parse_nth(full);
317 (a, b, None)
318 }
319 }
320}
321
322pub(crate) fn parse_nth(arg: &str) -> (i64, i64) {
324 let full = arg.trim().to_lowercase();
325 let s = full.split(" of ").next().unwrap_or(&full).trim();
330 match s {
331 "odd" => return (2, 1),
332 "even" => return (2, 0),
333 _ => {}
334 }
335 if let Some(npos) = s.find('n') {
336 let a_str = s.get(..npos).unwrap_or("").trim();
338 let a: i64 = match a_str {
339 "" | "+" => 1,
340 "-" => -1,
341 _ => a_str.parse().unwrap_or(1),
342 };
343 let b_str: String = s
345 .get(npos + 1..)
346 .unwrap_or("")
347 .chars()
348 .filter(|c| !c.is_whitespace())
349 .collect();
350 let b: i64 = if b_str.is_empty() {
351 0
352 } else {
353 b_str.parse().unwrap_or(0)
354 };
355 (a, b)
356 } else {
357 (0, s.parse().unwrap_or(0))
359 }
360}
361
362pub(crate) fn parse_selector_chain(s: &str) -> Option<Vec<SelectorStep>> {
363 if s.is_empty() {
364 return None;
365 }
366
367 let mut tokens: Vec<(Option<Combinator>, String)> = Vec::new();
368 let chars: Vec<char> = s.chars().collect();
369 let mut i = 0usize;
370 let mut current = String::new();
371 let mut pending_combinator: Option<Combinator> = None;
372 let mut depth = 0i32; while i < chars.len() {
375 let c = chars[i];
376 if c == '[' || c == '(' {
377 depth += 1;
378 current.push(c);
379 i += 1;
380 continue;
381 }
382 if c == ']' || c == ')' {
383 depth -= 1;
384 current.push(c);
385 i += 1;
386 continue;
387 }
388 if depth > 0 {
389 current.push(c);
390 i += 1;
391 continue;
392 }
393 if c == '>' || c == '+' || c == '~' {
394 if !current.trim().is_empty() {
395 tokens.push((pending_combinator, current.trim().to_string()));
396 current.clear();
397 }
398 pending_combinator = Some(match c {
399 '>' => Combinator::Child,
400 '+' => Combinator::NextSibling,
401 _ => Combinator::SubsequentSibling,
402 });
403 i += 1;
404 while i < chars.len() && chars[i].is_ascii_whitespace() {
405 i += 1;
406 }
407 continue;
408 }
409 if c.is_ascii_whitespace() {
410 if !current.trim().is_empty() {
411 tokens.push((pending_combinator, current.trim().to_string()));
412 current.clear();
413 pending_combinator = Some(Combinator::Descendant);
414 }
415 i += 1;
416 while i < chars.len() && chars[i].is_ascii_whitespace() {
417 i += 1;
418 }
419 continue;
420 }
421 current.push(c);
422 i += 1;
423 }
424 if !current.trim().is_empty() {
425 tokens.push((pending_combinator, current.trim().to_string()));
426 }
427 if tokens.is_empty() {
428 return None;
429 }
430
431 let mut steps = Vec::new();
432 for (idx, (comb, tok)) in tokens.into_iter().enumerate() {
433 let simple = parse_simple_selector(&tok)?;
434 let combinator = if idx == 0 { None } else { comb };
435 steps.push(SelectorStep { simple, combinator });
436 }
437 Some(steps)
438}
439
440pub(crate) fn is_selector_ident_char(c: char) -> bool {
441 c.is_ascii_alphanumeric() || c == '-' || c == '_'
442}
443
444pub(crate) fn simple_specificity(sel: &SimpleSelector) -> (u32, u32, u32) {
449 let mut spec = (
450 sel.id.as_ref().map_or(0u32, |_| 1),
451 sel.class.len() as u32 + sel.attrs.len() as u32,
452 sel.tag_name.as_ref().map_or(0u32, |_| 1),
453 );
454 for pc in &sel.pseudo_classes {
455 let add = match pc {
456 PseudoClass::Not(inner) => simple_specificity(inner.as_ref()),
457 PseudoClass::Is(list) => list.iter().map(simple_specificity).max().unwrap_or((0, 0, 0)),
458 PseudoClass::Has(list) => list
459 .iter()
460 .map(|(_, sel)| simple_specificity(sel))
461 .max()
462 .unwrap_or((0, 0, 0)),
463 PseudoClass::Where(_) => (0, 0, 0),
464 _ => (0, 1, 0),
465 };
466 spec.0 += add.0;
467 spec.1 += add.1;
468 spec.2 += add.2;
469 }
470 if sel.pseudo_element.is_some() {
474 spec.2 += 1;
475 }
476 spec
477}
478
479pub(crate) fn parse_quotes_pairs(v: &str) -> Option<alloc::vec::Vec<(String, String)>> {
493 let t = v.trim();
494 if t.eq_ignore_ascii_case("none") {
495 return Some(alloc::vec::Vec::new());
496 }
497 let mut lits: alloc::vec::Vec<String> = alloc::vec::Vec::new();
498 let bytes = t.as_bytes();
499 let mut i = 0usize;
500 while i < bytes.len() {
501 if bytes[i] == b'"' || bytes[i] == b'\'' {
502 let quote = bytes[i];
503 let start = i + 1;
504 let mut j = start;
505 while j < bytes.len() && bytes[j] != quote {
506 j += 1;
507 }
508 if let Some(sub) = t.get(start..j) {
509 lits.push(String::from(sub));
510 }
511 i = j + 1;
512 } else {
513 i += 1;
514 }
515 }
516 let mut pairs: alloc::vec::Vec<(String, String)> = alloc::vec::Vec::new();
517 let mut it = lits.into_iter();
518 while let (Some(a), Some(b)) = (it.next(), it.next()) {
519 pairs.push((a, b));
520 }
521 if pairs.is_empty() {
522 None
523 } else {
524 Some(pairs)
525 }
526}
527
528pub(crate) fn unescape_css_string(s: &str) -> String {
535 let mut out = String::with_capacity(s.len());
536 let mut chars = s.chars().peekable();
537 while let Some(c) = chars.next() {
538 if c != '\\' {
539 out.push(c);
540 continue;
541 }
542 let Some(&next) = chars.peek() else {
543 break;
544 };
545 if next.is_ascii_hexdigit() {
546 let mut hex = String::with_capacity(6);
547 while hex.len() < 6 {
548 match chars.peek() {
549 Some(&h) if h.is_ascii_hexdigit() => {
550 hex.push(h);
551 chars.next();
552 }
553 _ => break,
554 }
555 }
556 if let Ok(code) = u32::from_str_radix(&hex, 16) {
557 if let Some(ch) = char::from_u32(code) {
558 out.push(ch);
559 }
560 }
561 if let Some(&w) = chars.peek() {
563 if w.is_ascii_whitespace() {
564 chars.next();
565 }
566 }
567 } else {
568 out.push(next);
569 chars.next();
570 }
571 }
572 out
573}
574
575pub(crate) fn parse_content_value(
576 v: &str,
577 attributes: &BTreeMap<String, String>,
578 counters: &BTreeMap<String, i64>,
579 quotes_pairs: Option<&[(String, String)]>,
580 quote_depth: usize,
581 counter_styles: &[CounterStyleDef],
582) -> String {
583 parse_content_value_with_stack(
584 v,
585 attributes,
586 counters,
587 None,
588 quotes_pairs,
589 quote_depth,
590 counter_styles,
591 )
592}
593
594pub(crate) fn parse_content_value_with_stack(
595 v: &str,
596 attributes: &BTreeMap<String, String>,
597 counters: &BTreeMap<String, i64>,
598 counter_scope: Option<&crate::os_lib::css::counter_scope::CounterScopeStack>,
599 quotes_pairs: Option<&[(String, String)]>,
600 quote_depth: usize,
601 counter_styles: &[CounterStyleDef],
602) -> String {
603 let t = v.trim();
604 let mut out = String::new();
605 let mut i = 0usize;
606 let bytes = t.as_bytes();
607 while i < bytes.len() {
608 if bytes[i].is_ascii_whitespace() {
610 i += 1;
611 continue;
612 }
613 if bytes[i] == b'"' || bytes[i] == b'\'' {
614 let quote = bytes[i];
615 let start = i + 1;
616 let mut j = start;
617 while j < bytes.len() && bytes[j] != quote {
618 j += 1;
619 }
620 if let Some(sub) = t.get(start..j) {
621 out.push_str(&unescape_css_string(sub));
622 }
623 i = j + 1;
624 continue;
625 }
626 if let Some(rest) = t.get(i..).and_then(|r| r.strip_prefix("attr(")) {
627 if let Some(close) = rest.find(')') {
628 if let Some(inner) = rest.get(..close) {
629 let mut parts = inner.splitn(2, ',');
635 let name = parts.next().unwrap_or("").trim();
636 let fallback = parts.next().map(|s| {
637 let s = s.trim();
638 s.trim_matches('"').trim_matches('\'')
639 });
640 match attributes.get(name) {
641 Some(val) => out.push_str(val),
642 None => {
643 if let Some(fb) = fallback {
644 out.push_str(fb);
645 }
646 }
647 }
648 }
649 i += "attr(".len() + close + 1;
650 continue;
651 }
652 }
653 if let Some(rest) = t.get(i..).and_then(|r| r.strip_prefix("counter(")) {
654 if let Some(close) = rest.find(')') {
655 if let Some(inner) = rest.get(..close) {
656 let mut parts = inner.splitn(2, ',');
657 let name = parts.next().unwrap_or("").trim();
658 let style = parts.next().map(|s| s.trim().to_lowercase());
659 if let Some(stack) = counter_scope {
660 let formatted = stack.format_counter(name, style.as_deref(), counter_styles);
661 out.push_str(&formatted);
662 } else {
663 let val = counters.get(name).copied().unwrap_or(0);
664 let val_u32 = val.max(0) as u32;
665 let formatted = match style.as_deref() {
666 Some("upper-roman") => super::super::web_engine::number_to_roman(val_u32, true),
667 Some("lower-roman") => super::super::web_engine::number_to_roman(val_u32, false),
668 Some("upper-alpha") | Some("upper-latin") => {
669 super::super::web_engine::number_to_alpha(val_u32, true)
670 }
671 Some("lower-alpha") | Some("lower-latin") => {
672 super::super::web_engine::number_to_alpha(val_u32, false)
673 }
674 Some(custom) => match counter_styles.iter().find(|d| d.name == custom) {
675 Some(def) => format_with_counter_style(def, val),
676 None => alloc::format!("{}", val),
677 },
678 None => alloc::format!("{}", val),
679 };
680 out.push_str(&formatted);
681 }
682 }
683 i += "counter(".len() + close + 1;
684 continue;
685 }
686 }
687 if let Some(rest) = t.get(i..).and_then(|r| r.strip_prefix("counters(")) {
688 if let Some(close) = rest.find(')') {
689 if let Some(inner) = rest.get(..close) {
690 let mut parts = inner.splitn(3, ',');
691 let name = parts.next().unwrap_or("").trim();
692 let sep = parts.next().map(|s| {
693 s.trim()
694 .trim_matches(|c| c == '"' || c == '\'')
695 .to_string()
696 }).unwrap_or_default();
697 let style = parts.next().map(|s| s.trim().to_lowercase());
698 if let Some(stack) = counter_scope {
699 let formatted = stack.format_counters(name, &sep, style.as_deref(), counter_styles);
700 out.push_str(&formatted);
701 } else {
702 let val = counters.get(name).copied().unwrap_or(0);
703 let val_u32 = val.max(0) as u32;
704 let formatted = match style.as_deref() {
705 Some("upper-roman") => super::super::web_engine::number_to_roman(val_u32, true),
706 Some("lower-roman") => super::super::web_engine::number_to_roman(val_u32, false),
707 Some("upper-alpha") | Some("upper-latin") => {
708 super::super::web_engine::number_to_alpha(val_u32, true)
709 }
710 Some("lower-alpha") | Some("lower-latin") => {
711 super::super::web_engine::number_to_alpha(val_u32, false)
712 }
713 Some(custom) => match counter_styles.iter().find(|d| d.name == custom) {
714 Some(def) => format_with_counter_style(def, val),
715 None => alloc::format!("{}", val),
716 },
717 None => alloc::format!("{}", val),
718 };
719 out.push_str(&formatted);
720 }
721 }
722 i += "counters(".len() + close + 1;
723 continue;
724 }
725 }
726 if let Some(rest) = t.get(i..) {
732 if let Some(r2) = rest.strip_prefix("no-open-quote") {
733 let _ = r2;
734 i += "no-open-quote".len();
735 continue;
736 }
737 if let Some(r2) = rest.strip_prefix("no-close-quote") {
738 let _ = r2;
739 i += "no-close-quote".len();
740 continue;
741 }
742 if rest.starts_with("open-quote") {
743 match quotes_pairs {
744 Some(pairs) if !pairs.is_empty() => {
745 let idx = quote_depth.min(pairs.len() - 1);
746 out.push_str(&pairs[idx].0);
747 }
748 Some(_) => {} None => out.push('\u{201C}'),
750 }
751 i += "open-quote".len();
752 continue;
753 }
754 if rest.starts_with("close-quote") {
755 match quotes_pairs {
756 Some(pairs) if !pairs.is_empty() => {
757 let idx = quote_depth.min(pairs.len() - 1);
758 out.push_str(&pairs[idx].1);
759 }
760 Some(_) => {} None => out.push('\u{201D}'),
762 }
763 i += "close-quote".len();
764 continue;
765 }
766 }
767 while i < bytes.len() && !bytes[i].is_ascii_whitespace() {
770 i += 1;
771 }
772 }
773 out
774}
775
776pub(crate) fn parse_counter_decl(v: &str, default: i64) -> alloc::vec::Vec<(String, i64)> {
780 let t = v.trim();
781 if t.is_empty() || t.eq_ignore_ascii_case("none") {
782 return alloc::vec::Vec::new();
783 }
784 let tokens: alloc::vec::Vec<&str> = t.split_whitespace().collect();
785 let mut out = alloc::vec::Vec::new();
786 let mut i = 0usize;
787 while i < tokens.len() {
788 let name = tokens[i];
789 i += 1;
790 let value = if i < tokens.len() {
791 if let Ok(n) = tokens[i].parse::<i64>() {
792 i += 1;
793 n
794 } else {
795 default
796 }
797 } else {
798 default
799 };
800 out.push((name.to_string(), value));
801 }
802 out
803}
804
805pub(crate) fn apply_counter_decls(
812 specified_values: &BTreeMap<String, String>,
813 counters: &mut BTreeMap<String, i64>,
814) -> alloc::collections::BTreeSet<String> {
815 let mut reset_names = alloc::collections::BTreeSet::new();
816 if let Some(reset) = specified_values.get("counter-reset") {
817 for (name, value) in parse_counter_decl(reset, 0) {
818 counters.insert(name.clone(), value);
819 reset_names.insert(name);
820 }
821 }
822 if let Some(incr) = specified_values.get("counter-increment") {
823 for (name, value) in parse_counter_decl(incr, 1) {
824 let cur = counters.get(&name).copied().unwrap_or(0);
825 counters.insert(name, cur + value);
826 }
827 }
828 reset_names
829}
830
831pub(crate) fn selector_pseudo_element(sel: &Selector) -> Option<PseudoElement> {
834 match sel {
835 Selector::Simple(s) => s.pseudo_element,
836 Selector::Chain(steps) => steps.last().and_then(|s| s.simple.pseudo_element),
837 }
838}
839
840pub(crate) fn selector_specificity(sel: &Selector) -> (u32, u32, u32) {
841 match sel {
842 Selector::Simple(s) => simple_specificity(s),
843 Selector::Chain(steps) => {
844 let mut a = 0u32;
845 let mut b = 0u32;
846 let mut c = 0u32;
847 for step in steps {
848 let (sa, sb, sc) = simple_specificity(&step.simple);
849 a += sa;
850 b += sb;
851 c += sc;
852 }
853 (a, b, c)
854 }
855 }
856}
857
858fn is_node_disabled(node: &Node, ancestors: &[AncestorContext<'_>]) -> bool {
859 if let NodeType::Element { attributes, .. } = &node.node_type {
860 if attributes.contains_key("disabled") {
861 return true;
862 }
863 }
864 for anc in ancestors {
865 if let NodeType::Element { tag_name, attributes, .. } = &anc.node.node_type {
866 if tag_name == "fieldset" && attributes.contains_key("disabled") {
867 return true;
868 }
869 }
870 }
871 false
872}
873
874#[allow(clippy::too_many_arguments)]
875pub(crate) fn matches_simple_selector(
876 tag_name: &str,
877 id: &Option<String>,
878 classes: &Vec<String>,
879 attributes: &BTreeMap<String, String>,
880 sel: &SimpleSelector,
881 sibling_index: Option<usize>,
882 sibling_count: Option<usize>,
883 active_pseudo_states: &[&str],
884 type_index: Option<usize>,
885 type_count: Option<usize>,
886 is_empty: bool,
887 node_ptr: usize,
888 node_children: &[Node],
889 own_siblings: Option<&[Node]>,
890 is_disabled: bool,
891) -> bool {
892 if let Some(sel_tag) = &sel.tag_name {
893 if sel_tag != tag_name {
894 return false;
895 }
896 }
897 if let Some(sel_id) = &sel.id {
898 if id.as_ref() != Some(sel_id) {
899 return false;
900 }
901 }
902 for need in &sel.class {
903 if !classes.iter().any(|c| c == need) {
904 return false;
905 }
906 }
907 for attr in &sel.attrs {
908 if !attr_matches(attr, attributes) {
909 return false;
910 }
911 }
912 let (sibling_index, sibling_count) = match (own_siblings, sibling_index) {
917 (Some(sibs), Some(raw))
918 if sel.pseudo_classes.iter().any(|p| {
919 matches!(
920 p,
921 PseudoClass::FirstChild
922 | PseudoClass::LastChild
923 | PseudoClass::NthChild(..)
924 | PseudoClass::NthLastChild(..)
925 | PseudoClass::OnlyChild
926 )
927 }) =>
928 {
929 let is_el = |n: &&Node| matches!(n.node_type, NodeType::Element { .. });
930 let before = sibs.get(..raw).map_or(0, |b| b.iter().filter(is_el).count());
931 (Some(before), Some(sibs.iter().filter(is_el).count()))
932 }
933 _ => (sibling_index, sibling_count),
934 };
935 for pseudo in &sel.pseudo_classes {
936 match pseudo {
937 PseudoClass::FirstChild => {
938 if sibling_index != Some(0) {
939 return false;
940 }
941 }
942 PseudoClass::LastChild => match (sibling_index, sibling_count) {
943 (Some(idx), Some(count)) if idx + 1 == count => {}
944 _ => return false,
945 },
946 PseudoClass::NthChild(a, b, filter) => match filter {
947 Some(sel) => match filtered_sibling_position(own_siblings, node_ptr, sel) {
948 Some((idx, _count)) => {
949 if !nth_matches(*a, *b, (idx + 1) as i64) {
950 return false;
951 }
952 }
953 None => return false,
954 },
955 None => match sibling_index {
956 Some(idx) => {
957 if !nth_matches(*a, *b, (idx + 1) as i64) {
958 return false;
959 }
960 }
961 None => return false,
962 },
963 },
964 PseudoClass::NthLastChild(a, b, filter) => match filter {
965 Some(sel) => match filtered_sibling_position(own_siblings, node_ptr, sel) {
966 Some((idx, count)) => {
967 if !nth_matches(*a, *b, (count - idx) as i64) {
968 return false;
969 }
970 }
971 None => return false,
972 },
973 None => match (sibling_index, sibling_count) {
974 (Some(idx), Some(count)) => {
975 if !nth_matches(*a, *b, (count - idx) as i64) {
976 return false;
977 }
978 }
979 _ => return false,
980 },
981 },
982 PseudoClass::Checked => {
983 if !attributes.contains_key("checked") {
984 return false;
985 }
986 }
987 PseudoClass::Disabled => {
999 if !is_disabled {
1000 return false;
1001 }
1002 }
1003 PseudoClass::FirstOfType => {
1004 if type_index != Some(0) {
1005 return false;
1006 }
1007 }
1008 PseudoClass::LastOfType => match (type_index, type_count) {
1009 (Some(idx), Some(count)) if idx + 1 == count => {}
1010 _ => return false,
1011 },
1012 PseudoClass::NthOfType(a, b) => match type_index {
1013 Some(idx) => {
1014 if !nth_matches(*a, *b, (idx + 1) as i64) {
1015 return false;
1016 }
1017 }
1018 None => return false,
1019 },
1020 PseudoClass::NthLastOfType(a, b) => match (type_index, type_count) {
1021 (Some(idx), Some(count)) => {
1022 if !nth_matches(*a, *b, (count - idx) as i64) {
1023 return false;
1024 }
1025 }
1026 _ => return false,
1027 },
1028 PseudoClass::OnlyOfType => match (type_index, type_count) {
1029 (Some(0), Some(1)) => {}
1030 _ => return false,
1031 },
1032 PseudoClass::OnlyChild => match (sibling_index, sibling_count) {
1033 (Some(0), Some(1)) => {}
1034 _ => return false,
1035 },
1036 PseudoClass::Target => {
1037 let current = super::super::layout::CURRENT_TARGET_ID.lock();
1038 if current.is_empty() || id.as_deref() != Some(current.as_str()) {
1039 return false;
1040 }
1041 }
1042 PseudoClass::Empty => {
1043 if !is_empty {
1044 return false;
1045 }
1046 }
1047 PseudoClass::Not(inner) => {
1048 if matches_simple_selector(
1049 tag_name,
1050 id,
1051 classes,
1052 attributes,
1053 inner,
1054 sibling_index,
1055 sibling_count,
1056 active_pseudo_states,
1057 type_index,
1058 type_count,
1059 is_empty,
1060 node_ptr,
1061 node_children,
1062 own_siblings,
1063 is_disabled,
1064 ) {
1065 return false;
1066 }
1067 }
1068 PseudoClass::Is(list) | PseudoClass::Where(list) => {
1069 let any_match = list.iter().any(|inner| {
1070 matches_simple_selector(
1071 tag_name,
1072 id,
1073 classes,
1074 attributes,
1075 inner,
1076 sibling_index,
1077 sibling_count,
1078 active_pseudo_states,
1079 type_index,
1080 type_count,
1081 is_empty,
1082 node_ptr,
1083 node_children,
1084 own_siblings,
1085 is_disabled,
1086 )
1087 });
1088 if !any_match {
1089 return false;
1090 }
1091 }
1092 PseudoClass::Has(list) => {
1093 let (sibling_list, descendant_list): (
1094 Vec<&(HasCombinator, SimpleSelector)>,
1095 Vec<&(HasCombinator, SimpleSelector)>,
1096 ) = list.iter().partition(|(comb, _)| {
1097 matches!(comb, HasCombinator::NextSibling | HasCombinator::SubsequentSibling)
1098 });
1099 let sibling_ok = !sibling_list.is_empty()
1100 && own_siblings.zip(sibling_index).is_some_and(|(sibs, my_idx)| {
1101 sibling_list.iter().any(|(comb, sel)| {
1102 has_sibling_match(sibs, my_idx, *comb == HasCombinator::NextSibling, sel)
1103 })
1104 });
1105 let descendant_ok = !descendant_list.is_empty()
1106 && {
1107 let owned: Vec<(bool, SimpleSelector)> = descendant_list
1108 .iter()
1109 .map(|(comb, sel)| (*comb == HasCombinator::Child, sel.clone()))
1110 .collect();
1111 has_descendant_match(node_children, &owned)
1112 };
1113 if !sibling_ok && !descendant_ok {
1114 return false;
1115 }
1116 }
1117 PseudoClass::Dir(target) => {
1118 let effective = attributes
1119 .get("dir")
1120 .map(|v| v.trim().to_lowercase())
1121 .filter(|v| v == "ltr" || v == "rtl")
1122 .unwrap_or_else(|| String::from("ltr"));
1123 if effective != *target {
1124 return false;
1125 }
1126 }
1127 PseudoClass::Lang(target) => {
1128 let own_lang = attributes.get("lang").map(|v| v.to_lowercase());
1129 let effective = match own_lang {
1130 Some(l) if !l.is_empty() => l,
1131 _ => super::super::layout::DOCUMENT_LANG.lock().clone(),
1132 };
1133 let matched = !effective.is_empty()
1134 && (effective == *target
1135 || effective.starts_with(&alloc::format!("{}-", target)));
1136 if !matched {
1137 return false;
1138 }
1139 }
1140 PseudoClass::Other(p) => match p.as_str() {
1141 "checked" => {
1144 if !attributes.contains_key("checked") {
1145 return false;
1146 }
1147 }
1148 "disabled" => {
1149 if !is_disabled {
1150 return false;
1151 }
1152 }
1153 "enabled" => {
1157 if is_disabled {
1158 return false;
1159 }
1160 }
1161 "required" => {
1162 if !attributes.contains_key("required") {
1163 return false;
1164 }
1165 }
1166 "optional" => {
1168 if attributes.contains_key("required") {
1169 return false;
1170 }
1171 }
1172 "valid" | "invalid" | "user-valid" | "user-invalid" => {
1195 let value = attributes.get("value").map(|s| s.trim()).unwrap_or("");
1196 let ty = attributes.get("type").map(|s| s.as_str()).unwrap_or("");
1197 let required_empty = if !attributes.contains_key("required") {
1198 false
1199 } else if tag_name == "input" && ty == "checkbox" {
1200 !attributes.contains_key("checked")
1201 } else if tag_name == "input" && ty == "radio" {
1202 let group_checked = attributes.get("name").is_some_and(|nm| {
1203 own_siblings.is_some_and(|sibs| {
1204 sibs.iter().any(|sib| {
1205 if let NodeType::Element {
1206 tag_name: st,
1207 attributes: sa,
1208 ..
1209 } = &sib.node_type
1210 {
1211 st == "input"
1212 && sa.get("type").map(|t| t == "radio").unwrap_or(false)
1213 && sa.get("name") == Some(nm)
1214 && sa.contains_key("checked")
1215 } else {
1216 false
1217 }
1218 })
1219 })
1220 });
1221 !(attributes.contains_key("checked") || group_checked)
1222 } else {
1223 value.is_empty()
1224 };
1225 let min = attributes.get("min").and_then(|v| v.parse::<f64>().ok());
1226 let max = attributes.get("max").and_then(|v| v.parse::<f64>().ok());
1227 let out_of_range = if min.is_some() || max.is_some() {
1228 value.parse::<f64>().ok().map(|v| {
1229 !(min.is_none_or(|m| v >= m) && max.is_none_or(|m| v <= m))
1230 })
1231 } else {
1232 None
1233 }
1234 .unwrap_or(false);
1235 let is_invalid = required_empty || out_of_range;
1236 let is_user_variant = p.as_str().starts_with("user-");
1237 if is_user_variant && !attributes.contains_key("_user_interacted") {
1238 return false;
1239 }
1240 let want_valid = p.as_str() == "valid" || p.as_str() == "user-valid";
1241 if is_invalid == want_valid {
1242 return false;
1243 }
1244 }
1245 "readonly" | "read-only" => {
1246 if !attributes.contains_key("readonly") {
1247 return false;
1248 }
1249 }
1250 "read-write" => {
1254 if attributes.contains_key("readonly") {
1255 return false;
1256 }
1257 }
1258 "indeterminate" => {
1265 let is_indeterminate_progress =
1266 tag_name == "progress" && !attributes.contains_key("value");
1267 let matched = attributes.contains_key("indeterminate")
1268 || attributes.contains_key("_indeterminate")
1269 || is_indeterminate_progress;
1270 if !matched {
1271 return false;
1272 }
1273 }
1274 "focus-visible" => {
1277 if !active_pseudo_states.contains(&"focus") {
1278 return false;
1279 }
1280 }
1281 "focus-within" => {
1285 if !super::super::layout::FOCUS_WITHIN_PTRS.lock().contains(&node_ptr) {
1286 return false;
1287 }
1288 }
1289 "link" => {
1294 if !(tag_name == "a" || tag_name == "area") || !attributes.contains_key("href")
1295 {
1296 return false;
1297 }
1298 }
1299 "visited" => {
1300 return false;
1301 }
1302 "defined" => {
1307 if crate::os_lib::custom_elements::is_valid_custom_element_name(tag_name)
1308 && !attributes.contains_key("_defined")
1309 {
1310 return false;
1311 }
1312 }
1313 "fullscreen" => {
1317 if !attributes.contains_key("_fullscreen") {
1318 return false;
1319 }
1320 }
1321 "modal" => {
1327 if !attributes.contains_key("_fullscreen") && !attributes.contains_key("_modal")
1328 {
1329 return false;
1330 }
1331 }
1332 "popover-open" => {
1339 if !attributes.contains_key("_popover_open") {
1340 return false;
1341 }
1342 }
1343 "root" => {
1345 if tag_name != "html" {
1346 return false;
1347 }
1348 }
1349 "placeholder-shown" => {
1354 let has_placeholder = attributes
1355 .get("placeholder")
1356 .map(|p| !p.is_empty())
1357 .unwrap_or(false);
1358 let value_empty = attributes
1359 .get("value")
1360 .map(|v| v.is_empty())
1361 .unwrap_or(true);
1362 if !has_placeholder || !value_empty {
1363 return false;
1364 }
1365 }
1366 "in-range" | "out-of-range" => {
1372 let min = attributes.get("min").and_then(|v| v.parse::<f64>().ok());
1373 let max = attributes.get("max").and_then(|v| v.parse::<f64>().ok());
1374 if min.is_none() && max.is_none() {
1375 return false;
1376 }
1377 let value = attributes.get("value").and_then(|v| v.parse::<f64>().ok());
1378 let in_range = match value {
1379 Some(v) => min.is_none_or(|m| v >= m) && max.is_none_or(|m| v <= m),
1380 None => true,
1382 };
1383 let want_in_range = p.as_str() == "in-range";
1384 if in_range != want_in_range {
1385 return false;
1386 }
1387 }
1388 "default" => {
1395 let is_checkable = attributes
1396 .get("type")
1397 .map(|t| t == "checkbox" || t == "radio")
1398 .unwrap_or(false);
1399 let matched = if tag_name == "option" {
1400 attributes.contains_key("selected")
1401 } else if tag_name == "input" && is_checkable {
1402 attributes.contains_key("checked")
1403 } else {
1404 false
1405 };
1406 if !matched {
1407 return false;
1408 }
1409 }
1410 _ => {
1411 if !active_pseudo_states.contains(&p.as_str()) {
1412 return false;
1413 }
1414 }
1415 },
1416 }
1417 }
1418 true
1419}
1420
1421pub(crate) fn type_sibling_position(
1426 node: &Node,
1427 ancestors: &[AncestorContext<'_>],
1428 sibling_index: Option<usize>,
1429) -> (Option<usize>, Option<usize>) {
1430 let NodeType::Element { tag_name, .. } = &node.node_type else {
1431 return (None, None);
1432 };
1433 let Some(parent_ctx) = ancestors.last() else {
1434 return (Some(0), Some(1));
1435 };
1436 let Some(my_idx) = sibling_index else {
1437 return (None, None);
1438 };
1439 let mut type_idx = None;
1440 let mut type_count = 0usize;
1441 for (i, child) in parent_ctx.node.children.iter().enumerate() {
1442 if let NodeType::Element { tag_name: t, .. } = &child.node_type {
1443 if t == tag_name {
1444 if i == my_idx {
1445 type_idx = Some(type_count);
1446 }
1447 type_count += 1;
1448 }
1449 }
1450 }
1451 (type_idx, Some(type_count))
1452}
1453
1454pub(crate) fn nth_matches(a: i64, b: i64, position: i64) -> bool {
1456 if a == 0 {
1457 return position == b;
1458 }
1459 let diff = position - b;
1460 diff % a == 0 && diff / a >= 0
1461}
1462
1463pub(crate) fn attr_matches(attr: &AttrSelector, attributes: &BTreeMap<String, String>) -> bool {
1465 let val = attributes.get(&attr.name).or_else(|| {
1467 attributes
1468 .iter()
1469 .find(|(k, _)| k.to_lowercase() == attr.name)
1470 .map(|(_, v)| v)
1471 });
1472 let val = match val {
1473 Some(v) => v,
1474 None => return false, };
1476 let (val_cmp, need_cmp): (String, String) = if attr.case_insensitive {
1479 (val.to_lowercase(), attr.value.to_lowercase())
1480 } else {
1481 (val.clone(), attr.value.clone())
1482 };
1483 let val = &val_cmp;
1484 let attr_value = &need_cmp;
1485 match attr.op {
1486 AttrOp::Exists => true,
1487 AttrOp::Eq => val == attr_value,
1488 AttrOp::Prefix => !attr_value.is_empty() && val.starts_with(attr_value.as_str()),
1489 AttrOp::Suffix => !attr_value.is_empty() && val.ends_with(attr_value.as_str()),
1490 AttrOp::Contains => !attr_value.is_empty() && val.contains(attr_value.as_str()),
1491 AttrOp::Word => val.split_whitespace().any(|w| w == attr_value),
1492 AttrOp::Dash => {
1493 val == attr_value || val.starts_with(&alloc::format!("{}-", attr_value))
1494 }
1495 }
1496}
1497
1498pub(crate) struct NodeMatchContext<'a> {
1506 pub is_disabled: bool,
1507 pub type_index: Option<usize>,
1508 pub type_count: Option<usize>,
1509 pub own_siblings: Option<&'a [Node]>,
1510}
1511
1512impl<'a> NodeMatchContext<'a> {
1513 pub fn new(
1514 current: &Node,
1515 ancestors: &'a [AncestorContext<'a>],
1516 sibling_index: Option<usize>,
1517 ) -> Self {
1518 let is_disabled = is_node_disabled(current, ancestors);
1519 let (type_index, type_count) = type_sibling_position(current, ancestors, sibling_index);
1521 let own_siblings = ancestors.last().map(|a| a.node.children.as_slice());
1523 Self {
1524 is_disabled,
1525 type_index,
1526 type_count,
1527 own_siblings,
1528 }
1529 }
1530}
1531
1532pub(crate) fn matches_selector(
1533 selector: &Selector,
1534 current: &Node,
1535 ancestors: &[AncestorContext<'_>],
1536 sibling_index: Option<usize>,
1537 sibling_count: Option<usize>,
1538 active_pseudo_states: &[&str],
1539 ctx: &NodeMatchContext<'_>,
1540) -> bool {
1541 let is_disabled = ctx.is_disabled;
1542 let (type_index, type_count) = (ctx.type_index, ctx.type_count);
1543 let own_siblings = ctx.own_siblings;
1544 match selector {
1545 Selector::Simple(simple) => matches_selector_step_node(
1546 current,
1547 simple,
1548 sibling_index,
1549 sibling_count,
1550 active_pseudo_states,
1551 type_index,
1552 type_count,
1553 own_siblings,
1554 is_disabled,
1555 ),
1556 Selector::Chain(steps) => matches_selector_chain(
1557 steps,
1558 current,
1559 ancestors,
1560 sibling_index,
1561 sibling_count,
1562 active_pseudo_states,
1563 type_index,
1564 type_count,
1565 own_siblings,
1566 is_disabled,
1567 ),
1568 }
1569}
1570
1571#[allow(clippy::too_many_arguments)]
1572pub(crate) fn matches_selector_step_node(
1573 node: &Node,
1574 simple: &SimpleSelector,
1575 sibling_index: Option<usize>,
1576 sibling_count: Option<usize>,
1577 active_pseudo_states: &[&str],
1578 type_index: Option<usize>,
1579 type_count: Option<usize>,
1580 own_siblings: Option<&[Node]>,
1581 is_disabled: bool,
1582) -> bool {
1583 match &node.node_type {
1584 NodeType::Element {
1585 tag_name,
1586 classes,
1587 id,
1588 attributes,
1589 } => matches_simple_selector(
1590 tag_name,
1591 id,
1592 classes,
1593 attributes,
1594 simple,
1595 sibling_index,
1596 sibling_count,
1597 active_pseudo_states,
1598 type_index,
1599 type_count,
1600 node.children.is_empty(),
1601 node as *const Node as usize,
1602 &node.children,
1603 own_siblings,
1604 is_disabled,
1605 ),
1606 NodeType::Text(_) => false,
1607 }
1608}
1609
1610pub(crate) fn has_descendant_match(children: &[Node], list: &[(bool, SimpleSelector)]) -> bool {
1618 for child in children {
1619 if let NodeType::Element {
1620 tag_name,
1621 id,
1622 classes,
1623 attributes,
1624 } = &child.node_type
1625 {
1626 for (_, sel) in list {
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 child.children.is_empty(),
1640 child as *const Node as usize,
1641 &child.children,
1642 None,
1643 is_disabled,
1644 ) {
1645 return true;
1646 }
1647 }
1648 }
1649 let deeper_list: Vec<(bool, SimpleSelector)> = list
1651 .iter()
1652 .filter(|(direct_only, _)| !direct_only)
1653 .cloned()
1654 .collect();
1655 if !deeper_list.is_empty() && has_descendant_match(&child.children, &deeper_list) {
1656 return true;
1657 }
1658 }
1659 false
1660}
1661
1662pub(crate) fn has_sibling_match(siblings: &[Node], my_idx: usize, adjacent: bool, sel: &SimpleSelector) -> bool {
1666 for sib in siblings.iter().skip(my_idx + 1) {
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 if 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 return true;
1693 }
1694 if adjacent {
1695 return false;
1696 }
1697 }
1698 }
1699 false
1700}
1701
1702pub(crate) fn filtered_sibling_position(
1706 own_siblings: Option<&[Node]>,
1707 node_ptr: usize,
1708 sel: &SimpleSelector,
1709) -> Option<(usize, usize)> {
1710 let siblings = own_siblings?;
1711 let mut my_pos = None;
1712 let mut count = 0usize;
1713 for sib in siblings {
1714 if let NodeType::Element {
1715 tag_name,
1716 id,
1717 classes,
1718 attributes,
1719 } = &sib.node_type
1720 {
1721 let is_disabled = attributes.contains_key("disabled");
1722 let is_match = matches_simple_selector(
1723 tag_name,
1724 id,
1725 classes,
1726 attributes,
1727 sel,
1728 None,
1729 None,
1730 &[],
1731 None,
1732 None,
1733 sib.children.is_empty(),
1734 sib as *const Node as usize,
1735 &sib.children,
1736 None,
1737 is_disabled,
1738 );
1739 if is_match {
1740 if sib as *const Node as usize == node_ptr {
1741 my_pos = Some(count);
1742 }
1743 count += 1;
1744 }
1745 }
1746 }
1747 my_pos.map(|idx| (idx, count))
1748}
1749
1750#[allow(clippy::too_many_arguments)]
1751pub(crate) fn matches_selector_chain(
1752 steps: &[SelectorStep],
1753 current: &Node,
1754 ancestors: &[AncestorContext<'_>],
1755 sibling_index: Option<usize>,
1756 sibling_count: Option<usize>,
1757 active_pseudo_states: &[&str],
1758 type_index: Option<usize>,
1759 type_count: Option<usize>,
1760 own_siblings: Option<&[Node]>,
1761 is_disabled: bool,
1762) -> bool {
1763 if steps.is_empty() {
1764 return false;
1765 }
1766 let mut idx = steps.len() - 1;
1767 if !matches_selector_step_node(
1768 current,
1769 &steps[idx].simple,
1770 sibling_index,
1771 sibling_count,
1772 active_pseudo_states,
1773 type_index,
1774 type_count,
1775 own_siblings,
1776 is_disabled,
1777 ) {
1778 return false;
1779 }
1780
1781 let mut anc_pos = ancestors.len();
1782 let mut cur_sib_index = sibling_index;
1784 while idx > 0 {
1785 let comb = steps[idx].combinator.unwrap_or(Combinator::Descendant);
1786 idx -= 1;
1787 match comb {
1788 Combinator::Child => {
1789 if anc_pos == 0 {
1790 return false;
1791 }
1792 anc_pos -= 1;
1793 let anc = ancestors[anc_pos];
1794 let anc_disabled = is_node_disabled(anc.node, &ancestors[0..anc_pos]);
1795 let anc_siblings = anc_pos.checked_sub(1).and_then(|p| ancestors.get(p)).map(|a| a.node.children.as_slice());
1796 if !matches_selector_step_node(
1799 anc.node,
1800 &steps[idx].simple,
1801 anc.sibling_index,
1802 anc.sibling_count,
1803 active_pseudo_states,
1804 None,
1805 None,
1806 anc_siblings,
1807 anc_disabled,
1808 ) {
1809 return false;
1810 }
1811 cur_sib_index = anc.sibling_index;
1812 }
1813 Combinator::Descendant => {
1814 let mut found = false;
1815 while anc_pos > 0 {
1816 anc_pos -= 1;
1817 let anc = ancestors[anc_pos];
1818 let anc_disabled = is_node_disabled(anc.node, &ancestors[0..anc_pos]);
1819 let anc_siblings = anc_pos.checked_sub(1).and_then(|p| ancestors.get(p)).map(|a| a.node.children.as_slice());
1820 if matches_selector_step_node(
1821 anc.node,
1822 &steps[idx].simple,
1823 anc.sibling_index,
1824 anc.sibling_count,
1825 active_pseudo_states,
1826 None,
1827 None,
1828 anc_siblings,
1829 anc_disabled,
1830 ) {
1831 found = true;
1832 cur_sib_index = anc.sibling_index;
1833 break;
1834 }
1835 }
1836 if !found {
1837 return false;
1838 }
1839 }
1840 Combinator::NextSibling | Combinator::SubsequentSibling => {
1841 if anc_pos == 0 {
1843 return false;
1844 }
1845 let parent = ancestors[anc_pos - 1].node;
1846 let ci = match cur_sib_index {
1847 Some(i) => i,
1848 None => return false,
1849 };
1850 let count = Some(parent.children.len());
1851 let adjacent = comb == Combinator::NextSibling;
1852 let mut found = false;
1853 let mut j = ci;
1854 while j > 0 {
1855 j -= 1;
1856 if matches!(parent.children[j].node_type, NodeType::Element { .. }) {
1857 let sib_disabled = is_node_disabled(&parent.children[j], &ancestors[0..anc_pos - 1]);
1858 let m = matches_selector_step_node(
1859 &parent.children[j],
1860 &steps[idx].simple,
1861 Some(j),
1862 count,
1863 active_pseudo_states,
1864 None,
1865 None,
1866 Some(parent.children.as_slice()),
1867 sib_disabled,
1868 );
1869 if m {
1870 found = true;
1871 cur_sib_index = Some(j);
1872 break;
1873 }
1874 if adjacent {
1875 break;
1877 }
1878 }
1879 }
1880 if !found {
1881 return false;
1882 }
1883 }
1884 }
1885 }
1886 true
1887}
1888