1use crate::ArenaVec;
5use core::fmt;
6
7use bun_alloc::{Arena as Bump, ArenaPtr};
8use bun_core::strings;
9use bun_css as css;
10use bun_css::css_values::ident::{CustomIdent, Ident};
11use bun_css::selector::serialize;
12use bun_css::{
13 CSSStringFns, IdentFns, Parser as CssParser, ParserOptions, PrintErr, Printer, SmallList,
14 Token, TokenList,
15};
16use bun_wyhash::Wyhash;
17
18use super::impl_;
19
20use super::builder::SelectorBuilder;
21
22pub use bun_css::Printer as PrinterRe; type CResult<T> = css::Result<T>;
26
27type Str = &'static [u8]; use css::generics::{CssEql, CssHash};
51use bun_alloc::core_alloc::AllocVec as _PVec;
53use bun_alloc::core_alloc::Global as _PG;
54type PlainVec2<T> = _PVec<T, _PG>;
55
56fn small_list_into_box<T, const N: usize>(mut sl: SmallList<T, N>) -> Box<[T]> {
60 let len = sl.len() as usize;
61 let mut v: Vec<T> = Vec::with_capacity(len);
62 {
63 let src = sl.slice();
64 for i in 0..len {
65 unsafe { v.push(core::ptr::read(src.get_unchecked(i))) };
68 }
69 }
70 sl.set_len(0);
71 v.into_boxed_slice()
72}
73
74#[inline]
83fn arena_lowercase(bump: &Bump, name: &[u8]) -> *const [u8] {
84 let buf = bump.alloc_slice_fill_copy(name.len(), 0u8);
85 let _ = strings::copy_lowercase(name, buf);
86 std::ptr::from_ref::<[u8]>(buf)
87}
88
89#[inline]
95fn eql_selector_slice<Impl: BunSelectorImpl>(
96 a: &[GenericSelector<Impl>],
97 b: &[GenericSelector<Impl>],
98) -> bool {
99 a.len() == b.len() && a.iter().zip(b).all(|(l, r)| l.eql(r))
100}
101#[inline]
102fn hash_selector_slice<Impl: BunSelectorImpl>(s: &[GenericSelector<Impl>], hasher: &mut Wyhash) {
103 for sel in s {
104 sel.hash(hasher);
105 }
106}
107#[inline]
108fn deep_clone_selector_slice<Impl: BunSelectorImpl>(
109 s: &[GenericSelector<Impl>],
110) -> Box<[GenericSelector<Impl>]> {
111 s.iter().map(|sel| sel.deep_clone()).collect()
112}
113
114pub type Component = GenericComponent<impl_::Selectors>;
116pub type Selector = GenericSelector<impl_::Selectors>;
117pub type SelectorList = GenericSelectorList<impl_::Selectors>;
118
119#[derive(Clone, Copy, PartialEq, Eq)]
120pub enum ToCssCtx {
121 Lightning,
122 Servo,
123}
124
125pub const SELECTOR_WHITESPACE: &[u8] = &[b' ', b'\t', b'\n', b'\r', 0x0C];
127
128pub fn valid_selector_impl<T: SelectorImpl>() {
131 }
134
135pub trait SelectorImpl: Sized {
144 type ExtraMatchingData;
145 type AttrValue: Clone;
146 type Identifier: Clone;
147 type LocalIdentifier: Clone;
148 type LocalName: Clone;
149 type NamespaceUrl: Clone;
150 type NamespacePrefix: Clone;
151 type BorrowedNamespaceUrl;
152 type BorrowedLocalName;
153 type NonTSPseudoClass: Clone;
154 type VendorPrefix: Clone;
155 type PseudoElement: Clone;
156}
157
158pub trait BunSelectorImpl:
167 SelectorImpl<
168 AttrValue = css::CSSString,
169 Identifier = Ident,
170 LocalIdentifier = css::css_values::ident::IdentOrRef,
171 LocalName = Ident,
172 NamespaceUrl = Str,
173 NamespacePrefix = Ident,
174 NonTSPseudoClass = PseudoClass,
175 PseudoElement = PseudoElement,
176 VendorPrefix = css::VendorPrefix,
177 >
178{
179}
180impl BunSelectorImpl for impl_::Selectors {}
181
182pub mod attrs {
186 use super::*;
187
188 #[derive(Clone)]
189 pub struct NamespaceUrl<Impl: SelectorImpl> {
190 pub prefix: Impl::NamespacePrefix,
191 pub url: Impl::NamespaceUrl,
192 }
193
194 impl<Impl: BunSelectorImpl> NamespaceUrl<Impl> {
195 pub fn eql(&self, rhs: &Self) -> bool {
196 self.prefix.eql(&rhs.prefix) && strings::eql(self.url, rhs.url)
198 }
199 pub fn deep_clone(&self) -> Self {
200 Self {
202 prefix: self.prefix,
203 url: self.url,
204 }
205 }
206 pub fn hash(&self, hasher: &mut Wyhash) {
207 self.prefix.hash(hasher);
208 hasher.update(self.url);
209 }
210 }
211
212 #[derive(Clone)]
213 pub struct AttrSelectorWithOptionalNamespace<Impl: SelectorImpl> {
214 pub namespace: Option<NamespaceConstraint<NamespaceUrl<Impl>>>,
215 pub local_name: Impl::LocalName,
216 pub local_name_lower: Impl::LocalName,
217 pub operation: ParsedAttrSelectorOperation<Impl::AttrValue>,
218 pub never_matches: bool,
219 }
220
221 impl<Impl: BunSelectorImpl> AttrSelectorWithOptionalNamespace<Impl> {
222 pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
223 dest.write_char(b'[')?;
224 if let Some(nsp) = &self.namespace {
225 match nsp {
226 NamespaceConstraint::Specific(v) => {
227 IdentFns::to_css(&v.prefix, dest)?;
228 dest.write_char(b'|')?;
229 }
230 NamespaceConstraint::Any => {
231 dest.write_str("*|")?;
232 }
233 }
234 }
235 IdentFns::to_css(&self.local_name, dest)?;
236 match &self.operation {
237 ParsedAttrSelectorOperation::Exists => {}
238 ParsedAttrSelectorOperation::WithValue {
239 operator,
240 case_sensitivity,
241 expected_value,
242 } => {
243 operator.to_css(dest)?;
244 CSSStringFns::to_css(expected_value, dest)?;
246 match case_sensitivity {
247 ParsedCaseSensitivity::CaseSensitive
248 | ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {}
249 ParsedCaseSensitivity::AsciiCaseInsensitive => {
250 dest.write_str(" i")?;
251 }
252 ParsedCaseSensitivity::ExplicitCaseSensitive => {
253 dest.write_str(" s")?;
254 }
255 }
256 }
257 }
258 dest.write_char(b']')
259 }
260
261 pub fn eql(&self, rhs: &Self) -> bool {
262 match (&self.namespace, &rhs.namespace) {
263 (None, None) => true,
264 (Some(a), Some(b)) => a.eql(b),
265 _ => return false,
266 }
267 .then_some(())
268 .is_some()
269 && self.local_name.eql(&rhs.local_name)
270 && self.local_name_lower.eql(&rhs.local_name_lower)
271 && self.operation.eql(&rhs.operation)
272 && self.never_matches == rhs.never_matches
273 }
274 pub fn deep_clone(&self) -> Self {
275 Self {
276 namespace: self.namespace.as_ref().map(|n| n.deep_clone()),
277 local_name: self.local_name,
278 local_name_lower: self.local_name_lower,
279 operation: self.operation.deep_clone(),
280 never_matches: self.never_matches,
281 }
282 }
283 pub fn hash(&self, hasher: &mut Wyhash) {
284 if let Some(ns) = &self.namespace {
285 ns.hash(hasher);
286 }
287 self.local_name.hash(hasher);
288 self.local_name_lower.hash(hasher);
289 self.operation.hash(hasher);
290 hasher.update(&[self.never_matches as u8]);
291 }
292 }
293
294 #[derive(Clone, PartialEq, Eq)]
295 pub enum NamespaceConstraint<NamespaceUrl> {
296 Any,
297 Specific(NamespaceUrl),
299 }
300
301 impl<Impl: BunSelectorImpl> NamespaceConstraint<NamespaceUrl<Impl>> {
302 pub fn eql(&self, rhs: &Self) -> bool {
303 match (self, rhs) {
304 (Self::Any, Self::Any) => true,
305 (Self::Specific(a), Self::Specific(b)) => a.eql(b),
306 _ => false,
307 }
308 }
309 pub fn hash(&self, hasher: &mut Wyhash) {
310 match self {
311 Self::Any => hasher.update(&0u32.to_ne_bytes()),
312 Self::Specific(n) => {
313 hasher.update(&1u32.to_ne_bytes());
314 n.hash(hasher);
315 }
316 }
317 }
318 pub fn deep_clone(&self) -> Self {
319 match self {
320 Self::Any => Self::Any,
321 Self::Specific(n) => Self::Specific(n.deep_clone()),
322 }
323 }
324 }
325
326 #[derive(Clone, PartialEq, Eq)]
327 pub enum ParsedAttrSelectorOperation<AttrValue> {
328 Exists,
329 WithValue {
330 operator: AttrSelectorOperator,
331 case_sensitivity: ParsedCaseSensitivity,
332 expected_value: AttrValue,
333 },
334 }
335
336 impl ParsedAttrSelectorOperation<css::CSSString> {
339 pub fn deep_clone(&self) -> Self {
340 self.clone()
341 }
342 pub fn eql(&self, rhs: &Self) -> bool {
343 match (self, rhs) {
344 (Self::Exists, Self::Exists) => true,
345 (
346 Self::WithValue {
347 operator: ao,
348 case_sensitivity: ac,
349 expected_value: av,
350 },
351 Self::WithValue {
352 operator: bo,
353 case_sensitivity: bc,
354 expected_value: bv,
355 },
356 ) => {
357 ao == bo
358 && ac == bc
359 && unsafe { strings::eql(&**av, &**bv) }
361 }
362 _ => false,
363 }
364 }
365 pub fn hash(&self, hasher: &mut Wyhash) {
366 match self {
367 Self::Exists => hasher.update(&0u32.to_ne_bytes()),
368 Self::WithValue {
369 operator,
370 case_sensitivity,
371 expected_value,
372 } => {
373 hasher.update(&1u32.to_ne_bytes());
374 operator.hash(hasher);
375 case_sensitivity.hash(hasher);
376 hasher.update(unsafe { crate::arena_str(*expected_value) });
378 }
379 }
380 }
381 }
382
383 #[derive(Clone, Copy, PartialEq, Eq, Hash, CssEql, CssHash, css::generics::DeepClone)]
384 pub enum AttrSelectorOperator {
385 Equal,
386 Includes,
387 DashMatch,
388 Prefix,
389 Substring,
390 Suffix,
391 }
392
393 impl AttrSelectorOperator {
394 pub fn to_css(self, dest: &mut Printer) -> Result<(), PrintErr> {
395 dest.write_str(match self {
398 Self::Equal => "=",
399 Self::Includes => "~=",
400 Self::DashMatch => "|=",
401 Self::Prefix => "^=",
402 Self::Substring => "*=",
403 Self::Suffix => "$=",
404 })
405 }
406 }
407
408 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
409 pub enum AttrSelectorOperation {
410 Equal,
411 Includes,
412 DashMatch,
413 Prefix,
414 Substring,
415 Suffix,
416 }
417
418 #[derive(Clone, Copy, PartialEq, Eq, Hash, CssEql, CssHash, css::generics::DeepClone)]
419 pub enum ParsedCaseSensitivity {
420 ExplicitCaseSensitive,
422 AsciiCaseInsensitive,
424 CaseSensitive,
426 AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
428 }
429}
430
431#[derive(Clone, Copy, Default)]
436pub struct Specificity {
437 pub id_selectors: u32,
438 pub class_like_selectors: u32,
439 pub element_selectors: u32,
440}
441
442impl Specificity {
443 const MAX_10BIT: u32 = (1 << 10) - 1;
444
445 pub fn to_u32(self) -> u32 {
446 (self.id_selectors.min(Self::MAX_10BIT) << 20)
447 | (self.class_like_selectors.min(Self::MAX_10BIT) << 10)
448 | self.element_selectors.min(Self::MAX_10BIT)
449 }
450
451 pub fn from_u32(value: u32) -> Specificity {
452 debug_assert!(value <= (Self::MAX_10BIT << 20 | Self::MAX_10BIT << 10 | Self::MAX_10BIT));
453 Specificity {
454 id_selectors: value >> 20,
455 class_like_selectors: (value >> 10) & Self::MAX_10BIT,
456 element_selectors: value & Self::MAX_10BIT,
457 }
458 }
459
460 pub fn add(&mut self, rhs: Specificity) {
461 self.id_selectors += rhs.id_selectors;
462 self.element_selectors += rhs.element_selectors;
463 self.class_like_selectors += rhs.class_like_selectors;
464 }
465}
466
467pub fn compute_specificity<Impl: BunSelectorImpl>(iter: &[GenericComponent<Impl>]) -> u32 {
468 let spec = compute_complex_selector_specificity::<Impl>(iter);
469 spec.to_u32()
470}
471
472fn compute_complex_selector_specificity<Impl: BunSelectorImpl>(
473 iter: &[GenericComponent<Impl>],
474) -> Specificity {
475 let mut specificity = Specificity::default();
476 for simple_selector in iter {
477 compute_simple_selector_specificity::<Impl>(simple_selector, &mut specificity);
478 }
479 specificity
480}
481
482fn compute_simple_selector_specificity<Impl: BunSelectorImpl>(
483 simple_selector: &GenericComponent<Impl>,
484 specificity: &mut Specificity,
485) {
486 use GenericComponent as C;
487 match simple_selector {
488 C::Combinator(_) => {
489 unreachable!("Found combinator in simple selectors vector?");
490 }
491 C::Part(_) | C::PseudoElement(_) | C::LocalName(_) => {
492 specificity.element_selectors += 1;
493 }
494 C::Slotted(selector) => {
495 specificity.element_selectors += 1;
496 specificity.add(Specificity::from_u32(selector.specificity()));
503 }
504 C::Host(maybe_selector) => {
505 specificity.class_like_selectors += 1;
506 if let Some(selector) = maybe_selector {
507 specificity.add(Specificity::from_u32(selector.specificity()));
509 }
510 }
511 C::Id(_) => {
512 specificity.id_selectors += 1;
513 }
514 C::Class(_)
515 | C::AttributeInNoNamespace { .. }
516 | C::AttributeInNoNamespaceExists { .. }
517 | C::AttributeOther(_)
518 | C::Root
519 | C::Empty
520 | C::Scope
521 | C::Nth(_)
522 | C::NonTsPseudoClass(_) => {
523 specificity.class_like_selectors += 1;
524 }
525 C::NthOf(nth_of_data) => {
526 specificity.class_like_selectors += 1;
533 let mut max: u32 = 0;
534 for selector in nth_of_data.selectors.iter() {
535 max = selector.specificity().max(max);
536 }
537 specificity.add(Specificity::from_u32(max));
538 }
539 C::Negation(_) | C::Is(_) | C::Any { .. } => {
540 let list: &[GenericSelector<Impl>] = match simple_selector {
546 C::Negation(list) => list,
547 C::Is(list) => list,
548 C::Any { selectors, .. } => selectors,
549 _ => unreachable!(),
550 };
551 let mut max: u32 = 0;
552 for selector in list {
553 max = selector.specificity().max(max);
554 }
555 specificity.add(Specificity::from_u32(max));
556 }
557 C::Where(_)
558 | C::Has(_)
559 | C::ExplicitUniversalType
560 | C::ExplicitAnyNamespace
561 | C::ExplicitNoNamespace
562 | C::DefaultNamespace(_)
563 | C::Namespace { .. } => {
564 }
566 C::Nesting => {
567 }
569 }
570}
571
572fn parse_selector<Impl: BunSelectorImpl>(
577 parser: &mut SelectorParser,
578 input: &mut CssParser,
579 state: &mut SelectorParsingState,
580 nesting_requirement: NestingRequirement,
581) -> CResult<GenericSelector<Impl>> {
582 if nesting_requirement == NestingRequirement::Prefixed {
583 let parser_state = input.state();
584 if !input.expect_delim(b'&').is_ok() {
585 return Err(input.new_custom_error(
586 SelectorParseErrorKind::MissingNestingPrefix.into_default_parser_error(),
587 ));
588 }
589 input.reset(&parser_state);
590 }
591
592 let mut builder = SelectorBuilder::<Impl>::init_in(ArenaPtr::new(input.arena()));
593
594 'outer_loop: loop {
595 let empty = parse_compound_selector::<Impl>(parser, state, input, &mut builder)?;
597 if empty {
598 let kind: SelectorParseErrorKind = if builder.has_combinators() {
599 SelectorParseErrorKind::DanglingCombinator
600 } else {
601 SelectorParseErrorKind::EmptySelector
602 };
603 return Err(input.new_custom_error(kind.into_default_parser_error()));
604 }
605
606 if state.after_any_pseudo() {
607 let source_location = input.current_source_location();
608 if let Ok(next) = input.next() {
609 return Err(source_location.new_custom_error(
610 SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(next.clone())
611 .into_default_parser_error(),
612 ));
613 }
614 break;
615 }
616
617 let combinator: Combinator;
619 let mut any_whitespace = false;
620 loop {
621 let before_this_token = input.state();
622 let tok: &Token = match input.next_including_whitespace() {
623 Ok(vv) => vv,
624 Err(_) => break 'outer_loop,
625 };
626 match tok {
627 Token::Whitespace(_) => {
628 any_whitespace = true;
629 continue;
630 }
631 Token::Delim(d) => match u8::try_from(*d).ok() {
634 Some(b'>') => {
635 if parser.deep_combinator_enabled()
636 && input
637 .try_parse(|i: &mut CssParser| -> CResult<()> {
638 i.expect_delim(b'>')?;
639 i.expect_delim(b'>')
640 })
641 .is_ok()
642 {
643 combinator = Combinator::DeepDescendant;
644 } else {
645 combinator = Combinator::Child;
646 }
647 break;
648 }
649 Some(b'+') => {
650 combinator = Combinator::NextSibling;
651 break;
652 }
653 Some(b'~') => {
654 combinator = Combinator::LaterSibling;
655 break;
656 }
657 Some(b'/') => {
658 if parser.deep_combinator_enabled() {
659 if input
660 .try_parse(|i: &mut CssParser| -> CResult<()> {
661 i.expect_ident_matching(b"deep")?;
662 i.expect_delim(b'/')
663 })
664 .is_ok()
665 {
666 combinator = Combinator::Deep;
667 break;
668 } else {
669 break 'outer_loop;
670 }
671 }
672 }
673 _ => {}
674 },
675 _ => {}
676 }
677
678 input.reset(&before_this_token);
679
680 if any_whitespace {
681 combinator = Combinator::Descendant;
682 break;
683 } else {
684 break 'outer_loop;
685 }
686 }
687
688 if !state.allows_combinators() {
689 return Err(input.new_custom_error(
690 SelectorParseErrorKind::InvalidState.into_default_parser_error(),
691 ));
692 }
693
694 builder.push_combinator(combinator);
695 }
696
697 if !state.contains(SelectorParsingState::AFTER_NESTING) {
698 match nesting_requirement {
699 NestingRequirement::Implicit => {
700 builder.add_nesting_prefix();
701 }
702 NestingRequirement::Contained | NestingRequirement::Prefixed => {
703 return Err(input.new_custom_error(
704 SelectorParseErrorKind::MissingNestingSelector.into_default_parser_error(),
705 ));
706 }
707 _ => {}
708 }
709 }
710
711 let has_pseudo_element = state.contains(SelectorParsingState::AFTER_PSEUDO_ELEMENT)
712 || state.contains(SelectorParsingState::AFTER_UNKNOWN_PSEUDO_ELEMENT);
713 let slotted = state.contains(SelectorParsingState::AFTER_SLOTTED);
714 let part = state.contains(SelectorParsingState::AFTER_PART);
715 let result = builder.build(has_pseudo_element, slotted, part);
716 Ok(GenericSelector {
717 specificity_and_flags: result.specificity_and_flags,
718 components: result.components,
719 })
720}
721
722fn parse_compound_selector<Impl: BunSelectorImpl>(
729 parser: &mut SelectorParser,
730 state: &mut SelectorParsingState,
731 input: &mut CssParser,
732 builder: &mut SelectorBuilder<Impl>,
733) -> CResult<bool> {
734 input.skip_whitespace();
735
736 let mut empty: bool = true;
737 if parser.is_nesting_allowed() && input.try_parse(|i| i.expect_delim(b'&')).is_ok() {
738 state.insert(SelectorParsingState::AFTER_NESTING);
739 builder.push_simple_selector(GenericComponent::Nesting);
740 empty = false;
741 }
742
743 if parse_type_selector::<Impl>(parser, input, *state, builder).is_ok() {
744 empty = false;
748 }
749
750 loop {
751 let result: SimpleSelectorParseResult<Impl> = {
752 let ret = parse_one_simple_selector::<Impl>(parser, input, state)?;
753 match ret {
754 Some(result) => result,
755 None => break,
756 }
757 };
758
759 if empty {
760 if let Some(url) = parser.default_namespace() {
761 let ignore_default_ns = state
789 .contains(SelectorParsingState::SKIP_DEFAULT_NAMESPACE)
790 || matches!(
791 result,
792 SimpleSelectorParseResult::SimpleSelector(GenericComponent::Host(_))
793 );
794 if !ignore_default_ns {
795 builder.push_simple_selector(GenericComponent::DefaultNamespace(url));
796 }
797 }
798 }
799
800 empty = false;
801
802 match result {
803 SimpleSelectorParseResult::SimpleSelector(s) => {
804 builder.push_simple_selector(s);
805 }
806 SimpleSelectorParseResult::PartPseudo(selector) => {
807 state.insert(SelectorParsingState::AFTER_PART);
808 builder.push_combinator(Combinator::Part);
809 builder.push_simple_selector(GenericComponent::Part(selector));
810 }
811 SimpleSelectorParseResult::SlottedPseudo(selector) => {
812 state.insert(SelectorParsingState::AFTER_SLOTTED);
813 builder.push_combinator(Combinator::SlotAssignment);
814 builder.push_simple_selector(GenericComponent::Slotted(selector));
815 }
816 SimpleSelectorParseResult::PseudoElement(p) => {
817 if !p.is_unknown() {
818 state.insert(SelectorParsingState::AFTER_PSEUDO_ELEMENT);
819 builder.push_combinator(Combinator::PseudoElement);
820 } else {
821 state.insert(SelectorParsingState::AFTER_UNKNOWN_PSEUDO_ELEMENT);
822 }
823
824 if !p.accepts_state_pseudo_classes() {
825 state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
826 }
827
828 if p.is_webkit_scrollbar() {
829 state.insert(SelectorParsingState::AFTER_WEBKIT_SCROLLBAR);
830 }
831
832 if p.is_view_transition() {
833 state.insert(SelectorParsingState::AFTER_VIEW_TRANSITION);
834 }
835
836 builder.push_simple_selector(GenericComponent::PseudoElement(p));
837 }
838 }
839 }
840
841 Ok(empty)
842}
843
844fn parse_relative_selector<Impl: BunSelectorImpl>(
845 parser: &mut SelectorParser,
846 input: &mut CssParser,
847 state: &mut SelectorParsingState,
848 nesting_requirement_: NestingRequirement,
849) -> CResult<GenericSelector<Impl>> {
850 let mut nesting_requirement = nesting_requirement_;
852 let s = input.state();
853
854 let combinator: Option<Combinator> = 'combinator: {
855 let tok = input.next()?;
856 if let Token::Delim(c) = tok {
857 match u8::try_from(*c).ok() {
858 Some(b'>') => break 'combinator Some(Combinator::Child),
859 Some(b'+') => break 'combinator Some(Combinator::NextSibling),
860 Some(b'~') => break 'combinator Some(Combinator::LaterSibling),
861 _ => {}
862 }
863 }
864 input.reset(&s);
865 None
866 };
867
868 let scope: GenericComponent<Impl> = if nesting_requirement == NestingRequirement::Implicit {
869 GenericComponent::Nesting
870 } else {
871 GenericComponent::Scope
872 };
873
874 if combinator.is_some() {
875 nesting_requirement = NestingRequirement::None;
876 }
877
878 let mut selector = parse_selector::<Impl>(parser, input, state, nesting_requirement)?;
879 if let Some(wombo_combo) = combinator {
880 selector
882 .components
883 .push(GenericComponent::Combinator(wombo_combo));
884 selector.components.push(scope);
886 }
887
888 Ok(selector)
889}
890
891pub fn valid_selector_parser<T>() {
894 }
897
898pub use css::css_properties::text::Direction;
905
906#[derive(Clone, CssEql, CssHash)]
911pub enum PseudoClass {
912 Lang {
915 languages: PlainVec2<Str>,
917 },
919 Dir {
921 direction: Direction,
923 },
924
925 Hover,
928 Active,
930 Focus,
932 FocusVisible,
934 FocusWithin,
936
937 Current,
940 Past,
942 Future,
944
945 Playing,
948 Paused,
950 Seeking,
952 Buffering,
954 Stalled,
956 Muted,
958 VolumeLocked,
960
961 Fullscreen(css::VendorPrefix),
963
964 Open,
967 Closed,
969 Modal,
971 PictureInPicture,
973
974 PopoverOpen,
977
978 Defined,
980
981 AnyLink(css::VendorPrefix),
984 Link,
986 LocalLink,
988 Target,
990 TargetWithin,
992 Visited,
994
995 Enabled,
998 Disabled,
1000 ReadOnly(css::VendorPrefix),
1002 ReadWrite(css::VendorPrefix),
1004 PlaceholderShown(css::VendorPrefix),
1006 Default,
1008 Checked,
1010 Indeterminate,
1012 Blank,
1014 Valid,
1016 Invalid,
1018 InRange,
1020 OutOfRange,
1022 Required,
1024 Optional,
1026 UserValid,
1028 UserInvalid,
1030
1031 Autofill(css::VendorPrefix),
1033
1034 Local {
1037 selector: Box<Selector>,
1039 },
1040 Global {
1042 selector: Box<Selector>,
1044 },
1045
1046 WebkitScrollbar(WebKitScrollbarPseudoClass),
1049 Custom {
1051 name: Str,
1053 },
1054 CustomFunction {
1056 name: Str,
1058 arguments: TokenList,
1060 },
1061}
1062
1063impl PseudoClass {
1064 pub fn is_equivalent(&self, other: &PseudoClass) -> bool {
1065 use PseudoClass as P;
1066 if matches!(self, P::Fullscreen(_)) && matches!(other, P::Fullscreen(_)) {
1067 return true;
1068 }
1069 if matches!(self, P::AnyLink(_)) && matches!(other, P::AnyLink(_)) {
1070 return true;
1071 }
1072 if matches!(self, P::ReadOnly(_)) && matches!(other, P::ReadOnly(_)) {
1073 return true;
1074 }
1075 if matches!(self, P::ReadWrite(_)) && matches!(other, P::ReadWrite(_)) {
1076 return true;
1077 }
1078 if matches!(self, P::PlaceholderShown(_)) && matches!(other, P::PlaceholderShown(_)) {
1079 return true;
1080 }
1081 if matches!(self, P::Autofill(_)) && matches!(other, P::Autofill(_)) {
1082 return true;
1083 }
1084 self.eql(other)
1085 }
1086
1087 pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
1088 serialize::serialize_pseudo_class(self, dest, None)
1095 }
1096
1097 pub fn deep_clone(&self) -> Self {
1102 self.clone()
1103 }
1104
1105 pub fn get_prefix(&self) -> css::VendorPrefix {
1106 use PseudoClass as P;
1107 match self {
1108 P::Fullscreen(p)
1109 | P::AnyLink(p)
1110 | P::ReadOnly(p)
1111 | P::ReadWrite(p)
1112 | P::PlaceholderShown(p)
1113 | P::Autofill(p) => *p,
1114 _ => css::VendorPrefix::empty(),
1115 }
1116 }
1117
1118 pub fn get_necessary_prefixes(&mut self, targets: &css::targets::Targets) -> css::VendorPrefix {
1119 use PseudoClass as P;
1120 use css::prefixes::Feature as F;
1121 let (p, feature): (&mut css::VendorPrefix, F) = match self {
1122 P::Fullscreen(p) => (p, F::PseudoClassFullscreen),
1123 P::AnyLink(p) => (p, F::PseudoClassAnyLink),
1124 P::ReadOnly(p) => (p, F::PseudoClassReadOnly),
1125 P::ReadWrite(p) => (p, F::PseudoClassReadWrite),
1126 P::PlaceholderShown(p) => (p, F::PseudoClassPlaceholderShown),
1127 P::Autofill(p) => (p, F::PseudoClassAutofill),
1128 _ => return css::VendorPrefix::empty(),
1129 };
1130 *p = targets.prefixes(*p, feature);
1131 *p
1132 }
1133
1134 pub fn is_user_action_state(&self) -> bool {
1135 use PseudoClass as P;
1136 matches!(
1137 self,
1138 P::Active | P::Hover | P::Focus | P::FocusWithin | P::FocusVisible
1139 )
1140 }
1141
1142 pub fn is_valid_before_webkit_scrollbar(&self) -> bool {
1143 !matches!(self, PseudoClass::WebkitScrollbar(_))
1144 }
1145
1146 pub fn is_valid_after_webkit_scrollbar(&self) -> bool {
1147 use PseudoClass as P;
1148 matches!(
1149 self,
1150 P::WebkitScrollbar(_) | P::Enabled | P::Disabled | P::Hover | P::Active
1151 )
1152 }
1153}
1154
1155#[derive(Clone, Copy, PartialEq, Eq, Hash, CssEql, CssHash, css::generics::DeepClone)]
1157pub enum WebKitScrollbarPseudoClass {
1158 Horizontal,
1160 Vertical,
1162 Decrement,
1164 Increment,
1166 Start,
1168 End,
1170 DoubleButton,
1172 SingleButton,
1174 NoButton,
1176 CornerPresent,
1178 WindowInactive,
1180}
1181
1182#[derive(Clone, Copy, PartialEq, Eq, Hash, CssHash)]
1184pub enum WebKitScrollbarPseudoElement {
1185 Scrollbar,
1187 Button,
1189 Track,
1191 TrackPiece,
1193 Thumb,
1195 Corner,
1197 Resizer,
1199}
1200
1201impl WebKitScrollbarPseudoElement {
1202 #[inline]
1203 pub fn eql(self, rhs: Self) -> bool {
1204 self == rhs
1205 }
1206 }
1208
1209pub struct SelectorParser<'a> {
1214 pub is_nesting_allowed: bool,
1215 pub options: &'a ParserOptions<'a>,
1216 }
1219
1220pub type SelectorParserImpl = impl_::Selectors;
1225
1226impl<'a> SelectorParser<'a> {
1227 pub fn new_local_identifier(
1228 &mut self,
1229 input: &mut CssParser,
1230 tag: css::CssRefTag,
1231 raw: Str,
1232 loc: usize,
1233 ) -> <impl_::Selectors as SelectorImpl>::LocalIdentifier {
1234 if input.flags.css_modules() {
1240 return <impl_::Selectors as SelectorImpl>::LocalIdentifier::from_ref(
1241 input.add_symbol_for_name(
1242 raw,
1243 tag,
1244 bun_ast::Loc {
1245 start: i32::try_from(loc).expect("int cast"),
1246 },
1247 ),
1248 crate::values::ident::debug_ident(raw, input.arena()),
1249 );
1250 }
1251 let _ = (input, tag, loc);
1252 <impl_::Selectors as SelectorImpl>::LocalIdentifier::from_ident(Ident {
1253 v: std::ptr::from_ref::<[u8]>(raw),
1254 })
1255 }
1256
1257 pub fn namespace_for_prefix(&mut self, prefix: Ident) -> Option<Str> {
1258 let _ = self;
1259 Some(unsafe { crate::arena_str(prefix.v) })
1262 }
1263
1264 pub fn parse_functional_pseudo_element(
1265 &mut self,
1266 name: Str,
1267 input: &mut CssParser,
1268 ) -> CResult<PseudoElement> {
1269 match name.len() {
1281 3 if name == b"cue" => {
1282 return Ok(PseudoElement::CueFunction {
1283 selector: Box::new(Selector::parse(self, input)?),
1284 });
1285 }
1286 10 if name == b"cue-region" => {
1287 return Ok(PseudoElement::CueRegionFunction {
1288 selector: Box::new(Selector::parse(self, input)?),
1289 });
1290 }
1291 19 => match name {
1292 b"view-transition-old" => {
1293 return Ok(PseudoElement::ViewTransitionOld {
1294 part_name: ViewTransitionPartName::parse(input)?,
1295 });
1296 }
1297 b"view-transition-new" => {
1298 return Ok(PseudoElement::ViewTransitionNew {
1299 part_name: ViewTransitionPartName::parse(input)?,
1300 });
1301 }
1302 _ => {}
1303 },
1304 21 if name == b"view-transition-group" => {
1305 return Ok(PseudoElement::ViewTransitionGroup {
1306 part_name: ViewTransitionPartName::parse(input)?,
1307 });
1308 }
1309 26 if name == b"view-transition-image-pair" => {
1310 return Ok(PseudoElement::ViewTransitionImagePair {
1311 part_name: ViewTransitionPartName::parse(input)?,
1312 });
1313 }
1314 _ => {}
1315 }
1316 if !strings::starts_with(name, b"-") {
1317 self.options.warn(
1318 &input.new_custom_error(
1319 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)
1320 .into_default_parser_error(),
1321 ),
1322 );
1323 }
1324
1325 {
1331 let mut args: PlainVec2<css::css_properties::custom::TokenOrValue> = PlainVec2::new();
1332 TokenList::parse_raw(input, &mut args, self.options, 0)?;
1333 return Ok(PseudoElement::CustomFunction {
1334 name,
1335 arguments: TokenList { v: args },
1336 });
1337 }
1338 }
1339
1340 fn parse_is_and_where(&self) -> bool {
1341 let _ = self;
1342 true
1343 }
1344
1345 fn parse_any_prefix(&self, name: &[u8]) -> Option<css::VendorPrefix> {
1347 crate::match_ignore_ascii_case! { name, {
1348 b"-webkit-any" => Some(css::VendorPrefix::WEBKIT),
1349 b"-moz-any" => Some(css::VendorPrefix::MOZ),
1350 _ => None,
1351 }}
1352 }
1353
1354 pub fn parse_non_ts_pseudo_class(
1355 &mut self,
1356 loc: css::SourceLocation,
1357 name: Str,
1358 ) -> CResult<PseudoClass> {
1359 let pseudo_class: PseudoClass = 'pseudo_class: {
1361 if let Some(pseudo) = lookup_non_ts_pseudo_class(name) {
1365 break 'pseudo_class pseudo;
1366 }
1367 if strings::starts_with_char(name, b'_') {
1368 self.options.warn(&loc.new_custom_error(
1369 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
1370 ));
1371 } else if (self.options.css_modules.is_some()
1372 && strings::eql_case_insensitive_ascii_check_length(name, b"local"))
1373 || strings::eql_case_insensitive_ascii_check_length(name, b"global")
1374 {
1375 return Err(
1376 loc.new_custom_error(SelectorParseErrorKind::AmbiguousCssModuleClass(name))
1377 );
1378 }
1379 return Ok(PseudoClass::Custom { name });
1380 };
1381
1382 Ok(pseudo_class)
1383 }
1384
1385 pub fn parse_host(&mut self) -> bool {
1386 true
1387 }
1388
1389 pub fn parse_non_ts_functional_pseudo_class(
1390 &mut self,
1391 name: Str,
1392 parser: &mut CssParser,
1393 ) -> CResult<PseudoClass> {
1394 let pseudo_class = crate::match_ignore_ascii_case! { name, {
1395 b"lang" => {
1396 let languages = parser.parse_comma_separated(|p| -> CResult<Str> {
1401 let loc = p.current_source_location();
1402 let tok = p.next()?.clone();
1403 match tok {
1404 Token::Ident(i) | Token::QuotedString(i) => Ok(i),
1405 t => Err(loc.new_unexpected_token_error(t)),
1406 }
1407 })?;
1408 return Ok(PseudoClass::Lang { languages });
1409 },
1410 b"dir" => PseudoClass::Dir {
1411 direction: Direction::parse(parser)?,
1412 },
1413 b"local" if self.options.css_modules.is_some() => PseudoClass::Local {
1414 selector: Box::new(Selector::parse(self, parser)?),
1415 },
1416 b"global" if self.options.css_modules.is_some() => PseudoClass::Global {
1417 selector: Box::new(Selector::parse(self, parser)?),
1418 },
1419 _ => {
1420 if !strings::starts_with_char(name, b'-') {
1421 self.options.warn(
1422 &parser.new_custom_error(
1423 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)
1424 .into_default_parser_error(),
1425 ),
1426 );
1427 }
1428 let mut args: PlainVec2<css::css_properties::custom::TokenOrValue> = PlainVec2::new();
1429 css::TokenListFns::parse_raw(parser, &mut args, self.options, 0)?;
1430 PseudoClass::CustomFunction {
1431 name,
1432 arguments: TokenList { v: args },
1433 }
1434 },
1435 }};
1436
1437 Ok(pseudo_class)
1438 }
1439
1440 pub fn is_nesting_allowed(&self) -> bool {
1441 self.is_nesting_allowed
1442 }
1443
1444 pub fn deep_combinator_enabled(&self) -> bool {
1445 self.options
1446 .flags
1447 .contains(css::ParserFlags::DEEP_SELECTOR_COMBINATOR)
1448 }
1449
1450 pub fn default_namespace(&self) -> Option<<impl_::Selectors as SelectorImpl>::NamespaceUrl> {
1451 let _ = self;
1452 None
1453 }
1454
1455 pub fn parse_part(&self) -> bool {
1456 let _ = self;
1457 true
1458 }
1459
1460 pub fn parse_slotted(&self) -> bool {
1461 let _ = self;
1462 true
1463 }
1464
1465 fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
1467 let _ = self;
1468 ParseErrorRecovery::IgnoreInvalidSelector
1469 }
1470
1471 pub fn parse_pseudo_element(
1472 &mut self,
1473 loc: css::SourceLocation,
1474 name: Str,
1475 ) -> CResult<PseudoElement> {
1476 let pseudo_element = lookup_pseudo_element(name).unwrap_or_else(|| {
1478 if !strings::starts_with_char(name, b'-') {
1479 self.options.warn(&loc.new_custom_error(
1480 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name),
1481 ));
1482 }
1483 PseudoElement::Custom { name }
1484 });
1485
1486 Ok(pseudo_element)
1487 }
1488}
1489
1490fn lookup_non_ts_pseudo_class(name: &[u8]) -> Option<PseudoClass> {
1493 use PseudoClass as P;
1494 use WebKitScrollbarPseudoClass as WS;
1495 use css::VendorPrefix as VP;
1496 Some(crate::match_ignore_ascii_case! { name, {
1497 b"hover" => P::Hover,
1499 b"active" => P::Active,
1500 b"focus" => P::Focus,
1501 b"focus-visible" => P::FocusVisible,
1502 b"focus-within" => P::FocusWithin,
1503 b"current" => P::Current,
1505 b"past" => P::Past,
1506 b"future" => P::Future,
1507 b"playing" => P::Playing,
1509 b"paused" => P::Paused,
1510 b"seeking" => P::Seeking,
1511 b"buffering" => P::Buffering,
1512 b"stalled" => P::Stalled,
1513 b"muted" => P::Muted,
1514 b"volume-locked" => P::VolumeLocked,
1515 b"fullscreen" => P::Fullscreen(VP::NONE),
1517 b"-webkit-full-screen" => P::Fullscreen(VP::WEBKIT),
1518 b"-moz-full-screen" => P::Fullscreen(VP::MOZ),
1519 b"-ms-fullscreen" => P::Fullscreen(VP::MS),
1520 b"open" => P::Open,
1522 b"closed" => P::Closed,
1523 b"modal" => P::Modal,
1524 b"picture-in-picture" => P::PictureInPicture,
1525 b"popover-open" => P::PopoverOpen,
1527 b"defined" => P::Defined,
1529 b"any-link" => P::AnyLink(VP::NONE),
1531 b"-webkit-any-link" => P::AnyLink(VP::WEBKIT),
1532 b"-moz-any-link" => P::AnyLink(VP::MOZ),
1533 b"link" => P::Link,
1534 b"local-link" => P::LocalLink,
1535 b"target" => P::Target,
1536 b"target-within" => P::TargetWithin,
1537 b"visited" => P::Visited,
1538 b"enabled" => P::Enabled,
1540 b"disabled" => P::Disabled,
1541 b"read-only" => P::ReadOnly(VP::NONE),
1542 b"-moz-read-only" => P::ReadOnly(VP::MOZ),
1543 b"read-write" => P::ReadWrite(VP::NONE),
1544 b"-moz-read-write" => P::ReadWrite(VP::MOZ),
1545 b"placeholder-shown" => P::PlaceholderShown(VP::NONE),
1546 b"-moz-placeholder-shown" => P::PlaceholderShown(VP::MOZ),
1547 b"-ms-placeholder-shown" => P::PlaceholderShown(VP::MS),
1548 b"default" => P::Default,
1549 b"checked" => P::Checked,
1550 b"indeterminate" => P::Indeterminate,
1551 b"blank" => P::Blank,
1552 b"valid" => P::Valid,
1553 b"invalid" => P::Invalid,
1554 b"in-range" => P::InRange,
1555 b"out-of-range" => P::OutOfRange,
1556 b"required" => P::Required,
1557 b"optional" => P::Optional,
1558 b"user-valid" => P::UserValid,
1559 b"user-invalid" => P::UserInvalid,
1560 b"autofill" => P::Autofill(VP::NONE),
1562 b"-webkit-autofill" => P::Autofill(VP::WEBKIT),
1563 b"-o-autofill" => P::Autofill(VP::O),
1564 b"horizontal" => P::WebkitScrollbar(WS::Horizontal),
1566 b"vertical" => P::WebkitScrollbar(WS::Vertical),
1567 b"decrement" => P::WebkitScrollbar(WS::Decrement),
1568 b"increment" => P::WebkitScrollbar(WS::Increment),
1569 b"start" => P::WebkitScrollbar(WS::Start),
1570 b"end" => P::WebkitScrollbar(WS::End),
1571 b"double-button" => P::WebkitScrollbar(WS::DoubleButton),
1572 b"single-button" => P::WebkitScrollbar(WS::SingleButton),
1573 b"no-button" => P::WebkitScrollbar(WS::NoButton),
1574 b"corner-present" => P::WebkitScrollbar(WS::CornerPresent),
1575 b"window-inactive" => P::WebkitScrollbar(WS::WindowInactive),
1576 _ => return None,
1577 } })
1578}
1579
1580fn lookup_pseudo_element(name: &[u8]) -> Option<PseudoElement> {
1583 use PseudoElement as PE;
1584 use WebKitScrollbarPseudoElement as WS;
1585 use css::VendorPrefix as VP;
1586 Some(crate::match_ignore_ascii_case! { name, {
1587 b"before" => PE::Before,
1588 b"after" => PE::After,
1589 b"first-line" => PE::FirstLine,
1590 b"first-letter" => PE::FirstLetter,
1591 b"cue" => PE::Cue,
1592 b"cue-region" => PE::CueRegion,
1593 b"selection" => PE::Selection(VP::NONE),
1594 b"-moz-selection" => PE::Selection(VP::MOZ),
1595 b"placeholder" => PE::Placeholder(VP::NONE),
1596 b"-webkit-input-placeholder" => PE::Placeholder(VP::WEBKIT),
1597 b"-moz-placeholder" => PE::Placeholder(VP::MOZ),
1598 b"-ms-input-placeholder" => PE::Placeholder(VP::MS),
1599 b"marker" => PE::Marker,
1600 b"backdrop" => PE::Backdrop(VP::NONE),
1601 b"-webkit-backdrop" => PE::Backdrop(VP::WEBKIT),
1602 b"file-selector-button" => PE::FileSelectorButton(VP::NONE),
1603 b"-webkit-file-upload-button" => PE::FileSelectorButton(VP::WEBKIT),
1604 b"-ms-browse" => PE::FileSelectorButton(VP::MS),
1605 b"-webkit-scrollbar" => PE::WebkitScrollbar(WS::Scrollbar),
1606 b"-webkit-scrollbar-button" => PE::WebkitScrollbar(WS::Button),
1607 b"-webkit-scrollbar-track" => PE::WebkitScrollbar(WS::Track),
1608 b"-webkit-scrollbar-track-piece" => PE::WebkitScrollbar(WS::TrackPiece),
1609 b"-webkit-scrollbar-thumb" => PE::WebkitScrollbar(WS::Thumb),
1610 b"-webkit-scrollbar-corner" => PE::WebkitScrollbar(WS::Corner),
1611 b"-webkit-resizer" => PE::WebkitScrollbar(WS::Resizer),
1612 b"view-transition" => PE::ViewTransition,
1613 _ => return None,
1614 } })
1615}
1616
1617pub struct GenericSelectorList<Impl: SelectorImpl> {
1622 pub v: SmallList<GenericSelector<Impl>, 1>,
1624}
1625
1626impl<Impl: SelectorImpl> Default for GenericSelectorList<Impl> {
1627 fn default() -> Self {
1628 Self {
1629 v: SmallList::default(),
1630 }
1631 }
1632}
1633
1634impl<Impl: SelectorImpl> Default for GenericSelector<Impl> {
1635 fn default() -> Self {
1636 Self {
1637 specificity_and_flags: SpecificityAndFlags {
1638 specificity: 0,
1639 flags: SelectorFlags::empty(),
1640 },
1641 components: ArenaVec::new_in(ArenaPtr::global()),
1642 }
1643 }
1644}
1645
1646impl<Impl: SelectorImpl> GenericSelectorList<Impl> {
1647 #[inline]
1651 pub fn into_boxed_selectors(self) -> Box<[GenericSelector<Impl>]> {
1652 small_list_into_box(self.v)
1653 }
1654}
1655
1656pub struct SelectorListDebugFmt<'a, Impl: SelectorImpl>(pub &'a GenericSelectorList<Impl>);
1658
1659impl<'a, Impl: BunSelectorImpl> fmt::Display for SelectorListDebugFmt<'a, Impl> {
1660 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1661 if !cfg!(debug_assertions) {
1662 return Ok(());
1663 }
1664 writeln!(f, "SelectorList[")?;
1665 let last = (self.0.v.len() as usize).saturating_sub(1);
1666 for (i, sel) in self.0.v.slice().iter().enumerate() {
1667 if i != last {
1668 writeln!(f, " {}", sel.debug())?;
1669 } else {
1670 writeln!(f, " {},", sel.debug())?;
1671 }
1672 }
1673 writeln!(f, "]")
1674 }
1675}
1676
1677impl<Impl: BunSelectorImpl> GenericSelectorList<Impl> {
1678 pub fn debug(&self) -> SelectorListDebugFmt<'_, Impl> {
1679 SelectorListDebugFmt(self)
1680 }
1681
1682 pub fn any_has_pseudo_element(&self) -> bool {
1683 for sel in self.v.slice() {
1684 if sel.has_pseudo_element() {
1685 return true;
1686 }
1687 }
1688 false
1689 }
1690
1691 pub fn specifities_all_equal(&self) -> bool {
1692 if self.v.len() == 0 {
1693 return true;
1694 }
1695 if self.v.len() == 1 {
1696 return true;
1697 }
1698 let value = self.v.at(0).specificity();
1699 for sel in &self.v.slice()[1..] {
1700 if sel.specificity() != value {
1701 return false;
1702 }
1703 }
1704 true
1705 }
1706
1707 #[deprecated = "use serializer::serialize_selector_list()"]
1710 pub fn to_css(&self, _dest: &mut Printer) -> Result<(), PrintErr> {
1711 unreachable!("use serializer::serialize_selector_list()");
1712 }
1713
1714 pub fn parse_with_options(input: &mut CssParser, options: &ParserOptions) -> CResult<Self> {
1715 let mut parser = SelectorParser {
1716 options,
1717 is_nesting_allowed: true,
1718 };
1719 Self::parse(
1720 &mut parser,
1721 input,
1722 ParseErrorRecovery::DiscardList,
1723 NestingRequirement::None,
1724 )
1725 }
1726
1727 pub fn parse(
1728 parser: &mut SelectorParser,
1729 input: &mut CssParser,
1730 error_recovery: ParseErrorRecovery,
1731 nesting_requirement: NestingRequirement,
1732 ) -> CResult<Self> {
1733 let mut state = SelectorParsingState::empty();
1734 Self::parse_with_state(
1735 parser,
1736 input,
1737 &mut state,
1738 error_recovery,
1739 nesting_requirement,
1740 )
1741 }
1742
1743 pub fn parse_relative(
1744 parser: &mut SelectorParser,
1745 input: &mut CssParser,
1746 error_recovery: ParseErrorRecovery,
1747 nesting_requirement: NestingRequirement,
1748 ) -> CResult<Self> {
1749 let mut state = SelectorParsingState::empty();
1750 Self::parse_relative_with_state(
1751 parser,
1752 input,
1753 &mut state,
1754 error_recovery,
1755 nesting_requirement,
1756 )
1757 }
1758
1759 pub fn parse_with_state(
1760 parser: &mut SelectorParser,
1761 input: &mut CssParser,
1762 state: &mut SelectorParsingState,
1763 recovery: ParseErrorRecovery,
1764 nesting_requirement: NestingRequirement,
1765 ) -> CResult<Self> {
1766 let original_state = *state;
1767 let mut values: SmallList<GenericSelector<Impl>, 1> = SmallList::default();
1769
1770 loop {
1771 let mut saw_nesting = false;
1775 let selector =
1776 input.parse_until_before(css::Delimiters::COMMA, |input2: &mut CssParser| {
1777 let mut selector_state = original_state;
1778 let result = parse_selector::<Impl>(
1779 parser,
1780 input2,
1781 &mut selector_state,
1782 nesting_requirement,
1783 );
1784 if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
1785 saw_nesting = true;
1786 }
1787 result
1788 });
1789 if saw_nesting {
1790 state.insert(SelectorParsingState::AFTER_NESTING);
1791 }
1792
1793 let was_ok = selector.is_ok();
1794 match selector {
1795 Ok(sel) => {
1796 values.append(sel);
1797 }
1799 Err(e) => match recovery {
1800 ParseErrorRecovery::DiscardList => return Err(e),
1801 ParseErrorRecovery::IgnoreInvalidSelector => {}
1802 },
1803 }
1804
1805 if let Ok(tok) = input.next() {
1806 if matches!(tok, Token::Comma) {
1807 continue;
1808 }
1809 debug_assert!(!was_ok);
1811 }
1812 return Ok(Self { v: values });
1813 }
1814 }
1815
1816 pub fn parse_relative_with_state(
1819 parser: &mut SelectorParser,
1820 input: &mut CssParser,
1821 state: &mut SelectorParsingState,
1822 recovery: ParseErrorRecovery,
1823 nesting_requirement: NestingRequirement,
1824 ) -> CResult<Self> {
1825 let original_state = *state;
1826 let mut values: SmallList<GenericSelector<Impl>, 1> = SmallList::default();
1828
1829 loop {
1830 let mut saw_nesting = false;
1833 let selector =
1834 input.parse_until_before(css::Delimiters::COMMA, |input2: &mut CssParser| {
1835 let mut selector_state = original_state;
1836 let result = parse_relative_selector::<Impl>(
1837 parser,
1838 input2,
1839 &mut selector_state,
1840 nesting_requirement,
1841 );
1842 if selector_state.contains(SelectorParsingState::AFTER_NESTING) {
1843 saw_nesting = true;
1844 }
1845 result
1846 });
1847 if saw_nesting {
1848 state.insert(SelectorParsingState::AFTER_NESTING);
1849 }
1850
1851 let was_ok = selector.is_ok();
1852 match selector {
1853 Ok(sel) => {
1854 values.append(sel);
1855 }
1856 Err(e) => match recovery {
1857 ParseErrorRecovery::DiscardList => return Err(e),
1858 ParseErrorRecovery::IgnoreInvalidSelector => {}
1859 },
1860 }
1861
1862 if let Ok(tok) = input.next() {
1863 if matches!(tok, Token::Comma) {
1864 continue;
1865 }
1866 debug_assert!(!was_ok);
1868 }
1869 return Ok(Self { v: values });
1870 }
1871 }
1872
1873 pub fn from_selector(selector: GenericSelector<Impl>) -> Self {
1874 let mut result = Self::default();
1875 result.v.append(selector);
1876 result
1877 }
1878
1879 pub fn deep_clone(&self) -> Self {
1880 let mut v = SmallList::<GenericSelector<Impl>, 1>::init_capacity(self.v.len());
1881 for sel in self.v.slice() {
1882 v.append(sel.deep_clone());
1883 }
1884 Self { v }
1885 }
1886
1887 pub fn eql(&self, rhs: &Self) -> bool {
1888 eql_selector_slice(self.v.slice(), rhs.v.slice())
1889 }
1890
1891 pub fn hash(&self, hasher: &mut Wyhash) {
1892 hash_selector_slice(self.v.slice(), hasher);
1893 }
1894}
1895
1896impl<Impl: BunSelectorImpl> CssEql for GenericSelectorList<Impl> {
1897 #[inline]
1898 fn eql(&self, other: &Self) -> bool {
1899 self.eql(other)
1900 }
1901}
1902impl<Impl: BunSelectorImpl> CssHash for GenericSelectorList<Impl> {
1903 #[inline]
1904 fn hash(&self, hasher: &mut Wyhash) {
1905 self.hash(hasher)
1906 }
1907}
1908
1909#[derive(Clone)]
1929pub struct GenericSelector<Impl: SelectorImpl> {
1930 pub specificity_and_flags: SpecificityAndFlags,
1931 pub components: ArenaVec<GenericComponent<Impl>>,
1932}
1933
1934pub struct SelectorDebugFmt<'a, Impl: SelectorImpl>(pub &'a GenericSelector<Impl>);
1935
1936impl<'a, Impl: SelectorImpl> fmt::Display for SelectorDebugFmt<'a, Impl> {
1937 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1938 if !cfg!(debug_assertions) {
1939 return Ok(());
1940 }
1941 write!(f, "Selector(<{} components>)", self.0.components.len())
1946 }
1947}
1948
1949impl<Impl: BunSelectorImpl> GenericSelector<Impl> {
1950 pub fn debug(&self) -> SelectorDebugFmt<'_, Impl> {
1951 SelectorDebugFmt(self)
1952 }
1953
1954 pub fn parse(parser: &mut SelectorParser, input: &mut CssParser) -> CResult<Self> {
1956 let mut state = SelectorParsingState::empty();
1957 parse_selector::<Impl>(parser, input, &mut state, NestingRequirement::None)
1958 }
1959
1960 #[deprecated = "use serializer::serialize_selector()"]
1963 pub fn to_css(&self, _dest: &mut Printer) -> Result<(), PrintErr> {
1964 unreachable!("use serializer::serialize_selector()");
1965 }
1966
1967 pub fn append(&mut self, component: GenericComponent<Impl>) {
1968 let index = 'index: {
1969 for (i, comp) in self.components.iter().enumerate() {
1970 match comp {
1971 GenericComponent::Combinator(_) | GenericComponent::PseudoElement(_) => {
1972 break 'index i;
1973 }
1974 _ => {}
1975 }
1976 }
1977 self.components.len()
1978 };
1979 self.components.insert(index, component);
1980 }
1981
1982 pub fn deep_clone(&self) -> Self {
1983 let alloc = *self.components.allocator();
1984 let mut components = ArenaVec::with_capacity_in(self.components.len(), alloc);
1985 components.extend(self.components.iter().map(|c| c.deep_clone()));
1986 Self {
1987 specificity_and_flags: self.specificity_and_flags,
1988 components,
1989 }
1990 }
1991
1992 pub fn eql(&self, other: &Self) -> bool {
1993 self.specificity_and_flags.eql(&other.specificity_and_flags)
1994 && self.components.len() == other.components.len()
1995 && self
1996 .components
1997 .iter()
1998 .zip(other.components.iter())
1999 .all(|(a, b)| a.eql(b))
2000 }
2001
2002 pub fn has_combinator(&self) -> bool {
2003 for c in &self.components {
2004 if let GenericComponent::Combinator(comb) = c {
2005 if comb.is_tree_combinator() {
2006 return true;
2007 }
2008 }
2009 }
2010 false
2011 }
2012
2013 pub fn has_pseudo_element(&self) -> bool {
2014 self.specificity_and_flags.has_pseudo_element()
2015 }
2016
2017 pub fn len(&self) -> usize {
2019 self.components.len()
2020 }
2021
2022 pub fn from_component(component: GenericComponent<Impl>) -> Self {
2023 Self::from_component_in(component, ArenaPtr::global())
2024 }
2025
2026 pub fn from_component_in(component: GenericComponent<Impl>, alloc: ArenaPtr) -> Self {
2027 let mut builder = SelectorBuilder::<Impl>::init_in(alloc);
2028 if let Some(combinator) = component.as_combinator() {
2029 builder.push_combinator(combinator);
2030 } else {
2031 builder.push_simple_selector(component);
2032 }
2033 let result = builder.build(false, false, false);
2034 Self {
2035 specificity_and_flags: result.specificity_and_flags,
2036 components: result.components,
2037 }
2038 }
2039
2040 pub fn specificity(&self) -> u32 {
2041 self.specificity_and_flags.specificity
2042 }
2043
2044 pub fn parse_with_options(input: &mut CssParser, options: &ParserOptions) -> CResult<Self> {
2045 let mut selector_parser = SelectorParser {
2046 is_nesting_allowed: true,
2047 options,
2048 };
2049 Self::parse(&mut selector_parser, input)
2050 }
2051
2052 pub fn iter_raw_match_order(&self) -> RawMatchOrderIterator<'_, Impl> {
2053 RawMatchOrderIterator {
2054 slice: &self.components,
2055 i: 0,
2056 }
2057 }
2058
2059 pub fn iter_raw_parse_order_from(&self, offset: usize) -> RawParseOrderFromIter<'_, Impl> {
2063 RawParseOrderFromIter {
2064 slice: &self.components[0..self.components.len() - offset],
2065 i: 0,
2066 }
2067 }
2068
2069 pub fn hash(&self, hasher: &mut Wyhash) {
2070 self.specificity_and_flags.hash(hasher);
2071 for c in &self.components {
2072 c.hash(hasher);
2073 }
2074 }
2075}
2076
2077impl<Impl: BunSelectorImpl> CssEql for GenericSelector<Impl> {
2078 #[inline]
2079 fn eql(&self, other: &Self) -> bool {
2080 self.eql(other)
2081 }
2082}
2083impl<Impl: BunSelectorImpl> CssHash for GenericSelector<Impl> {
2084 #[inline]
2085 fn hash(&self, hasher: &mut Wyhash) {
2086 self.hash(hasher)
2087 }
2088}
2089
2090pub struct RawMatchOrderIterator<'a, Impl: SelectorImpl> {
2091 slice: &'a [GenericComponent<Impl>],
2092 i: usize,
2093}
2094
2095impl<'a, Impl: SelectorImpl> Iterator for RawMatchOrderIterator<'a, Impl> {
2096 type Item = &'a GenericComponent<Impl>;
2097 fn next(&mut self) -> Option<Self::Item> {
2098 if self.i >= self.slice.len() {
2099 return None;
2100 }
2101 let result = &self.slice[self.i];
2102 self.i += 1;
2103 Some(result)
2104 }
2105}
2106
2107pub struct RawParseOrderFromIter<'a, Impl: SelectorImpl> {
2108 slice: &'a [GenericComponent<Impl>],
2109 i: usize,
2110}
2111
2112impl<'a, Impl: SelectorImpl> Iterator for RawParseOrderFromIter<'a, Impl> {
2113 type Item = &'a GenericComponent<Impl>;
2114 fn next(&mut self) -> Option<Self::Item> {
2115 if !(self.i < self.slice.len()) {
2116 return None;
2117 }
2118 let result = &self.slice[self.slice.len() - 1 - self.i];
2119 self.i += 1;
2120 Some(result)
2121 }
2122}
2123
2124#[derive(Clone)]
2133pub enum GenericComponent<Impl: SelectorImpl> {
2134 Combinator(Combinator),
2135
2136 ExplicitAnyNamespace,
2137 ExplicitNoNamespace,
2138 DefaultNamespace(Impl::NamespaceUrl),
2139 Namespace {
2140 prefix: Impl::NamespacePrefix,
2141 url: Impl::NamespaceUrl,
2142 },
2143
2144 ExplicitUniversalType,
2145 LocalName(LocalName<Impl>),
2146
2147 Id(Impl::LocalIdentifier),
2148 Class(Impl::LocalIdentifier),
2149
2150 AttributeInNoNamespaceExists {
2151 local_name: Impl::LocalName,
2152 local_name_lower: Impl::LocalName,
2153 },
2154 AttributeInNoNamespace {
2156 local_name: Impl::LocalName,
2157 operator: attrs::AttrSelectorOperator,
2158 value: Impl::AttrValue,
2159 case_sensitivity: attrs::ParsedCaseSensitivity,
2160 never_matches: bool,
2161 },
2162 AttributeOther(Box<attrs::AttrSelectorWithOptionalNamespace<Impl>>),
2164
2165 Negation(Box<[GenericSelector<Impl>]>),
2167 Root,
2168 Empty,
2169 Scope,
2170 Nth(NthSelectorData),
2171 NthOf(NthOfSelectorData<Impl>),
2172 NonTsPseudoClass(Impl::NonTSPseudoClass),
2173 Slotted(GenericSelector<Impl>),
2185 Part(Box<[Impl::Identifier]>),
2188 Host(Option<GenericSelector<Impl>>),
2198 Where(Box<[GenericSelector<Impl>]>),
2205 Is(Box<[GenericSelector<Impl>]>),
2211 Any {
2212 vendor_prefix: Impl::VendorPrefix,
2213 selectors: Box<[GenericSelector<Impl>]>,
2214 },
2215 Has(Box<[GenericSelector<Impl>]>),
2219 PseudoElement(Impl::PseudoElement),
2221 Nesting,
2227}
2228
2229impl<Impl: BunSelectorImpl> GenericComponent<Impl> {
2230 pub fn is_locally_scoped(&self) -> bool {
2232 matches!(self, Self::Id(_) | Self::Class(_))
2233 }
2234
2235 pub fn as_class(&self) -> Option<&Impl::LocalIdentifier> {
2236 match self {
2237 Self::Class(v) => Some(v),
2238 _ => None,
2239 }
2240 }
2241
2242 pub fn deep_clone(&self) -> Self {
2243 use GenericComponent as C;
2247 match self {
2248 C::Combinator(c) => C::Combinator(*c),
2249 C::ExplicitAnyNamespace => C::ExplicitAnyNamespace,
2250 C::ExplicitNoNamespace => C::ExplicitNoNamespace,
2251 C::DefaultNamespace(u) => C::DefaultNamespace(*u),
2252 C::Namespace { prefix, url } => C::Namespace {
2253 prefix: *prefix,
2254 url: *url,
2255 },
2256 C::ExplicitUniversalType => C::ExplicitUniversalType,
2257 C::LocalName(ln) => C::LocalName(ln.deep_clone()),
2258 C::Id(i) => C::Id(*i),
2259 C::Class(i) => C::Class(*i),
2260 C::AttributeInNoNamespaceExists {
2261 local_name,
2262 local_name_lower,
2263 } => C::AttributeInNoNamespaceExists {
2264 local_name: *local_name,
2265 local_name_lower: *local_name_lower,
2266 },
2267 C::AttributeInNoNamespace {
2268 local_name,
2269 operator,
2270 value,
2271 case_sensitivity,
2272 never_matches,
2273 } => C::AttributeInNoNamespace {
2274 local_name: *local_name,
2275 operator: *operator,
2276 value: *value,
2277 case_sensitivity: *case_sensitivity,
2278 never_matches: *never_matches,
2279 },
2280 C::AttributeOther(a) => C::AttributeOther(Box::new(a.deep_clone())),
2281 C::Negation(s) => C::Negation(deep_clone_selector_slice(s)),
2282 C::Root => C::Root,
2283 C::Empty => C::Empty,
2284 C::Scope => C::Scope,
2285 C::Nth(n) => C::Nth(*n),
2286 C::NthOf(n) => C::NthOf(n.deep_clone()),
2287 C::NonTsPseudoClass(p) => C::NonTsPseudoClass(p.deep_clone()),
2288 C::Slotted(s) => C::Slotted(s.deep_clone()),
2289 C::Part(p) => C::Part(p.iter().cloned().collect()),
2290 C::Host(h) => C::Host(h.as_ref().map(|s| s.deep_clone())),
2291 C::Where(s) => C::Where(deep_clone_selector_slice(s)),
2292 C::Is(s) => C::Is(deep_clone_selector_slice(s)),
2293 C::Any {
2294 vendor_prefix,
2295 selectors,
2296 } => C::Any {
2297 vendor_prefix: *vendor_prefix,
2298 selectors: deep_clone_selector_slice(selectors),
2299 },
2300 C::Has(s) => C::Has(deep_clone_selector_slice(s)),
2301 C::PseudoElement(pe) => C::PseudoElement(pe.deep_clone()),
2302 C::Nesting => C::Nesting,
2303 }
2304 }
2305
2306 pub fn eql(&self, rhs: &Self) -> bool {
2307 use GenericComponent as C;
2308 match (self, rhs) {
2309 (C::Combinator(a), C::Combinator(b)) => a.eql(b),
2310 (C::ExplicitAnyNamespace, C::ExplicitAnyNamespace)
2311 | (C::ExplicitNoNamespace, C::ExplicitNoNamespace)
2312 | (C::ExplicitUniversalType, C::ExplicitUniversalType)
2313 | (C::Root, C::Root)
2314 | (C::Empty, C::Empty)
2315 | (C::Scope, C::Scope)
2316 | (C::Nesting, C::Nesting) => true,
2317 (C::DefaultNamespace(a), C::DefaultNamespace(b)) => strings::eql(a, b),
2318 (
2319 C::Namespace {
2320 prefix: ap,
2321 url: au,
2322 },
2323 C::Namespace {
2324 prefix: bp,
2325 url: bu,
2326 },
2327 ) => ap.eql(bp) && strings::eql(au, bu),
2328 (C::LocalName(a), C::LocalName(b)) => a.eql(b),
2329 (C::Id(a), C::Id(b)) | (C::Class(a), C::Class(b)) => a.eql(b),
2330 (
2331 C::AttributeInNoNamespaceExists {
2332 local_name: an,
2333 local_name_lower: al,
2334 },
2335 C::AttributeInNoNamespaceExists {
2336 local_name: bn,
2337 local_name_lower: bl,
2338 },
2339 ) => an.eql(bn) && al.eql(bl),
2340 (
2341 C::AttributeInNoNamespace {
2342 local_name: an,
2343 operator: ao,
2344 value: av,
2345 case_sensitivity: ac,
2346 never_matches: am,
2347 },
2348 C::AttributeInNoNamespace {
2349 local_name: bn,
2350 operator: bo,
2351 value: bv,
2352 case_sensitivity: bc,
2353 never_matches: bm,
2354 },
2355 ) => {
2356 an.eql(bn)
2357 && ao == bo
2358 && ac == bc
2359 && am == bm
2360 && unsafe { strings::eql(&**av, &**bv) }
2362 }
2363 (C::AttributeOther(a), C::AttributeOther(b)) => a.eql(b),
2364 (C::Negation(a), C::Negation(b))
2365 | (C::Where(a), C::Where(b))
2366 | (C::Is(a), C::Is(b))
2367 | (C::Has(a), C::Has(b)) => eql_selector_slice(a, b),
2368 (C::Nth(a), C::Nth(b)) => a.eql(b),
2369 (C::NthOf(a), C::NthOf(b)) => a.eql(b),
2370 (C::NonTsPseudoClass(a), C::NonTsPseudoClass(b)) => a.eql(b),
2371 (C::Slotted(a), C::Slotted(b)) => a.eql(b),
2372 (C::Part(a), C::Part(b)) => {
2373 a.len() == b.len() && a.iter().zip(b.iter()).all(|(l, r)| l.eql(r))
2374 }
2375 (C::Host(a), C::Host(b)) => match (a, b) {
2376 (None, None) => true,
2377 (Some(a), Some(b)) => a.eql(b),
2378 _ => false,
2379 },
2380 (
2381 C::Any {
2382 vendor_prefix: ap,
2383 selectors: asel,
2384 },
2385 C::Any {
2386 vendor_prefix: bp,
2387 selectors: bsel,
2388 },
2389 ) => ap == bp && eql_selector_slice(asel, bsel),
2390 (C::PseudoElement(a), C::PseudoElement(b)) => a.eql(b),
2391 _ => false,
2392 }
2393 }
2394
2395 pub fn as_combinator(&self) -> Option<Combinator> {
2396 if let Self::Combinator(c) = self {
2397 Some(*c)
2398 } else {
2399 None
2400 }
2401 }
2402
2403 pub fn convert_helper_is(s: Box<[GenericSelector<Impl>]>) -> Self {
2404 Self::Is(s)
2405 }
2406
2407 pub fn convert_helper_where(s: Box<[GenericSelector<Impl>]>) -> Self {
2408 Self::Where(s)
2409 }
2410
2411 pub fn convert_helper_any(s: Box<[GenericSelector<Impl>]>, prefix: Impl::VendorPrefix) -> Self {
2412 Self::Any {
2413 vendor_prefix: prefix,
2414 selectors: s,
2415 }
2416 }
2417
2418 pub fn is_combinator(&self) -> bool {
2420 matches!(self, Self::Combinator(_))
2421 }
2422
2423 #[deprecated = "use serializer::serialize_component()"]
2426 pub fn to_css(&self, _dest: &mut Printer) -> Result<(), PrintErr> {
2427 unreachable!("use serializer::serialize_component()");
2428 }
2429
2430 pub fn hash(&self, hasher: &mut Wyhash) {
2431 use GenericComponent as C;
2432 macro_rules! tag {
2438 ($n:expr) => {
2439 hasher.update(&($n as u32).to_ne_bytes())
2440 };
2441 }
2442 match self {
2443 C::Combinator(c) => {
2444 tag!(0);
2445 CssHash::hash(c, hasher);
2446 }
2447 C::ExplicitAnyNamespace => tag!(1),
2448 C::ExplicitNoNamespace => tag!(2),
2449 C::DefaultNamespace(u) => {
2450 tag!(3);
2451 hasher.update(u);
2452 }
2453 C::Namespace { prefix, url } => {
2454 tag!(4);
2455 prefix.hash(hasher);
2456 hasher.update(url);
2457 }
2458 C::ExplicitUniversalType => tag!(5),
2459 C::LocalName(ln) => {
2460 tag!(6);
2461 ln.hash(hasher);
2462 }
2463 C::Id(i) => {
2464 tag!(7);
2465 i.hash(hasher);
2466 }
2467 C::Class(i) => {
2468 tag!(8);
2469 i.hash(hasher);
2470 }
2471 C::AttributeInNoNamespaceExists {
2472 local_name,
2473 local_name_lower,
2474 } => {
2475 tag!(9);
2476 local_name.hash(hasher);
2477 local_name_lower.hash(hasher);
2478 }
2479 C::AttributeInNoNamespace {
2480 local_name,
2481 operator,
2482 value,
2483 case_sensitivity,
2484 never_matches,
2485 } => {
2486 tag!(10);
2487 local_name.hash(hasher);
2488 CssHash::hash(operator, hasher);
2489 hasher.update(unsafe { crate::arena_str(*value) });
2491 CssHash::hash(case_sensitivity, hasher);
2492 hasher.update(&[*never_matches as u8]);
2493 }
2494 C::AttributeOther(a) => {
2495 tag!(11);
2496 a.hash(hasher);
2497 }
2498 C::Negation(s) => {
2499 tag!(12);
2500 hash_selector_slice(s, hasher);
2501 }
2502 C::Root => tag!(13),
2503 C::Empty => tag!(14),
2504 C::Scope => tag!(15),
2505 C::Nth(n) => {
2506 tag!(16);
2507 n.hash(hasher);
2508 }
2509 C::NthOf(n) => {
2510 tag!(17);
2511 n.hash(hasher);
2512 }
2513 C::NonTsPseudoClass(p) => {
2514 tag!(18);
2515 CssHash::hash(p, hasher);
2516 }
2517 C::Slotted(s) => {
2518 tag!(19);
2519 s.hash(hasher);
2520 }
2521 C::Part(p) => {
2522 tag!(20);
2523 for id in p.iter() {
2524 id.hash(hasher);
2525 }
2526 }
2527 C::Host(h) => {
2528 tag!(21);
2529 if let Some(s) = h {
2530 s.hash(hasher);
2531 }
2532 }
2533 C::Where(s) => {
2534 tag!(22);
2535 hash_selector_slice(s, hasher);
2536 }
2537 C::Is(s) => {
2538 tag!(23);
2539 hash_selector_slice(s, hasher);
2540 }
2541 C::Any {
2542 vendor_prefix,
2543 selectors,
2544 } => {
2545 tag!(24);
2546 CssHash::hash(vendor_prefix, hasher);
2547 hash_selector_slice(selectors, hasher);
2548 }
2549 C::Has(s) => {
2550 tag!(25);
2551 hash_selector_slice(s, hasher);
2552 }
2553 C::PseudoElement(pe) => {
2554 tag!(26);
2555 CssHash::hash(pe, hasher);
2556 }
2557 C::Nesting => tag!(27),
2558 }
2559 }
2560}
2561
2562impl<Impl: BunSelectorImpl> CssEql for GenericComponent<Impl> {
2563 #[inline]
2564 fn eql(&self, other: &Self) -> bool {
2565 self.eql(other)
2566 }
2567}
2568impl<Impl: BunSelectorImpl> CssHash for GenericComponent<Impl> {
2569 #[inline]
2570 fn hash(&self, hasher: &mut Wyhash) {
2571 self.hash(hasher)
2572 }
2573}
2574
2575impl<Impl: BunSelectorImpl> fmt::Display for GenericComponent<Impl> {
2576 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2577 match self {
2580 Self::LocalName(ln) => write!(f, "local_name={}", bstr::BStr::new(ln.name.v())),
2581 Self::Combinator(c) => write!(f, "combinator='{}'", c),
2582 Self::PseudoElement(_) => write!(f, "pseudo_element=<..>"),
2583 Self::Class(_) => write!(f, "class=<..>"),
2584 _ => write!(f, "<component>"),
2585 }
2586 }
2587}
2588
2589#[derive(Clone, Copy, PartialEq, Eq)]
2597pub struct NthSelectorData {
2598 pub ty: NthType,
2599 pub is_function: bool,
2600 pub a: i32,
2601 pub b: i32,
2602}
2603
2604impl NthSelectorData {
2605 pub fn only(of_type: bool) -> NthSelectorData {
2607 NthSelectorData {
2608 ty: if of_type {
2609 NthType::OnlyOfType
2610 } else {
2611 NthType::OnlyChild
2612 },
2613 is_function: false,
2614 a: 0,
2615 b: 1,
2616 }
2617 }
2618
2619 pub fn first(of_type: bool) -> NthSelectorData {
2621 NthSelectorData {
2622 ty: if of_type {
2623 NthType::OfType
2624 } else {
2625 NthType::Child
2626 },
2627 is_function: false,
2628 a: 0,
2629 b: 1,
2630 }
2631 }
2632
2633 pub fn last(of_type: bool) -> NthSelectorData {
2635 NthSelectorData {
2636 ty: if of_type {
2637 NthType::LastOfType
2638 } else {
2639 NthType::LastChild
2640 },
2641 is_function: false,
2642 a: 0,
2643 b: 1,
2644 }
2645 }
2646
2647 pub fn write_start(&self, dest: &mut Printer, is_function: bool) -> Result<(), PrintErr> {
2648 dest.write_str(match self.ty {
2649 NthType::Child => {
2650 if is_function {
2651 ":nth-child("
2652 } else {
2653 ":first-child"
2654 }
2655 }
2656 NthType::LastChild => {
2657 if is_function {
2658 ":nth-last-child("
2659 } else {
2660 ":last-child"
2661 }
2662 }
2663 NthType::OfType => {
2664 if is_function {
2665 ":nth-of-type("
2666 } else {
2667 ":first-of-type"
2668 }
2669 }
2670 NthType::LastOfType => {
2671 if is_function {
2672 ":nth-last-of-type("
2673 } else {
2674 ":last-of-type"
2675 }
2676 }
2677 NthType::OnlyChild => ":only-child",
2678 NthType::OnlyOfType => ":only-of-type",
2679 NthType::Col => ":nth-col(",
2680 NthType::LastCol => ":nth-last-col(",
2681 })
2682 }
2683
2684 pub fn is_function_(&self) -> bool {
2685 self.a != 0 || self.b != 1
2686 }
2687
2688 fn number_sign(num: i32) -> &'static str {
2689 if num >= 0 { "+" } else { "" }
2690 }
2691
2692 pub fn write_affine(&self, dest: &mut Printer) -> Result<(), PrintErr> {
2693 if self.a == 0 && self.b == 0 {
2695 dest.write_char(b'0')
2696 } else if self.a == 1 && self.b == 0 {
2697 dest.write_char(b'n')
2698 } else if self.a == -1 && self.b == 0 {
2699 dest.write_str("-n")
2700 } else if self.b == 0 {
2701 dest.write_fmt(format_args!("{}n", self.a))
2702 } else if self.a == 2 && self.b == 1 {
2703 dest.write_str("odd")
2704 } else if self.a == 0 {
2705 dest.write_fmt(format_args!("{}", self.b))
2706 } else if self.a == 1 {
2707 dest.write_fmt(format_args!("n{}{}", Self::number_sign(self.b), self.b))
2708 } else if self.a == -1 {
2709 dest.write_fmt(format_args!("-n{}{}", Self::number_sign(self.b), self.b))
2710 } else {
2711 dest.write_fmt(format_args!(
2712 "{}n{}{}",
2713 self.a,
2714 Self::number_sign(self.b),
2715 self.b
2716 ))
2717 }
2718 }
2719
2720 pub fn hash(&self, hasher: &mut Wyhash) {
2721 hasher.update(&(self.ty as u32).to_ne_bytes());
2722 hasher.update(&[self.is_function as u8]);
2723 hasher.update(&self.a.to_ne_bytes());
2724 hasher.update(&self.b.to_ne_bytes());
2725 }
2726
2727 #[inline]
2728 pub fn deep_clone(&self) -> Self {
2729 *self
2730 }
2731}
2732
2733#[derive(Clone)]
2737pub struct NthOfSelectorData<Impl: SelectorImpl> {
2738 pub data: NthSelectorData,
2739 pub selectors: Box<[GenericSelector<Impl>]>,
2740}
2741
2742impl<Impl: BunSelectorImpl> NthOfSelectorData<Impl> {
2743 pub fn eql(&self, rhs: &Self) -> bool {
2744 self.data.eql(&rhs.data) && eql_selector_slice(&self.selectors, &rhs.selectors)
2745 }
2746 pub fn hash(&self, hasher: &mut Wyhash) {
2747 self.data.hash(hasher);
2748 hash_selector_slice(&self.selectors, hasher);
2749 }
2750 pub fn deep_clone(&self) -> Self {
2751 Self {
2752 data: self.data,
2753 selectors: deep_clone_selector_slice(&self.selectors),
2754 }
2755 }
2756 pub fn nth_data(&self) -> NthSelectorData {
2757 self.data
2758 }
2759}
2760
2761bitflags::bitflags! {
2766 #[derive(Clone, Copy, PartialEq, Eq)]
2767 pub struct SelectorParsingState: u16 {
2768 const SKIP_DEFAULT_NAMESPACE = 1 << 0;
2771
2772 const AFTER_SLOTTED = 1 << 1;
2777
2778 const AFTER_PART = 1 << 2;
2783
2784 const AFTER_PSEUDO_ELEMENT = 1 << 3;
2791
2792 const AFTER_NON_STATEFUL_PSEUDO_ELEMENT = 1 << 4;
2797
2798 const DISALLOW_COMBINATORS = 1 << 5;
2800
2801 const DISALLOW_PSEUDOS = 1 << 6;
2803
2804 const AFTER_NESTING = 1 << 7;
2806
2807 const AFTER_WEBKIT_SCROLLBAR = 1 << 8;
2808 const AFTER_VIEW_TRANSITION = 1 << 9;
2809 const AFTER_UNKNOWN_PSEUDO_ELEMENT = 1 << 10;
2810 }
2811}
2812
2813impl SelectorParsingState {
2814 pub fn after_any_pseudo(self) -> bool {
2816 self.intersects(Self::AFTER_PART | Self::AFTER_SLOTTED | Self::AFTER_PSEUDO_ELEMENT)
2817 }
2818
2819 pub fn allows_pseudos(self) -> bool {
2820 !self.contains(Self::AFTER_PSEUDO_ELEMENT) && !self.contains(Self::DISALLOW_PSEUDOS)
2821 }
2822
2823 pub fn allows_part(self) -> bool {
2824 !self.contains(Self::DISALLOW_PSEUDOS) && !self.after_any_pseudo()
2825 }
2826
2827 pub fn allows_slotted(self) -> bool {
2828 self.allows_part()
2829 }
2830
2831 pub fn allows_tree_structural_pseudo_classes(self) -> bool {
2832 !self.after_any_pseudo()
2833 }
2834
2835 pub fn allows_non_functional_pseudo_classes(self) -> bool {
2836 !self.contains(Self::AFTER_SLOTTED)
2837 && !self.contains(Self::AFTER_NON_STATEFUL_PSEUDO_ELEMENT)
2838 }
2839
2840 pub fn allows_combinators(self) -> bool {
2841 !self.contains(Self::DISALLOW_COMBINATORS)
2842 }
2843
2844 pub fn allows_custom_functional_pseudo_classes(self) -> bool {
2845 !self.after_any_pseudo()
2846 }
2847}
2848
2849#[derive(Clone, Copy, PartialEq, Eq)]
2852pub struct SpecificityAndFlags {
2853 pub specificity: u32,
2856 pub flags: SelectorFlags,
2858}
2859
2860impl SpecificityAndFlags {
2861 pub fn has_pseudo_element(self) -> bool {
2862 self.flags.contains(SelectorFlags::HAS_PSEUDO)
2863 }
2864 pub fn hash(self, hasher: &mut Wyhash) {
2865 hasher.update(&self.specificity.to_ne_bytes());
2866 hasher.update(&[self.flags.bits()]);
2867 }
2868 pub fn deep_clone(self) -> Self {
2869 self
2870 }
2871}
2872
2873bitflags::bitflags! {
2874 #[derive(Clone, Copy, PartialEq, Eq)]
2875 pub struct SelectorFlags: u8 {
2876 const HAS_PSEUDO = 1 << 0;
2877 const HAS_SLOTTED = 1 << 1;
2878 const HAS_PART = 1 << 2;
2879 }
2880}
2881
2882#[derive(Clone, Copy, PartialEq, Eq)]
2884pub enum ParseErrorRecovery {
2885 DiscardList,
2888 IgnoreInvalidSelector,
2892}
2893
2894#[derive(Clone, Copy, PartialEq, Eq)]
2895pub enum NestingRequirement {
2896 None,
2897 Prefixed,
2898 Contained,
2899 Implicit,
2900}
2901
2902#[derive(Clone, Copy, PartialEq, Eq, Hash, strum::IntoStaticStr, CssHash)]
2903pub enum Combinator {
2904 Child, Descendant, NextSibling, LaterSibling, PseudoElement,
2915 SlotAssignment,
2918
2919 Part,
2922
2923 DeepDescendant,
2926 Deep,
2931}
2932
2933impl Combinator {
2934 #[deprecated = "use serializer::serialize_combinator()"]
2939 pub fn to_css(self, _dest: &mut Printer) -> Result<(), PrintErr> {
2940 unreachable!("use serializer::serialize_combinator()");
2941 }
2942
2943 pub fn is_tree_combinator(self) -> bool {
2944 matches!(
2945 self,
2946 Self::Child | Self::Descendant | Self::NextSibling | Self::LaterSibling
2947 )
2948 }
2949}
2950
2951impl fmt::Display for Combinator {
2952 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2953 match self {
2954 Self::Child => write!(f, ">"),
2955 Self::Descendant => write!(f, "`descendant` (space)"),
2956 Self::NextSibling => write!(f, "+"),
2957 Self::LaterSibling => write!(f, "~"),
2958 other => write!(f, "{}", <&'static str>::from(*other)),
2959 }
2960 }
2961}
2962
2963#[derive(Clone)]
2964pub enum SelectorParseErrorKind {
2965 InvalidState,
2966 ClassNeedsIdent(Token),
2967 PseudoElementExpectedIdent(Token),
2968 UnsupportedPseudoClassOrElement(Str),
2969 NoQualifiedNameInAttributeSelector(Token),
2970 UnexpectedTokenInAttributeSelector(Token),
2971 UnexpectedSelectorAfterPseudoElement(Token),
2972 InvalidQualNameInAttr(Token),
2973 ExpectedBarInAttr(Token),
2974 EmptySelector,
2975 DanglingCombinator,
2976 InvalidPseudoClassBeforeWebkitScrollbar,
2977 InvalidPseudoClassAfterWebkitScrollbar,
2978 InvalidPseudoClassAfterPseudoElement,
2979 MissingNestingSelector,
2980 MissingNestingPrefix,
2981 ExpectedNamespace(Str),
2982 BadValueInAttr(Token),
2983 ExplicitNamespaceUnexpectedToken(Token),
2984 UnexpectedIdent(Str),
2985 AmbiguousCssModuleClass(Str),
2986}
2987
2988impl SelectorParseErrorKind {
2989 pub fn into_default_parser_error(self) -> css::ParserError {
2990 css::ParserError::selector_error(self.into_selector_error())
2991 }
2992
2993 pub fn into_selector_error(self) -> css::SelectorError {
2994 use SelectorParseErrorKind as K;
2997 use css::SelectorError as S;
2998 match self {
2999 K::InvalidState => S::invalid_state,
3000 K::ClassNeedsIdent(token) => S::class_needs_ident(token),
3001 K::PseudoElementExpectedIdent(token) => S::pseudo_element_expected_ident(token),
3002 K::UnsupportedPseudoClassOrElement(name) => {
3003 S::unsupported_pseudo_class_or_element(name)
3004 }
3005 K::NoQualifiedNameInAttributeSelector(token) => {
3006 S::no_qualified_name_in_attribute_selector(token)
3007 }
3008 K::UnexpectedTokenInAttributeSelector(token) => {
3009 S::unexpected_token_in_attribute_selector(token)
3010 }
3011 K::InvalidQualNameInAttr(token) => S::invalid_qual_name_in_attr(token),
3012 K::ExpectedBarInAttr(token) => S::expected_bar_in_attr(token),
3013 K::EmptySelector => S::empty_selector,
3014 K::DanglingCombinator => S::dangling_combinator,
3015 K::InvalidPseudoClassBeforeWebkitScrollbar => {
3016 S::invalid_pseudo_class_before_webkit_scrollbar
3017 }
3018 K::InvalidPseudoClassAfterWebkitScrollbar => {
3019 S::invalid_pseudo_class_after_webkit_scrollbar
3020 }
3021 K::InvalidPseudoClassAfterPseudoElement => S::invalid_pseudo_class_after_pseudo_element,
3022 K::MissingNestingSelector => S::missing_nesting_selector,
3023 K::MissingNestingPrefix => S::missing_nesting_prefix,
3024 K::ExpectedNamespace(name) => S::expected_namespace(name),
3025 K::BadValueInAttr(token) => S::bad_value_in_attr(token),
3026 K::ExplicitNamespaceUnexpectedToken(token) => {
3027 S::explicit_namespace_unexpected_token(token)
3028 }
3029 K::UnexpectedIdent(ident) => S::unexpected_ident(ident),
3030 K::UnexpectedSelectorAfterPseudoElement(tok) => {
3031 S::unexpected_selector_after_pseudo_element(tok)
3032 }
3033 K::AmbiguousCssModuleClass(name) => S::ambiguous_css_module_class(name),
3034 }
3035 }
3036}
3037
3038impl css::IntoParserError for SelectorParseErrorKind {
3040 fn into_parser_error(self) -> css::ParserError {
3041 self.into_default_parser_error()
3042 }
3043}
3044
3045pub enum SimpleSelectorParseResult<Impl: SelectorImpl> {
3046 SimpleSelector(GenericComponent<Impl>),
3047 PseudoElement(Impl::PseudoElement),
3048 SlottedPseudo(GenericSelector<Impl>),
3049 PartPseudo(Box<[Impl::Identifier]>),
3051}
3052
3053#[derive(Clone, CssEql, CssHash)]
3056pub enum PseudoElement {
3057 After,
3059 Before,
3061 FirstLine,
3063 FirstLetter,
3065 Selection(css::VendorPrefix),
3067 Placeholder(css::VendorPrefix),
3069 Marker,
3071 Backdrop(css::VendorPrefix),
3073 FileSelectorButton(css::VendorPrefix),
3075 WebkitScrollbar(WebKitScrollbarPseudoElement),
3077 Cue,
3079 CueRegion,
3081 CueFunction {
3083 selector: Box<Selector>,
3085 },
3086 CueRegionFunction {
3088 selector: Box<Selector>,
3090 },
3091 ViewTransition,
3093 ViewTransitionGroup {
3095 part_name: ViewTransitionPartName,
3097 },
3098 ViewTransitionImagePair {
3100 part_name: ViewTransitionPartName,
3102 },
3103 ViewTransitionOld {
3105 part_name: ViewTransitionPartName,
3107 },
3108 ViewTransitionNew {
3110 part_name: ViewTransitionPartName,
3112 },
3113 Custom {
3115 name: Str,
3117 },
3118 CustomFunction {
3120 name: Str,
3122 arguments: TokenList,
3124 },
3125}
3126
3127impl PseudoElement {
3128 pub fn is_equivalent(&self, other: &PseudoElement) -> bool {
3129 use PseudoElement as PE;
3130 if matches!(self, PE::Selection(_)) && matches!(other, PE::Selection(_)) {
3131 return true;
3132 }
3133 if matches!(self, PE::Placeholder(_)) && matches!(other, PE::Placeholder(_)) {
3134 return true;
3135 }
3136 if matches!(self, PE::Backdrop(_)) && matches!(other, PE::Backdrop(_)) {
3137 return true;
3138 }
3139 if matches!(self, PE::FileSelectorButton(_)) && matches!(other, PE::FileSelectorButton(_)) {
3140 return true;
3141 }
3142 self.eql(other)
3143 }
3144
3145 pub fn deep_clone(&self) -> Self {
3147 self.clone()
3148 }
3149
3150 pub fn get_necessary_prefixes(&mut self, targets: &css::targets::Targets) -> css::VendorPrefix {
3151 use PseudoElement as PE;
3152 use css::prefixes::Feature as F;
3153 let (p, feature): (&mut css::VendorPrefix, F) = match self {
3154 PE::Selection(p) => (p, F::PseudoElementSelection),
3155 PE::Placeholder(p) => (p, F::PseudoElementPlaceholder),
3156 PE::Backdrop(p) => (p, F::PseudoElementBackdrop),
3157 PE::FileSelectorButton(p) => (p, F::PseudoElementFileSelectorButton),
3158 _ => return css::VendorPrefix::empty(),
3159 };
3160 *p = targets.prefixes(*p, feature);
3161 *p
3162 }
3163
3164 pub fn get_prefix(&self) -> css::VendorPrefix {
3165 use PseudoElement as PE;
3166 match self {
3167 PE::Selection(p) | PE::Placeholder(p) | PE::Backdrop(p) | PE::FileSelectorButton(p) => {
3168 *p
3169 }
3170 _ => css::VendorPrefix::empty(),
3171 }
3172 }
3173
3174 pub fn valid_after_slotted(&self) -> bool {
3175 use PseudoElement as PE;
3176 matches!(
3177 self,
3178 PE::Before | PE::After | PE::Marker | PE::Placeholder(_) | PE::FileSelectorButton(_)
3179 )
3180 }
3181
3182 pub fn is_unknown(&self) -> bool {
3183 matches!(
3184 self,
3185 PseudoElement::Custom { .. } | PseudoElement::CustomFunction { .. }
3186 )
3187 }
3188
3189 pub fn accepts_state_pseudo_classes(&self) -> bool {
3190 let _ = self;
3191 true
3193 }
3194
3195 pub fn is_webkit_scrollbar(&self) -> bool {
3196 matches!(self, PseudoElement::WebkitScrollbar(_))
3197 }
3198
3199 pub fn is_view_transition(&self) -> bool {
3200 use PseudoElement as PE;
3201 matches!(
3202 self,
3203 PE::ViewTransitionGroup { .. }
3204 | PE::ViewTransitionImagePair { .. }
3205 | PE::ViewTransitionNew { .. }
3206 | PE::ViewTransitionOld { .. }
3207 )
3208 }
3209
3210 pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
3211 serialize::serialize_pseudo_element(self, dest, None)
3215 }
3216}
3217
3218impl fmt::Display for PseudoElement {
3219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3220 write!(f, "<pseudo_element>")
3222 }
3223}
3224
3225#[derive(Clone, Copy, PartialEq, Eq, Hash)]
3227pub enum NthType {
3228 Child,
3229 LastChild,
3230 OnlyChild,
3231 OfType,
3232 LastOfType,
3233 OnlyOfType,
3234 Col,
3235 LastCol,
3236}
3237
3238impl NthType {
3239 pub fn is_only(self) -> bool {
3240 self == NthType::OnlyChild || self == NthType::OnlyOfType
3241 }
3242
3243 pub fn is_of_type(self) -> bool {
3244 self == NthType::OfType || self == NthType::LastOfType || self == NthType::OnlyOfType
3245 }
3246
3247 pub fn is_from_end(self) -> bool {
3248 self == NthType::LastChild || self == NthType::LastOfType || self == NthType::LastCol
3249 }
3250
3251 pub fn allows_of_selector(self) -> bool {
3252 self == NthType::Child || self == NthType::LastChild
3253 }
3254}
3255
3256pub fn parse_type_selector<Impl: BunSelectorImpl>(
3264 parser: &mut SelectorParser,
3265 input: &mut CssParser,
3266 state: SelectorParsingState,
3267 sink: &mut SelectorBuilder<Impl>,
3268) -> CResult<bool> {
3269 let result = match parse_qualified_name::<Impl>(parser, input, false) {
3270 Ok(v) => v,
3271 Err(e) => {
3272 if matches!(
3273 e.kind,
3274 css::ParseErrorKind::basic(css::BasicParseErrorKind::end_of_input)
3275 ) {
3276 return Ok(false);
3277 }
3278 return Err(e);
3279 }
3280 };
3281
3282 let (namespace, local_name) = match result {
3283 OptionalQName::None(_) => return Ok(false),
3284 OptionalQName::Some(ns, ln) => (ns, ln),
3285 };
3286
3287 if state.after_any_pseudo() {
3288 return Err(input
3289 .new_custom_error(SelectorParseErrorKind::InvalidState.into_default_parser_error()));
3290 }
3291
3292 match namespace {
3293 QNamePrefix::ImplicitAnyNamespace => {}
3294 QNamePrefix::ImplicitDefaultNamespace(url) => {
3295 sink.push_simple_selector(GenericComponent::DefaultNamespace(url));
3296 }
3297 QNamePrefix::ExplicitNamespace(prefix, url) => {
3298 let component: GenericComponent<Impl> = 'component: {
3299 if let Some(default_url) = parser.default_namespace() {
3300 if url == default_url {
3301 break 'component GenericComponent::DefaultNamespace(url);
3302 }
3303 }
3304 GenericComponent::Namespace { prefix, url }
3305 };
3306 sink.push_simple_selector(component);
3307 }
3308 QNamePrefix::ExplicitNoNamespace => {
3309 sink.push_simple_selector(GenericComponent::ExplicitNoNamespace);
3310 }
3311 QNamePrefix::ExplicitAnyNamespace => {
3312 sink.push_simple_selector(GenericComponent::ExplicitAnyNamespace);
3325 }
3326 QNamePrefix::ImplicitNoNamespace => {
3327 unreachable!("Should not be returned with in_attr_selector = false");
3328 }
3329 }
3330
3331 if let Some(name) = local_name {
3332 sink.push_simple_selector(GenericComponent::LocalName(LocalName {
3333 lower_name: {
3334 Ident {
3337 v: arena_lowercase(input.arena(), name),
3338 }
3339 },
3340 name: Ident {
3341 v: std::ptr::from_ref::<[u8]>(name),
3342 },
3343 }));
3344 } else {
3345 sink.push_simple_selector(GenericComponent::ExplicitUniversalType);
3346 }
3347
3348 Ok(true)
3349}
3350
3351pub fn parse_one_simple_selector<Impl: BunSelectorImpl>(
3357 parser: &mut SelectorParser,
3358 input: &mut CssParser,
3359 state: &mut SelectorParsingState,
3360) -> CResult<Option<SimpleSelectorParseResult<Impl>>> {
3361 type S<Impl> = SimpleSelectorParseResult<Impl>;
3362
3363 let start = input.state();
3364 let token_location = input.current_source_location();
3365 let token_loc = input.position();
3366 let token = match input.next_including_whitespace() {
3367 Ok(v) => v.clone(),
3368 Err(_) => {
3369 input.reset(&start);
3370 return Ok(None);
3371 }
3372 };
3373
3374 match token {
3375 Token::IdHash(id) => {
3376 if state.after_any_pseudo() {
3377 return Err(token_location.new_custom_error(
3378 SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::IdHash(id))
3379 .into_default_parser_error(),
3380 ));
3381 }
3382 let component = GenericComponent::Id(parser.new_local_identifier(
3383 input,
3384 css::CssRefTag::ID,
3385 id,
3386 token_loc,
3387 ));
3388 return Ok(Some(S::SimpleSelector(component)));
3389 }
3390 Token::OpenSquare => {
3391 if state.after_any_pseudo() {
3392 return Err(token_location.new_custom_error(
3393 SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::OpenSquare)
3394 .into_default_parser_error(),
3395 ));
3396 }
3397 let attr = input.parse_nested_block(|input2: &mut CssParser| {
3398 parse_attribute_selector::<Impl>(parser, input2)
3399 })?;
3400 return Ok(Some(S::SimpleSelector(attr)));
3401 }
3402 Token::Colon => {
3403 let location = input.current_source_location();
3404 let (is_single_colon, next_token): (bool, Token) =
3405 match input.next_including_whitespace()?.clone() {
3406 Token::Colon => (false, input.next_including_whitespace()?.clone()),
3407 t => (true, t),
3408 };
3409 let (name, is_functional): (Str, bool) = match next_token {
3410 Token::Ident(name) => (name, false),
3411 Token::Function(name) => (name, true),
3412 t => {
3413 let e = SelectorParseErrorKind::PseudoElementExpectedIdent(t);
3414 return Err(input.new_custom_error(e.into_default_parser_error()));
3415 }
3416 };
3417 let is_pseudo_element = !is_single_colon || is_css2_pseudo_element(name);
3418 if is_pseudo_element {
3419 if !state.allows_pseudos() {
3420 return Err(input.new_custom_error(
3421 SelectorParseErrorKind::InvalidState.into_default_parser_error(),
3422 ));
3423 }
3424 let pseudo_element: Impl::PseudoElement = if is_functional {
3425 if parser.parse_part()
3426 && strings::eql_case_insensitive_ascii_check_length(name, b"part")
3427 {
3428 if !state.allows_part() {
3429 return Err(input.new_custom_error(
3430 SelectorParseErrorKind::InvalidState.into_default_parser_error(),
3431 ));
3432 }
3433
3434 let names = input.parse_nested_block(
3435 |input2: &mut CssParser| -> CResult<Box<[Impl::Identifier]>> {
3436 let mut result: Vec<Impl::Identifier> = Vec::with_capacity(1);
3439
3440 result.push(Ident {
3441 v: input2.expect_ident()?,
3442 });
3443
3444 while !input2.is_exhausted() {
3445 result.push(Ident {
3446 v: input2.expect_ident()?,
3447 });
3448 }
3449
3450 Ok(result.into_boxed_slice())
3451 },
3452 )?;
3453
3454 return Ok(Some(S::PartPseudo(names)));
3455 }
3456
3457 if parser.parse_slotted()
3458 && strings::eql_case_insensitive_ascii_check_length(name, b"slotted")
3459 {
3460 if !state.allows_slotted() {
3461 return Err(input.new_custom_error(
3462 SelectorParseErrorKind::InvalidState.into_default_parser_error(),
3463 ));
3464 }
3465 let selector = input.parse_nested_block(|input2: &mut CssParser| {
3466 parse_inner_compound_selector::<Impl>(parser, input2, state)
3467 })?;
3468 return Ok(Some(S::SlottedPseudo(selector)));
3469 }
3470
3471 input.parse_nested_block(|i: &mut CssParser| {
3472 parser.parse_functional_pseudo_element(name, i)
3473 })?
3474 } else {
3477 parser.parse_pseudo_element(location, name)?
3478 };
3479
3480 if state.contains(SelectorParsingState::AFTER_SLOTTED)
3481 && pseudo_element.valid_after_slotted()
3482 {
3483 return Ok(Some(S::PseudoElement(pseudo_element)));
3484 }
3485
3486 return Ok(Some(S::PseudoElement(pseudo_element)));
3487 } else {
3488 let pseudo_class: GenericComponent<Impl> = if is_functional {
3489 input.parse_nested_block(|input2: &mut CssParser| {
3490 parse_functional_pseudo_class::<Impl>(parser, input2, name, state)
3491 })?
3492 } else {
3493 parse_simple_pseudo_class::<Impl>(parser, location, name, *state)?
3494 };
3495 return Ok(Some(S::SimpleSelector(pseudo_class)));
3496 }
3497 }
3498 Token::Delim(d) => match u8::try_from(d).ok() {
3499 Some(b'.') => {
3500 if state.after_any_pseudo() {
3501 return Err(token_location.new_custom_error(
3502 SelectorParseErrorKind::UnexpectedSelectorAfterPseudoElement(Token::Delim(
3503 b'.' as u32,
3504 ))
3505 .into_default_parser_error(),
3506 ));
3507 }
3508 let location = input.current_source_location();
3509 let class = match input.next_including_whitespace()?.clone() {
3510 Token::Ident(class) => class,
3511 t => {
3512 let e = SelectorParseErrorKind::ClassNeedsIdent(t);
3513 return Err(location.new_custom_error(e.into_default_parser_error()));
3514 }
3515 };
3516 return Ok(Some(S::SimpleSelector(GenericComponent::Class(
3517 parser.new_local_identifier(input, css::CssRefTag::CLASS, class, token_loc),
3518 ))));
3519 }
3520 Some(b'&') => {
3521 if parser.is_nesting_allowed() {
3522 state.insert(SelectorParsingState::AFTER_NESTING);
3523 return Ok(Some(S::SimpleSelector(GenericComponent::Nesting)));
3524 }
3525 }
3526 _ => {}
3527 },
3528 _ => {}
3529 }
3530
3531 input.reset(&start);
3532 Ok(None)
3533}
3534
3535pub fn parse_attribute_selector<Impl: BunSelectorImpl>(
3536 parser: &mut SelectorParser,
3537 input: &mut CssParser,
3538) -> CResult<GenericComponent<Impl>> {
3539 type N<Impl> = attrs::NamespaceConstraint<attrs::NamespaceUrl<Impl>>;
3540
3541 let (namespace, local_name): (Option<N<Impl>>, Str) = 'brk: {
3542 input.skip_whitespace();
3543
3544 let qname = parse_qualified_name::<Impl>(parser, input, true)?;
3545 match qname {
3546 OptionalQName::None(t) => {
3547 return Err(input.new_custom_error(
3548 SelectorParseErrorKind::NoQualifiedNameInAttributeSelector(t)
3549 .into_default_parser_error(),
3550 ));
3551 }
3552 OptionalQName::Some(ns, ln) => {
3553 let ln = ln.unwrap_or_else(|| unreachable!());
3554 break 'brk (
3555 match ns {
3556 QNamePrefix::ImplicitNoNamespace | QNamePrefix::ExplicitNoNamespace => None,
3557 QNamePrefix::ExplicitNamespace(prefix, url) => {
3558 Some(attrs::NamespaceConstraint::Specific(attrs::NamespaceUrl {
3559 prefix,
3560 url,
3561 }))
3562 }
3563 QNamePrefix::ExplicitAnyNamespace => Some(attrs::NamespaceConstraint::Any),
3564 QNamePrefix::ImplicitAnyNamespace
3565 | QNamePrefix::ImplicitDefaultNamespace(_) => {
3566 unreachable!("Not returned with in_attr_selector = true");
3567 }
3568 },
3569 ln,
3570 );
3571 }
3572 }
3573 };
3574
3575 let location = input.current_source_location();
3576 let operator: attrs::AttrSelectorOperator = 'operator: {
3577 let tok = match input.next() {
3578 Ok(v) => v.clone(),
3579 Err(_) => {
3580 let local_name_lower: *const [u8] = arena_lowercase(input.arena(), local_name);
3583 if let Some(ns) = namespace {
3584 let x = attrs::AttrSelectorWithOptionalNamespace::<Impl> {
3585 namespace: Some(ns),
3586 local_name: Ident { v: local_name },
3587 local_name_lower: Ident {
3588 v: local_name_lower,
3589 },
3590 never_matches: false,
3591 operation: attrs::ParsedAttrSelectorOperation::Exists,
3592 };
3593 return Ok(GenericComponent::AttributeOther(Box::new(x)));
3594 } else {
3595 return Ok(GenericComponent::AttributeInNoNamespaceExists {
3596 local_name: Ident { v: local_name },
3597 local_name_lower: Ident {
3598 v: local_name_lower,
3599 },
3600 });
3601 }
3602 }
3603 };
3604
3605 match tok {
3606 Token::Delim(d) if d == b'=' as u32 => {
3608 break 'operator attrs::AttrSelectorOperator::Equal;
3609 }
3610 Token::IncludeMatch => break 'operator attrs::AttrSelectorOperator::Includes,
3612 Token::DashMatch => break 'operator attrs::AttrSelectorOperator::DashMatch,
3614 Token::PrefixMatch => break 'operator attrs::AttrSelectorOperator::Prefix,
3616 Token::SubstringMatch => break 'operator attrs::AttrSelectorOperator::Substring,
3618 Token::SuffixMatch => break 'operator attrs::AttrSelectorOperator::Suffix,
3620 _ => {}
3621 }
3622 return Err(location.new_custom_error(
3623 SelectorParseErrorKind::UnexpectedTokenInAttributeSelector(tok)
3624 .into_default_parser_error(),
3625 ));
3626 };
3627
3628 let value_str: Str = {
3632 let value_loc = input.current_source_location();
3633 let tok = input.next()?.clone();
3634 match tok {
3635 Token::Ident(v) | Token::QuotedString(v) => v,
3636 t => {
3637 return Err(value_loc.new_custom_error(
3638 SelectorParseErrorKind::BadValueInAttr(t).into_default_parser_error(),
3639 ));
3640 }
3641 }
3642 };
3643 let never_matches = match operator {
3644 attrs::AttrSelectorOperator::Equal | attrs::AttrSelectorOperator::DashMatch => false,
3645 attrs::AttrSelectorOperator::Includes => {
3646 value_str.is_empty() || strings::index_of_any(value_str, SELECTOR_WHITESPACE).is_some()
3647 }
3648 attrs::AttrSelectorOperator::Prefix
3649 | attrs::AttrSelectorOperator::Substring
3650 | attrs::AttrSelectorOperator::Suffix => value_str.is_empty(),
3651 };
3652
3653 let attribute_flags = parse_attribute_flags(input)?;
3654
3655 let value: Impl::AttrValue = std::ptr::from_ref::<[u8]>(value_str);
3656 let (local_name_lower, local_name_is_ascii_lowercase): (Impl::LocalName, bool) = 'brk: {
3657 let first_uppercase = 'a: {
3658 for (i, &b) in local_name.iter().enumerate() {
3659 if b >= b'A' && b <= b'Z' {
3660 break 'a Some(i);
3661 }
3662 }
3663 None
3664 };
3665 if let Some(first_uppercase) = first_uppercase {
3666 let str_ = &local_name[first_uppercase..];
3667 let lowered: *const [u8] = arena_lowercase(input.arena(), str_);
3669 break 'brk (Ident { v: lowered }, false);
3670 } else {
3671 break 'brk (
3672 Ident {
3673 v: std::ptr::from_ref::<[u8]>(local_name),
3674 },
3675 true,
3676 );
3677 }
3678 };
3679 let case_sensitivity: attrs::ParsedCaseSensitivity =
3680 attribute_flags.to_case_sensitivity(local_name_lower.v(), namespace.is_some());
3681 if namespace.is_some() && !local_name_is_ascii_lowercase {
3682 Ok(GenericComponent::AttributeOther(Box::new(
3683 attrs::AttrSelectorWithOptionalNamespace::<Impl> {
3684 namespace,
3685 local_name: Ident {
3686 v: std::ptr::from_ref::<[u8]>(local_name),
3687 },
3688 local_name_lower,
3689 never_matches,
3690 operation: attrs::ParsedAttrSelectorOperation::WithValue {
3691 operator,
3692 case_sensitivity,
3693 expected_value: value,
3694 },
3695 },
3696 )))
3697 } else {
3698 Ok(GenericComponent::AttributeInNoNamespace {
3699 local_name: Ident { v: local_name },
3700 operator,
3701 value,
3702 case_sensitivity,
3703 never_matches,
3704 })
3705 }
3706}
3707
3708pub fn is_css2_pseudo_element(name: &[u8]) -> bool {
3712 crate::match_ignore_ascii_case! { name, {
3714 b"before" | b"after" | b"first-line" | b"first-letter" => true,
3715 _ => false,
3716 }}
3717}
3718
3719pub fn parse_inner_compound_selector<Impl: BunSelectorImpl>(
3721 parser: &mut SelectorParser,
3722 input: &mut CssParser,
3723 state: &mut SelectorParsingState,
3724) -> CResult<GenericSelector<Impl>> {
3725 let mut child_state = {
3726 let mut child_state = *state;
3727 child_state.insert(SelectorParsingState::DISALLOW_PSEUDOS);
3728 child_state.insert(SelectorParsingState::DISALLOW_COMBINATORS);
3729 child_state
3730 };
3731 let result = parse_selector::<Impl>(parser, input, &mut child_state, NestingRequirement::None)?;
3732 if child_state.contains(SelectorParsingState::AFTER_NESTING) {
3733 state.insert(SelectorParsingState::AFTER_NESTING);
3734 }
3735 Ok(result)
3736}
3737
3738pub fn parse_functional_pseudo_class<Impl: BunSelectorImpl>(
3739 parser: &mut SelectorParser,
3740 input: &mut CssParser,
3741 name: Str,
3742 state: &mut SelectorParsingState,
3743) -> CResult<GenericComponent<Impl>> {
3744 crate::match_ignore_ascii_case! { name, {
3745 b"nth-child" => return parse_nth_pseudo_class::<Impl>(parser, input, *state, NthType::Child),
3746 b"nth-of-type" => return parse_nth_pseudo_class::<Impl>(parser, input, *state, NthType::OfType),
3747 b"nth-last-child" => return parse_nth_pseudo_class::<Impl>(parser, input, *state, NthType::LastChild),
3748 b"nth-last-of-type" => return parse_nth_pseudo_class::<Impl>(parser, input, *state, NthType::LastOfType),
3749 b"nth-col" => return parse_nth_pseudo_class::<Impl>(parser, input, *state, NthType::Col),
3750 b"nth-last-col" => return parse_nth_pseudo_class::<Impl>(parser, input, *state, NthType::LastCol),
3751 b"is" => if parser.parse_is_and_where() {
3752 return parse_is_or_where::<Impl, _>(parser, input, state, |s| GenericComponent::convert_helper_is(s));
3753 },
3754 b"where" => if parser.parse_is_and_where() {
3755 return parse_is_or_where::<Impl, _>(parser, input, state, |s| GenericComponent::convert_helper_where(s));
3756 },
3757 b"has" => return parse_has::<Impl>(parser, input, state),
3758 b"host" => {
3759 if !state.allows_tree_structural_pseudo_classes() {
3760 return Err(input.new_custom_error(
3761 SelectorParseErrorKind::InvalidState.into_default_parser_error(),
3762 ));
3763 }
3764 return Ok(GenericComponent::Host(Some(
3765 parse_inner_compound_selector::<Impl>(parser, input, state)?,
3766 )));
3767 },
3768 b"not" => return parse_negation::<Impl>(parser, input, state),
3769 _ => {},
3770 } }
3771
3772 if let Some(prefix) = parser.parse_any_prefix(name) {
3773 return parse_is_or_where::<Impl, _>(parser, input, state, move |s| {
3774 GenericComponent::convert_helper_any(s, prefix)
3775 });
3776 }
3777
3778 if !state.allows_custom_functional_pseudo_classes() {
3779 return Err(input
3780 .new_custom_error(SelectorParseErrorKind::InvalidState.into_default_parser_error()));
3781 }
3782
3783 let result = parser.parse_non_ts_functional_pseudo_class(name, input)?;
3784
3785 Ok(GenericComponent::NonTsPseudoClass(result))
3786 }
3789
3790pub fn parse_simple_pseudo_class<Impl: BunSelectorImpl>(
3791 parser: &mut SelectorParser,
3792 location: css::SourceLocation,
3793 name: Str,
3794 state: SelectorParsingState,
3795) -> CResult<GenericComponent<Impl>> {
3796 if !state.allows_non_functional_pseudo_classes() {
3797 return Err(location
3798 .new_custom_error(SelectorParseErrorKind::InvalidState.into_default_parser_error()));
3799 }
3800
3801 if state.allows_tree_structural_pseudo_classes() {
3802 crate::match_ignore_ascii_case! { name, {
3803 b"first-child" => return Ok(GenericComponent::Nth(NthSelectorData::first(false))),
3804 b"last-child" => return Ok(GenericComponent::Nth(NthSelectorData::last(false))),
3805 b"only-child" => return Ok(GenericComponent::Nth(NthSelectorData::only(false))),
3806 b"root" => return Ok(GenericComponent::Root),
3807 b"empty" => return Ok(GenericComponent::Empty),
3808 b"scope" => return Ok(GenericComponent::Scope),
3809 b"host" => if parser.parse_host() {
3810 return Ok(GenericComponent::Host(None));
3811 },
3812 b"first-of-type" => return Ok(GenericComponent::Nth(NthSelectorData::first(true))),
3813 b"last-of-type" => return Ok(GenericComponent::Nth(NthSelectorData::last(true))),
3814 b"only-of-type" => return Ok(GenericComponent::Nth(NthSelectorData::only(true))),
3815 _ => {},
3816 } }
3817 }
3818
3819 if state.contains(SelectorParsingState::AFTER_VIEW_TRANSITION) {
3822 if strings::eql_case_insensitive_ascii_check_length(name, b"only-child") {
3823 return Ok(GenericComponent::Nth(NthSelectorData::only(false)));
3824 }
3825 }
3826
3827 let pseudo_class = parser.parse_non_ts_pseudo_class(location, name)?;
3828 if state.contains(SelectorParsingState::AFTER_WEBKIT_SCROLLBAR) {
3829 if !pseudo_class.is_valid_after_webkit_scrollbar() {
3830 return Err(location.new_custom_error(
3831 SelectorParseErrorKind::InvalidPseudoClassAfterWebkitScrollbar
3832 .into_default_parser_error(),
3833 ));
3834 }
3835 } else if state.contains(SelectorParsingState::AFTER_PSEUDO_ELEMENT) {
3836 if !pseudo_class.is_user_action_state() {
3837 return Err(location.new_custom_error(
3838 SelectorParseErrorKind::InvalidPseudoClassAfterPseudoElement
3839 .into_default_parser_error(),
3840 ));
3841 }
3842 } else if !pseudo_class.is_valid_before_webkit_scrollbar() {
3843 return Err(location.new_custom_error(
3844 SelectorParseErrorKind::InvalidPseudoClassBeforeWebkitScrollbar
3845 .into_default_parser_error(),
3846 ));
3847 }
3848
3849 Ok(GenericComponent::NonTsPseudoClass(pseudo_class))
3850}
3851
3852pub fn parse_nth_pseudo_class<Impl: BunSelectorImpl>(
3853 parser: &mut SelectorParser,
3854 input: &mut CssParser,
3855 state: SelectorParsingState,
3856 ty: NthType,
3857) -> CResult<GenericComponent<Impl>> {
3858 if !state.allows_tree_structural_pseudo_classes() {
3859 return Err(input
3860 .new_custom_error(SelectorParseErrorKind::InvalidState.into_default_parser_error()));
3861 }
3862
3863 let (a, b) = css::nth::parse_nth(input)?;
3864 let nth_data = NthSelectorData {
3865 ty,
3866 is_function: true,
3867 a,
3868 b,
3869 };
3870
3871 if !ty.allows_of_selector() {
3872 return Ok(GenericComponent::Nth(nth_data));
3873 }
3874
3875 if input.try_parse(|i| i.expect_ident_matching(b"of")).is_err() {
3877 return Ok(GenericComponent::Nth(nth_data));
3878 }
3879
3880 let mut child_state = {
3883 let mut s = state;
3884 s.insert(SelectorParsingState::SKIP_DEFAULT_NAMESPACE);
3885 s.insert(SelectorParsingState::DISALLOW_PSEUDOS);
3886 s
3887 };
3888
3889 let selectors = GenericSelectorList::<Impl>::parse_with_state(
3890 parser,
3891 input,
3892 &mut child_state,
3893 ParseErrorRecovery::IgnoreInvalidSelector,
3894 NestingRequirement::None,
3895 )?;
3896
3897 Ok(GenericComponent::NthOf(NthOfSelectorData {
3898 data: nth_data,
3899 selectors: selectors.into_boxed_selectors(),
3900 }))
3901}
3902
3903pub fn parse_is_or_where<Impl: BunSelectorImpl, F>(
3906 parser: &mut SelectorParser,
3907 input: &mut CssParser,
3908 state: &mut SelectorParsingState,
3909 func: F,
3910) -> CResult<GenericComponent<Impl>>
3911where
3912 F: FnOnce(Box<[GenericSelector<Impl>]>) -> GenericComponent<Impl>,
3913{
3914 debug_assert!(parser.parse_is_and_where());
3915 let mut child_state = {
3921 let mut child_state = *state;
3922 child_state.insert(SelectorParsingState::SKIP_DEFAULT_NAMESPACE);
3923 child_state.insert(SelectorParsingState::DISALLOW_PSEUDOS);
3924 child_state
3925 };
3926
3927 let inner = GenericSelectorList::<Impl>::parse_with_state(
3928 parser,
3929 input,
3930 &mut child_state,
3931 parser.is_and_where_error_recovery(),
3932 NestingRequirement::None,
3933 )?;
3934 if child_state.contains(SelectorParsingState::AFTER_NESTING) {
3935 state.insert(SelectorParsingState::AFTER_NESTING);
3936 }
3937
3938 let selector_slice = inner.into_boxed_selectors();
3939
3940 let result = func(selector_slice);
3943
3944 Ok(result)
3945}
3946
3947pub fn parse_has<Impl: BunSelectorImpl>(
3948 parser: &mut SelectorParser,
3949 input: &mut CssParser,
3950 state: &mut SelectorParsingState,
3951) -> CResult<GenericComponent<Impl>> {
3952 let mut child_state = *state;
3953 let inner = GenericSelectorList::<Impl>::parse_relative_with_state(
3954 parser,
3955 input,
3956 &mut child_state,
3957 parser.is_and_where_error_recovery(),
3958 NestingRequirement::None,
3959 )?;
3960
3961 if child_state.contains(SelectorParsingState::AFTER_NESTING) {
3962 state.insert(SelectorParsingState::AFTER_NESTING);
3963 }
3964 Ok(GenericComponent::Has(inner.into_boxed_selectors()))
3965}
3966
3967pub fn parse_negation<Impl: BunSelectorImpl>(
3970 parser: &mut SelectorParser,
3971 input: &mut CssParser,
3972 state: &mut SelectorParsingState,
3973) -> CResult<GenericComponent<Impl>> {
3974 let mut child_state = *state;
3975 child_state.insert(SelectorParsingState::SKIP_DEFAULT_NAMESPACE);
3976 child_state.insert(SelectorParsingState::DISALLOW_PSEUDOS);
3977
3978 let list = GenericSelectorList::<Impl>::parse_with_state(
3979 parser,
3980 input,
3981 &mut child_state,
3982 ParseErrorRecovery::DiscardList,
3983 NestingRequirement::None,
3984 )?;
3985
3986 if child_state.contains(SelectorParsingState::AFTER_NESTING) {
3987 state.insert(SelectorParsingState::AFTER_NESTING);
3988 }
3989
3990 Ok(GenericComponent::Negation(list.into_boxed_selectors()))
3991}
3992
3993pub enum OptionalQName<Impl: SelectorImpl> {
3994 Some(QNamePrefix<Impl>, Option<Str>),
3995 None(Token),
3996}
3997
3998pub enum QNamePrefix<Impl: SelectorImpl> {
3999 ImplicitNoNamespace, ImplicitAnyNamespace, ImplicitDefaultNamespace(Impl::NamespaceUrl), ExplicitNoNamespace, ExplicitAnyNamespace, ExplicitNamespace(Impl::NamespacePrefix, Impl::NamespaceUrl), }
4006
4007pub fn parse_qualified_name<Impl: BunSelectorImpl>(
4012 parser: &mut SelectorParser,
4013 input: &mut CssParser,
4014 in_attr_selector: bool,
4015) -> CResult<OptionalQName<Impl>> {
4016 let start = input.state();
4017
4018 let tok = match input.next_including_whitespace() {
4019 Ok(v) => v.clone(),
4020 Err(e) => {
4021 input.reset(&start);
4022 return Err(e);
4023 }
4024 };
4025 match &tok {
4026 Token::Ident(value) => {
4027 let value = *value;
4028 let after_ident = input.state();
4029 let n = if let Ok(t) = input.next_including_whitespace() {
4030 matches!(t, Token::Delim(d) if *d == b'|' as u32)
4031 } else {
4032 false
4033 };
4034 if n {
4035 let prefix: Impl::NamespacePrefix = Ident { v: value };
4036 let result: Option<Impl::NamespaceUrl> =
4037 parser.namespace_for_prefix(Ident { v: value });
4038 let url: Impl::NamespaceUrl = match result {
4039 Some(url) => url,
4040 None => {
4041 return Err(input.new_custom_error(
4042 SelectorParseErrorKind::UnsupportedPseudoClassOrElement(value)
4043 .into_default_parser_error(),
4044 ));
4045 }
4046 };
4047 return parse_qualified_name_eplicit_namespace_helper::<Impl>(
4048 input,
4049 QNamePrefix::ExplicitNamespace(prefix, url),
4050 in_attr_selector,
4051 );
4052 } else {
4053 input.reset(&after_ident);
4054 if in_attr_selector {
4055 return Ok(OptionalQName::Some(
4056 QNamePrefix::ImplicitNoNamespace,
4057 Some(value),
4058 ));
4059 }
4060 return Ok(parse_qualified_name_default_namespace_helper::<Impl>(
4061 parser,
4062 Some(value),
4063 ));
4064 }
4065 }
4066 Token::Delim(c) => match u8::try_from(*c).ok() {
4067 Some(b'*') => {
4068 let after_star = input.state();
4069 let result = input.next_including_whitespace();
4070 if let Ok(t) = &result {
4071 if matches!(t, Token::Delim(d) if *d == b'|' as u32) {
4072 return parse_qualified_name_eplicit_namespace_helper::<Impl>(
4073 input,
4074 QNamePrefix::ExplicitAnyNamespace,
4075 in_attr_selector,
4076 );
4077 }
4078 }
4079 let result_cloned = result.cloned();
4081 input.reset(&after_star);
4082 if in_attr_selector {
4083 let t = result_cloned?;
4084 return Err(after_star
4085 .source_location()
4086 .new_custom_error(SelectorParseErrorKind::ExpectedBarInAttr(t)));
4087 } else {
4088 return Ok(parse_qualified_name_default_namespace_helper::<Impl>(
4089 parser, None,
4090 ));
4091 }
4092 }
4093 Some(b'|') => {
4094 return parse_qualified_name_eplicit_namespace_helper::<Impl>(
4095 input,
4096 QNamePrefix::ExplicitNoNamespace,
4097 in_attr_selector,
4098 );
4099 }
4100 _ => {}
4101 },
4102 _ => {}
4103 }
4104 input.reset(&start);
4105 Ok(OptionalQName::None(tok))
4106}
4107
4108fn parse_qualified_name_default_namespace_helper<Impl: BunSelectorImpl>(
4109 parser: &mut SelectorParser,
4110 local_name: Option<Str>,
4111) -> OptionalQName<Impl> {
4112 let namespace: QNamePrefix<Impl> = if let Some(url) = parser.default_namespace() {
4113 QNamePrefix::ImplicitDefaultNamespace(url)
4114 } else {
4115 QNamePrefix::ImplicitAnyNamespace
4116 };
4117 OptionalQName::Some(namespace, local_name)
4118}
4119
4120fn parse_qualified_name_eplicit_namespace_helper<Impl: BunSelectorImpl>(
4121 input: &mut CssParser,
4122 namespace: QNamePrefix<Impl>,
4123 in_attr_selector: bool,
4124) -> CResult<OptionalQName<Impl>> {
4125 let location = input.current_source_location();
4126 let t = input.next_including_whitespace()?.clone();
4127 match &t {
4128 Token::Ident(local_name) => return Ok(OptionalQName::Some(namespace, Some(*local_name))),
4129 Token::Delim(c) if *c == b'*' as u32 && !in_attr_selector => {
4132 return Ok(OptionalQName::Some(namespace, None));
4133 }
4134 _ => {}
4135 }
4136 if in_attr_selector {
4137 let e = SelectorParseErrorKind::InvalidQualNameInAttr(t);
4138 return Err(location.new_custom_error(e));
4139 }
4140 Err(location.new_custom_error(SelectorParseErrorKind::ExplicitNamespaceUnexpectedToken(t)))
4141}
4142
4143#[derive(Clone, PartialEq)]
4144pub struct LocalName<Impl: SelectorImpl> {
4145 pub name: Impl::LocalName,
4146 pub lower_name: Impl::LocalName,
4147}
4148
4149impl<Impl: BunSelectorImpl> LocalName<Impl> {
4150 pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
4151 IdentFns::to_css(&self.name, dest)
4152 }
4153 pub fn eql(&self, rhs: &Self) -> bool {
4154 self.name.eql(&rhs.name) && self.lower_name.eql(&rhs.lower_name)
4155 }
4156 pub fn hash(&self, hasher: &mut Wyhash) {
4157 self.name.hash(hasher);
4158 self.lower_name.hash(hasher);
4159 }
4160 pub fn deep_clone(&self) -> Self {
4161 Self {
4162 name: self.name,
4163 lower_name: self.lower_name,
4164 }
4165 }
4166}
4167
4168#[derive(Clone, Copy, PartialEq, Eq)]
4170pub enum AttributeFlags {
4171 CaseSensitive,
4173 AsciiCaseInsensitive,
4175 CaseSensitivityDependsOnName,
4177}
4178
4179impl AttributeFlags {
4180 pub fn to_case_sensitivity(
4181 self,
4182 local_name: &[u8],
4183 have_namespace: bool,
4184 ) -> attrs::ParsedCaseSensitivity {
4185 match self {
4186 AttributeFlags::CaseSensitive => attrs::ParsedCaseSensitivity::ExplicitCaseSensitive,
4187 AttributeFlags::AsciiCaseInsensitive => {
4188 attrs::ParsedCaseSensitivity::AsciiCaseInsensitive
4189 }
4190 AttributeFlags::CaseSensitivityDependsOnName => {
4191 if !have_namespace && is_html_case_insensitive_attribute(local_name) {
4193 return attrs::ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument;
4194 }
4195 attrs::ParsedCaseSensitivity::CaseSensitive
4196 }
4197 }
4198 }
4199}
4200
4201#[inline]
4215fn is_html_case_insensitive_attribute(name: &[u8]) -> bool {
4216 match name.len() {
4220 3 => match name[0] {
4221 b'd' => name == b"dir",
4222 b'r' => matches!(name, b"rel" | b"rev"),
4223 _ => false,
4224 },
4225 4 => match name[0] {
4226 b'a' => name == b"axis",
4227 b'f' => name == b"face",
4228 b'l' => matches!(name, b"lang" | b"link"),
4229 b't' => matches!(name, b"text" | b"type"),
4230 _ => false,
4231 },
4232 5 => match name[0] {
4233 b'a' => matches!(name, b"align" | b"alink"),
4234 b'c' => matches!(name, b"clear" | b"color"),
4235 b'd' => name == b"defer",
4236 b'f' => name == b"frame",
4237 b'm' => name == b"media",
4238 b'r' => name == b"rules",
4239 b's' => matches!(name, b"scope" | b"shape"),
4240 b'v' => name == b"vlink",
4241 _ => false,
4242 },
4243 6 => match name[0] {
4244 b'a' => name == b"accept",
4245 b'm' => name == b"method",
4246 b'n' => matches!(name, b"nohref" | b"nowrap"),
4247 b't' => name == b"target",
4248 b'v' => name == b"valign",
4249 _ => false,
4250 },
4251 7 => match name[0] {
4252 b'b' => name == b"bgcolor",
4253 b'c' => matches!(name, b"charset" | b"checked" | b"compact"),
4254 b'd' => name == b"declare",
4255 b'e' => name == b"enctype",
4256 b'n' => name == b"noshade",
4257 _ => false,
4258 },
4259 8 => match name[0] {
4260 b'c' => name == b"codetype",
4262 b'd' => name == b"disabled",
4263 b'h' => name == b"hreflang",
4264 b'l' => name == b"language",
4265 b'm' => name == b"multiple",
4266 b'n' => name == b"noresize",
4267 b'r' => name == b"readonly",
4268 b's' => name == b"selected",
4269 _ => false,
4270 },
4271 9 => match name[0] {
4272 b'd' => name == b"direction",
4273 b's' => name == b"scrolling",
4274 b'v' => name == b"valuetype",
4275 _ => false,
4276 },
4277 10 => name == b"http_equiv",
4278 14 => name == b"accept_charset",
4279 _ => false,
4280 }
4281}
4282
4283#[derive(Clone)]
4285pub enum ViewTransitionPartName {
4286 All,
4288 Name(CustomIdent),
4290 Class(CustomIdent),
4292}
4293
4294impl ViewTransitionPartName {
4295 pub fn to_css(&self, dest: &mut Printer) -> Result<(), PrintErr> {
4296 let write_ci = |name: &CustomIdent, dest: &mut Printer| -> Result<(), PrintErr> {
4302 dest.serialize_identifier(name.v())
4303 };
4304 match self {
4305 Self::All => dest.write_str("*"),
4306 Self::Name(name) => write_ci(name, dest),
4307 Self::Class(name) => {
4308 dest.write_char(b'.')?;
4309 write_ci(name, dest)
4310 }
4311 }
4312 }
4313
4314 pub fn parse(input: &mut CssParser) -> CResult<ViewTransitionPartName> {
4315 if input.try_parse(|i| i.expect_delim(b'*')).is_ok() {
4316 return Ok(Self::All);
4317 }
4318
4319 if input.try_parse(|i| i.expect_delim(b'.')).is_ok() {
4321 return Ok(Self::Class(CustomIdent::parse(input)?));
4322 }
4323
4324 Ok(Self::Name(CustomIdent::parse(input)?))
4325 }
4326
4327 pub fn eql(&self, rhs: &Self) -> bool {
4328 match (self, rhs) {
4329 (Self::All, Self::All) => true,
4330 (Self::Name(a), Self::Name(b)) | (Self::Class(a), Self::Class(b)) => a.eql(b),
4331 _ => false,
4332 }
4333 }
4334
4335 pub fn hash(&self, hasher: &mut Wyhash) {
4336 match self {
4337 Self::All => hasher.update(&0u32.to_ne_bytes()),
4338 Self::Name(n) => {
4339 hasher.update(&1u32.to_ne_bytes());
4340 n.hash(hasher);
4341 }
4342 Self::Class(n) => {
4343 hasher.update(&2u32.to_ne_bytes());
4344 n.hash(hasher);
4345 }
4346 }
4347 }
4348
4349 pub fn deep_clone(&self) -> Self {
4350 self.clone()
4351 }
4352}
4353
4354pub fn parse_attribute_flags(input: &mut CssParser) -> CResult<AttributeFlags> {
4355 let location = input.current_source_location();
4356 let token = match input.next() {
4357 Ok(v) => v.clone(),
4358 Err(_) => {
4359 return Ok(AttributeFlags::CaseSensitivityDependsOnName);
4362 }
4363 };
4364
4365 let ident = if let Token::Ident(ident) = &token {
4366 *ident
4367 } else {
4368 return Err(location.new_basic_unexpected_token_error(token));
4369 };
4370
4371 if strings::eql_case_insensitive_ascii_check_length(ident, b"i") {
4372 Ok(AttributeFlags::AsciiCaseInsensitive)
4373 } else if strings::eql_case_insensitive_ascii_check_length(ident, b"s") {
4374 Ok(AttributeFlags::CaseSensitive)
4375 } else {
4376 Err(location.new_basic_unexpected_token_error(token))
4377 }
4378}
4379
4380crate::css_eql_partialeq!(NthSelectorData, SpecificityAndFlags, Combinator);
4381
4382