1use super::parser::{Selector, SelectorImplDescriptor};
2use hashbrown::HashSet;
3use selectors::attr::{AttrSelectorOperator, ParsedCaseSensitivity};
4use selectors::parser::{Combinator, Component, NthType};
5use std::fmt::{self, Debug, Formatter};
6use std::hash::Hash;
7
8#[derive(PartialEq, Eq, Debug, Copy, Clone)]
9pub(crate) struct NthChild {
10 step: i32,
11 offset: i32,
12}
13
14impl NthChild {
15 #[inline]
16 #[must_use]
17 pub const fn new(step: i32, offset: i32) -> Self {
18 Self { step, offset }
19 }
20
21 #[must_use]
22 pub const fn has_index(self, index: i32) -> bool {
23 let Self { offset, step } = self;
24 let offsetted = index.wrapping_sub(offset);
27 if step == 0 {
28 offsetted == 0
29 } else if (offsetted < 0 && step > 0) || (offsetted > 0 && step < 0) {
30 false
31 } else {
32 offsetted.wrapping_rem(step) == 0
37 }
38 }
39}
40
41#[derive(PartialEq, Eq, Debug)]
42pub(crate) enum OnTagNameExpr {
43 ExplicitAny,
44 Unmatchable,
45 LocalName(Box<str>),
46 NthChild(NthChild),
47 NthOfType(NthChild),
48}
49
50#[derive(Eq, PartialEq)]
51pub(crate) struct AttributeComparisonExpr {
52 pub name: Box<str>,
53 pub value: Box<str>,
54 pub case_sensitivity: ParsedCaseSensitivity,
55 pub operator: AttrSelectorOperator,
56}
57
58impl AttributeComparisonExpr {
59 #[inline]
60 #[must_use]
61 pub const fn new(
62 name: Box<str>,
63 value: Box<str>,
64 case_sensitivity: ParsedCaseSensitivity,
65 operator: AttrSelectorOperator,
66 ) -> Self {
67 Self {
68 name,
69 value,
70 case_sensitivity,
71 operator,
72 }
73 }
74}
75
76impl Debug for AttributeComparisonExpr {
77 #[cold]
78 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
79 f.debug_struct("AttributeExpr")
80 .field("name", &self.name)
81 .field("value", &self.value)
82 .field("case_sensitivity", &self.case_sensitivity)
83 .field(
84 "operator",
85 match self.operator {
86 AttrSelectorOperator::Equal => &"AttrSelectorOperator::Equal",
87 AttrSelectorOperator::Includes => &"AttrSelectorOperator::Includes",
88 AttrSelectorOperator::DashMatch => &"AttrSelectorOperator::DashMatch",
89 AttrSelectorOperator::Prefix => &"AttrSelectorOperator::Prefix",
90 AttrSelectorOperator::Substring => &"AttrSelectorOperator::Substring",
91 AttrSelectorOperator::Suffix => &"AttrSelectorOperator::Suffix",
92 },
93 )
94 .finish()
95 }
96}
97
98#[derive(PartialEq, Eq, Debug)]
100pub(crate) enum OnAttributesExpr {
101 Id(Box<str>),
102 Class(Box<str>),
103 AttributeExists(Box<str>),
104 AttributeComparisonExpr(AttributeComparisonExpr),
105}
106
107#[derive(PartialEq, Eq, Debug)]
108enum Condition {
111 OnTagName(OnTagNameExpr),
112 OnAttributes(OnAttributesExpr),
113}
114
115impl From<&Component<SelectorImplDescriptor>> for Condition {
116 fn from(component: &Component<SelectorImplDescriptor>) -> Self {
117 match component {
118 Component::LocalName(n) => {
119 Self::OnTagName(OnTagNameExpr::LocalName(n.name.to_boxed_slice()))
120 }
121 Component::ExplicitUniversalType | Component::ExplicitAnyNamespace => {
122 Self::OnTagName(OnTagNameExpr::ExplicitAny)
123 }
124 Component::ExplicitNoNamespace => Self::OnTagName(OnTagNameExpr::Unmatchable),
125 Component::ID(id) => Self::OnAttributes(OnAttributesExpr::Id(id.to_boxed_slice())),
126 Component::Class(c) => Self::OnAttributes(OnAttributesExpr::Class(c.to_boxed_slice())),
127 Component::AttributeInNoNamespaceExists { local_name, .. } => Self::OnAttributes(
128 OnAttributesExpr::AttributeExists(local_name.to_boxed_slice()),
129 ),
130 &Component::AttributeInNoNamespace {
131 ref local_name,
132 ref value,
133 operator,
134 case_sensitivity,
135 } => Self::OnAttributes(OnAttributesExpr::AttributeComparisonExpr(
136 AttributeComparisonExpr::new(
137 local_name.to_boxed_slice(),
138 value.to_boxed_slice(),
139 case_sensitivity,
140 operator,
141 ),
142 )),
143 Component::Nth(data) if data.ty == NthType::Child => {
144 Self::OnTagName(OnTagNameExpr::NthChild(NthChild::new(data.an_plus_b.0, data.an_plus_b.1)))
145 }
146 Component::Nth(data) if data.ty == NthType::OfType => {
147 Self::OnTagName(OnTagNameExpr::NthOfType(NthChild::new(data.an_plus_b.0, data.an_plus_b.1)))
148 }
149 bad_selector => unreachable!(
154 "Unsupported selector components should be filtered out by the parser: {bad_selector:?}"
155 ),
156 }
157 }
158}
159
160#[derive(PartialEq, Eq, Debug)]
161pub(crate) struct Expr<E>
162where
163 E: PartialEq + Eq + Debug,
164{
165 pub simple_expr: E,
166 pub negation: bool,
167}
168
169impl<E> Expr<E>
170where
171 E: PartialEq + Eq + Debug,
172{
173 #[inline]
174 const fn new(simple_expr: E, negation: bool) -> Self {
175 Self {
176 simple_expr,
177 negation,
178 }
179 }
180}
181
182#[derive(PartialEq, Eq, Debug, Default)]
183pub(crate) struct Predicate {
184 pub on_tag_name_exprs: Vec<Expr<OnTagNameExpr>>,
185 pub on_attr_exprs: Vec<Expr<OnAttributesExpr>>,
186}
187
188#[inline]
189fn add_expr_to_list<E>(list: &mut Vec<Expr<E>>, expr: E, negation: bool)
190where
191 E: PartialEq + Eq + Debug,
192{
193 list.push(Expr::new(expr, negation));
194}
195
196impl Predicate {
197 #[inline]
198 fn add_component(&mut self, component: &Component<SelectorImplDescriptor>, negation: bool) {
199 match Condition::from(component) {
200 Condition::OnTagName(e) => add_expr_to_list(&mut self.on_tag_name_exprs, e, negation),
201 Condition::OnAttributes(e) => add_expr_to_list(&mut self.on_attr_exprs, e, negation),
202 }
203 }
204}
205
206#[derive(PartialEq, Eq, Debug)]
207pub(crate) struct AstNode<P>
208where
209 P: Hash + Eq,
210{
211 pub predicate: Predicate,
212 pub children: Vec<AstNode<P>>,
213 pub descendants: Vec<AstNode<P>>,
214 pub payload: HashSet<P>,
215}
216
217impl<P> AstNode<P>
218where
219 P: Hash + Eq,
220{
221 fn new(predicate: Predicate) -> Self {
222 Self {
223 predicate,
224 children: Vec::default(),
225 descendants: Vec::default(),
226 payload: HashSet::default(),
227 }
228 }
229}
230
231#[derive(Default, PartialEq, Eq, Debug)]
233pub struct Ast<P>
234where
235 P: PartialEq + Eq + Copy + Debug + Hash,
236{
237 pub(crate) root: Vec<AstNode<P>>,
238 pub(crate) cumulative_node_count: usize,
240}
241
242impl<P> Ast<P>
243where
244 P: PartialEq + Eq + Copy + Debug + Hash,
245{
246 #[inline]
247 fn host_expressions(
248 predicate: Predicate,
249 branches: &mut Vec<AstNode<P>>,
250 cumulative_node_count: &mut usize,
251 ) -> usize {
252 branches
253 .iter()
254 .enumerate()
255 .find(|(_, n)| n.predicate == predicate)
256 .map(|(i, _)| i)
257 .unwrap_or_else(|| {
258 branches.push(AstNode::new(predicate));
259 *cumulative_node_count += 1;
260
261 branches.len() - 1
262 })
263 }
264
265 pub fn add_selector(&mut self, selector: &Selector, payload: P) {
266 for selector_item in (selector.0).slice() {
267 let mut predicate = Predicate::default();
268 let mut branches = &mut self.root;
269
270 macro_rules! host_and_switch_branch_vec {
271 ($branches:ident) => {{
272 let node_idx = Self::host_expressions(
273 predicate,
274 branches,
275 &mut self.cumulative_node_count,
276 );
277
278 branches = &mut branches[node_idx].$branches;
279 predicate = Predicate::default();
280 }};
281 }
282
283 for component in selector_item.iter_raw_parse_order_from(0) {
284 match component {
285 Component::Combinator(Combinator::Child) => {
286 host_and_switch_branch_vec!(children);
287 }
288 Component::Combinator(Combinator::Descendant) => {
289 host_and_switch_branch_vec!(descendants);
290 }
291 Component::Negation(ss) => {
292 ss.slice()
293 .iter()
294 .for_each(|s| s.iter().for_each(|c| predicate.add_component(c, true)));
295 }
296 _ => predicate.add_component(component, false),
297 }
298 }
299
300 let node_idx =
301 Self::host_expressions(predicate, branches, &mut self.cumulative_node_count);
302
303 branches[node_idx].payload.insert(payload);
304 }
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311 use crate::selectors_vm::SelectorError;
312
313 macro_rules! set {
314 ($($items:expr),*) => {
315 vec![$($items),*].into_iter().collect::<HashSet<_>>()
316 };
317 }
318
319 #[track_caller]
320 fn assert_ast(selectors: &[&str], expected: Ast<usize>) {
321 let mut ast = Ast::default();
322
323 for (idx, selector) in selectors.iter().enumerate() {
324 ast.add_selector(&selector.parse().unwrap(), idx);
325 }
326
327 assert_eq!(ast, expected);
328 }
329
330 #[track_caller]
331 fn assert_err(selector: &str, expected_err: SelectorError) {
332 assert_eq!(selector.parse::<Selector>().unwrap_err(), expected_err);
333 }
334
335 #[test]
336 fn simple_non_attr_expression() {
337 for (selector, expected) in [
338 (
339 "*",
340 Expr {
341 simple_expr: OnTagNameExpr::ExplicitAny,
342 negation: false,
343 },
344 ),
345 (
346 "div",
347 Expr {
348 simple_expr: OnTagNameExpr::LocalName("div".into()),
349 negation: false,
350 },
351 ),
352 (
353 ":not(div)",
354 Expr {
355 simple_expr: OnTagNameExpr::LocalName("div".into()),
356 negation: true,
357 },
358 ),
359 ] {
360 assert_ast(
361 &[selector],
362 Ast {
363 root: vec![AstNode {
364 predicate: Predicate {
365 on_tag_name_exprs: vec![expected],
366 ..Default::default()
367 },
368 children: vec![],
369 descendants: vec![],
370 payload: set![0],
371 }],
372 cumulative_node_count: 1,
373 },
374 );
375 }
376 }
377
378 #[test]
379 fn simple_attr_expression() {
380 for (selector, expected) in [
381 (
382 "#foo",
383 Expr {
384 simple_expr: OnAttributesExpr::Id("foo".into()),
385 negation: false,
386 },
387 ),
388 (
389 ".bar",
390 Expr {
391 simple_expr: OnAttributesExpr::Class("bar".into()),
392 negation: false,
393 },
394 ),
395 (
396 "[foo]",
397 Expr {
398 simple_expr: OnAttributesExpr::AttributeExists("foo".into()),
399 negation: false,
400 },
401 ),
402 (
403 r#"[foo="bar"]"#,
404 Expr {
405 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
406 AttributeComparisonExpr {
407 name: "foo".into(),
408 value: "bar".into(),
409 case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
410 operator: AttrSelectorOperator::Equal,
411 },
412 ),
413 negation: false,
414 },
415 ),
416 (
417 r#"[foo*=""]"#,
418 Expr {
419 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
420 AttributeComparisonExpr {
421 name: "foo".into(),
422 value: "".into(),
423 case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
424 operator: AttrSelectorOperator::Substring,
425 },
426 ),
427 negation: false,
428 },
429 ),
430 (
431 r#"[foo~="bar" i]"#,
432 Expr {
433 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
434 AttributeComparisonExpr {
435 name: "foo".into(),
436 value: "bar".into(),
437 case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
438 operator: AttrSelectorOperator::Includes,
439 },
440 ),
441 negation: false,
442 },
443 ),
444 (
445 r#"[foo|="bar" s]"#,
446 Expr {
447 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
448 AttributeComparisonExpr {
449 name: "foo".into(),
450 value: "bar".into(),
451 case_sensitivity: ParsedCaseSensitivity::ExplicitCaseSensitive,
452 operator: AttrSelectorOperator::DashMatch,
453 },
454 ),
455 negation: false,
456 },
457 ),
458 (
459 r#"[foo^="bar"]"#,
460 Expr {
461 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
462 AttributeComparisonExpr {
463 name: "foo".into(),
464 value: "bar".into(),
465 case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
466 operator: AttrSelectorOperator::Prefix,
467 },
468 ),
469 negation: false,
470 },
471 ),
472 (
473 r#"[foo*="bar"]"#,
474 Expr {
475 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
476 AttributeComparisonExpr {
477 name: "foo".into(),
478 value: "bar".into(),
479 case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
480 operator: AttrSelectorOperator::Substring,
481 },
482 ),
483 negation: false,
484 },
485 ),
486 (
487 r#"[foo$="bar"]"#,
488 Expr {
489 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
490 AttributeComparisonExpr {
491 name: "foo".into(),
492 value: "bar".into(),
493 case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
494 operator: AttrSelectorOperator::Suffix,
495 },
496 ),
497 negation: false,
498 },
499 ),
500 (
501 r#":not([foo$="bar"])"#,
502 Expr {
503 simple_expr: OnAttributesExpr::AttributeComparisonExpr(
504 AttributeComparisonExpr {
505 name: "foo".into(),
506 value: "bar".into(),
507 case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
508 operator: AttrSelectorOperator::Suffix,
509 },
510 ),
511 negation: true,
512 },
513 ),
514 ] {
515 assert_ast(
516 &[selector],
517 Ast {
518 root: vec![AstNode {
519 predicate: Predicate {
520 on_attr_exprs: vec![expected],
521 ..Default::default()
522 },
523 children: vec![],
524 descendants: vec![],
525 payload: set![0],
526 }],
527 cumulative_node_count: 1,
528 },
529 );
530 }
531 }
532
533 #[test]
534 fn compound_selectors() {
535 assert_ast(
536 &["div.foo#bar:not([baz])"],
537 Ast {
538 root: vec![AstNode {
539 predicate: Predicate {
540 on_tag_name_exprs: vec![Expr {
541 simple_expr: OnTagNameExpr::LocalName("div".into()),
542 negation: false,
543 }],
544 on_attr_exprs: vec![
545 Expr {
546 simple_expr: OnAttributesExpr::AttributeExists("baz".into()),
547 negation: true,
548 },
549 Expr {
550 simple_expr: OnAttributesExpr::Id("bar".into()),
551 negation: false,
552 },
553 Expr {
554 simple_expr: OnAttributesExpr::Class("foo".into()),
555 negation: false,
556 },
557 ],
558 },
559 children: vec![],
560 descendants: vec![],
561 payload: set![0],
562 }],
563 cumulative_node_count: 1,
564 },
565 );
566 }
567
568 #[test]
569 fn multiple_payloads() {
570 assert_ast(
571 &["#foo", "#foo"],
572 Ast {
573 root: vec![AstNode {
574 predicate: Predicate {
575 on_attr_exprs: vec![Expr {
576 simple_expr: OnAttributesExpr::Id("foo".into()),
577 negation: false,
578 }],
579 ..Default::default()
580 },
581 children: vec![],
582 descendants: vec![],
583 payload: set![0, 1],
584 }],
585 cumulative_node_count: 1,
586 },
587 );
588 }
589
590 #[test]
591 fn selector_list() {
592 assert_ast(
593 &["#foo > div, #foo > span", "#foo > .c1, #foo > .c2"],
594 Ast {
595 root: vec![AstNode {
596 predicate: Predicate {
597 on_attr_exprs: vec![Expr {
598 simple_expr: OnAttributesExpr::Id("foo".into()),
599 negation: false,
600 }],
601 ..Default::default()
602 },
603 children: vec![
604 AstNode {
605 predicate: Predicate {
606 on_tag_name_exprs: vec![Expr {
607 simple_expr: OnTagNameExpr::LocalName("div".into()),
608 negation: false,
609 }],
610 ..Default::default()
611 },
612 children: vec![],
613 descendants: vec![],
614 payload: set![0],
615 },
616 AstNode {
617 predicate: Predicate {
618 on_tag_name_exprs: vec![Expr {
619 simple_expr: OnTagNameExpr::LocalName("span".into()),
620 negation: false,
621 }],
622 ..Default::default()
623 },
624 children: vec![],
625 descendants: vec![],
626 payload: set![0],
627 },
628 AstNode {
629 predicate: Predicate {
630 on_attr_exprs: vec![Expr {
631 simple_expr: OnAttributesExpr::Class("c1".into()),
632 negation: false,
633 }],
634 ..Default::default()
635 },
636 children: vec![],
637 descendants: vec![],
638 payload: set![1],
639 },
640 AstNode {
641 predicate: Predicate {
642 on_attr_exprs: vec![Expr {
643 simple_expr: OnAttributesExpr::Class("c2".into()),
644 negation: false,
645 }],
646 ..Default::default()
647 },
648 children: vec![],
649 descendants: vec![],
650 payload: set![1],
651 },
652 ],
653 descendants: vec![],
654 payload: set![],
655 }],
656 cumulative_node_count: 5,
657 },
658 );
659 }
660
661 #[test]
662 fn combinators() {
663 assert_ast(
664 &[
665 ".c1 > .c2 .c3 #foo",
666 ".c1 > .c2 #bar",
667 ".c1 > #qux",
668 ".c1 #baz",
669 ".c1 [foo] [bar]",
670 "#quz",
671 ],
672 Ast {
673 root: vec![
674 AstNode {
675 predicate: Predicate {
676 on_attr_exprs: vec![Expr {
677 simple_expr: OnAttributesExpr::Class("c1".into()),
678 negation: false,
679 }],
680 ..Default::default()
681 },
682 children: vec![
683 AstNode {
684 predicate: Predicate {
685 on_attr_exprs: vec![Expr {
686 simple_expr: OnAttributesExpr::Class("c2".into()),
687 negation: false,
688 }],
689 ..Default::default()
690 },
691 children: vec![],
692 descendants: vec![
693 AstNode {
694 predicate: Predicate {
695 on_attr_exprs: vec![Expr {
696 simple_expr: OnAttributesExpr::Class("c3".into()),
697 negation: false,
698 }],
699 ..Default::default()
700 },
701 children: vec![],
702 descendants: vec![AstNode {
703 predicate: Predicate {
704 on_attr_exprs: vec![Expr {
705 simple_expr: OnAttributesExpr::Id("foo".into()),
706 negation: false,
707 }],
708 ..Default::default()
709 },
710 children: vec![],
711 descendants: vec![],
712 payload: set![0],
713 }],
714 payload: set![],
715 },
716 AstNode {
717 predicate: Predicate {
718 on_attr_exprs: vec![Expr {
719 simple_expr: OnAttributesExpr::Id("bar".into()),
720 negation: false,
721 }],
722 ..Default::default()
723 },
724 children: vec![],
725 descendants: vec![],
726 payload: set![1],
727 },
728 ],
729 payload: set![],
730 },
731 AstNode {
732 predicate: Predicate {
733 on_attr_exprs: vec![Expr {
734 simple_expr: OnAttributesExpr::Id("qux".into()),
735 negation: false,
736 }],
737 ..Default::default()
738 },
739 children: vec![],
740 descendants: vec![],
741 payload: set![2],
742 },
743 ],
744 descendants: vec![
745 AstNode {
746 predicate: Predicate {
747 on_attr_exprs: vec![Expr {
748 simple_expr: OnAttributesExpr::Id("baz".into()),
749 negation: false,
750 }],
751 ..Default::default()
752 },
753 children: vec![],
754 descendants: vec![],
755 payload: set![3],
756 },
757 AstNode {
758 predicate: Predicate {
759 on_attr_exprs: vec![Expr {
760 simple_expr: OnAttributesExpr::AttributeExists(
761 "foo".into(),
762 ),
763 negation: false,
764 }],
765 ..Default::default()
766 },
767 children: vec![],
768 descendants: vec![AstNode {
769 predicate: Predicate {
770 on_attr_exprs: vec![Expr {
771 simple_expr: OnAttributesExpr::AttributeExists(
772 "bar".into(),
773 ),
774 negation: false,
775 }],
776 ..Default::default()
777 },
778 children: vec![],
779 descendants: vec![],
780 payload: set![4],
781 }],
782 payload: set![],
783 },
784 ],
785 payload: set![],
786 },
787 AstNode {
788 predicate: Predicate {
789 on_attr_exprs: vec![Expr {
790 simple_expr: OnAttributesExpr::Id("quz".into()),
791 negation: false,
792 }],
793 ..Default::default()
794 },
795 children: vec![],
796 descendants: vec![],
797 payload: set![5],
798 },
799 ],
800 cumulative_node_count: 10,
801 },
802 );
803 }
804
805 #[test]
806 fn parse_errors() {
807 assert_err("div@", SelectorError::UnexpectedToken);
808 assert_err("div.", SelectorError::UnexpectedEnd);
809 assert_err(r#"div[="foo"]"#, SelectorError::MissingAttributeName);
810 assert_err("", SelectorError::EmptySelector);
811 assert_err("div >", SelectorError::DanglingCombinator);
812 assert_err(
813 r#"div[foo~"bar"]"#,
814 SelectorError::UnexpectedTokenInAttribute,
815 );
816 assert_err("svg|img", SelectorError::NamespacedSelector);
817 assert_err(".foo()", SelectorError::InvalidClassName);
818 assert_err(":not()", SelectorError::EmptySelector);
819 assert_err("div + span", SelectorError::UnsupportedCombinator('+'));
820 assert_err("div ~ span", SelectorError::UnsupportedCombinator('~'));
821 assert_err(":nth-child(n of a)", SelectorError::UnexpectedToken);
822 }
823
824 #[test]
825 fn pseudo_class_parse_errors() {
826 for s in &[
827 ":active",
828 ":any-link",
829 ":blank",
830 ":checked",
831 ":current",
832 ":default",
833 ":defined",
834 ":dir(rtl)",
835 ":disabled",
836 ":drop",
837 ":empty",
838 ":enabled",
839 ":first",
840 ":fullscreen",
841 ":future",
842 ":focus",
843 ":focus-visible",
844 ":focus-within",
845 ":has(div)",
846 ":host",
847 ":host(h1)",
848 ":host-context(h1)",
849 ":hover",
850 ":indeterminate",
851 ":in-range",
852 ":invalid",
853 ":is(header)",
854 ":lang(en)",
855 ":last-child",
856 ":last-of-type",
857 ":left",
858 ":link",
859 ":local-link",
860 ":nth-col(1)",
861 ":nth-last-child(1)",
862 ":nth-last-col(1)",
863 ":nth-last-of-type(1)",
864 ":only-child",
865 ":only-of-type",
866 ":optional",
867 ":out-of-range",
868 ":past",
869 ":placeholder-shown",
870 ":read-only",
871 ":read-write",
872 ":required",
873 ":right",
874 ":root",
875 ":scope",
876 ":target",
877 ":target-within",
878 ":user-invalid",
879 ":valid",
880 ":visited",
881 ":where(p)",
882 ] {
883 assert_err(s, SelectorError::UnsupportedPseudoClassOrElement);
884 }
885 }
886
887 #[test]
888 fn pseudo_elements_parse_errors() {
889 for s in &[
890 "::after",
891 "::backdrop",
892 "::before",
893 "::cue",
894 "::first-letter",
895 "::first-line",
896 "::grammar-error",
897 "::marker",
898 "::placeholder",
899 "::selection",
900 "::slotted()",
901 "::spelling-error",
902 ] {
903 assert_err(s, SelectorError::UnsupportedPseudoClassOrElement);
904 }
905 }
906
907 #[test]
908 fn negated_pseudo_class_parse_error() {
909 assert_err(
910 ":not(:nth-last-child(even))",
911 SelectorError::UnsupportedPseudoClassOrElement,
912 );
913 }
914
915 #[test]
916 fn nth_child_is_index() {
917 let even = NthChild::new(2, 0);
918 assert!(even.has_index(2));
919 assert!(!even.has_index(1));
920
921 let odd = NthChild::new(2, 1);
922 assert!(odd.has_index(1));
923 assert!(!odd.has_index(2));
924 assert!(odd.has_index(3));
925
926 let first = NthChild::new(0, 1);
927 assert!(first.has_index(1));
928 assert!(!first.has_index(2));
929 assert!(!first.has_index(3));
930 }
931}