1#![deny(missing_docs)]
6
7use crate::FxHashMap;
10use crate::attr::{AttrIdentifier, AttrValue};
11use crate::computed_value_flags::ComputedValueFlags;
12use crate::derives::*;
13use crate::dom::{OpaqueNode, TElement, TNode};
14use crate::invalidation::element::document_state::InvalidationMatchingData;
15use crate::invalidation::element::element_wrapper::ElementSnapshot;
16use crate::properties::longhands::display::computed_value::T as Display;
17use crate::properties::{ComputedValues, PropertyFlags};
18use crate::selector_parser::AttrValue as SelectorAttrValue;
19use crate::selector_parser::{PseudoElementCascadeType, SelectorParser};
20use crate::values::{AtomIdent, AtomString};
21use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
22use cssparser::{Parser as CssParser, ToCss, match_ignore_ascii_case, serialize_identifier};
23use dom::{DocumentState, ElementState};
24use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
25use selectors::parser::SelectorParseErrorKind;
26use std::fmt;
27use std::mem;
28use std::ops::{Deref, DerefMut};
29use style_traits::{ParseError, StyleParseErrorKind};
30
31#[derive(
35 Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToShmem,
36)]
37#[allow(missing_docs)]
38#[repr(u8)]
39pub enum PseudoElement {
40 After = 0,
42 Before,
43 Selection,
44 FirstLetter,
47 FirstLine,
48
49 Backdrop,
51 DetailsContent,
52 Marker,
53
54 ColorSwatch,
58 FileSelectorButton,
59 Placeholder,
60 SliderFill,
61 SliderThumb,
62 SliderTrack,
63 MozProgressBar,
64
65 ServoTextControlInnerContainer,
67 ServoTextControlInnerEditor,
68
69 ServoAnonymousBox,
71 ServoAnonymousTable,
72 ServoAnonymousTableCell,
73 ServoAnonymousTableRow,
74 ServoTableGrid,
75 ServoTableWrapper,
76}
77
78pub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1;
80
81impl ToCss for PseudoElement {
82 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
83 where
84 W: fmt::Write,
85 {
86 use self::PseudoElement::*;
87 dest.write_str(match *self {
88 After => "::after",
89 Before => "::before",
90 Selection => "::selection",
91 FirstLetter => "::first-letter",
92 FirstLine => "::first-line",
93 Backdrop => "::backdrop",
94 DetailsContent => "::details-content",
95 Marker => "::marker",
96 ColorSwatch => "::color-swatch",
97 FileSelectorButton => "::file-selector-button",
98 Placeholder => "::placeholder",
99 SliderFill => "::slider-fill",
100 SliderTrack => "::slider-track",
101 SliderThumb => "::slider-thumb",
102 MozProgressBar => "::-moz-progress-bar",
103 ServoTextControlInnerContainer => "::-servo-text-control-inner-container",
104 ServoTextControlInnerEditor => "::-servo-text-control-inner-editor",
105 ServoAnonymousBox => "::-servo-anonymous-box",
106 ServoAnonymousTable => "::-servo-anonymous-table",
107 ServoAnonymousTableCell => "::-servo-anonymous-table-cell",
108 ServoAnonymousTableRow => "::-servo-anonymous-table-row",
109 ServoTableGrid => "::-servo-table-grid",
110 ServoTableWrapper => "::-servo-table-wrapper",
111 })
112 }
113}
114
115impl ::selectors::parser::PseudoElement for PseudoElement {
116 fn parses_as_element_backed(&self) -> bool {
117 matches!(self, Self::DetailsContent)
118 }
119}
120
121pub const EAGER_PSEUDO_COUNT: usize = 5;
123
124impl PseudoElement {
125 #[inline]
127 pub fn eager_index(&self) -> usize {
128 debug_assert!(self.is_eager());
129 self.clone() as usize
130 }
131
132 #[inline]
134 pub fn index(&self) -> usize {
135 self.clone() as usize
136 }
137
138 pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
140 Default::default()
141 }
142
143 #[inline]
145 pub fn from_eager_index(i: usize) -> Self {
146 const _: () = assert!(EAGER_PSEUDO_COUNT <= (u8::MAX as usize));
147 assert!(i < EAGER_PSEUDO_COUNT);
148 let result: PseudoElement = unsafe { mem::transmute(i as u8) };
149 debug_assert!(result.is_eager());
150 result
151 }
152
153 #[inline]
155 pub fn is_before_or_after(&self) -> bool {
156 self.is_before() || self.is_after()
157 }
158
159 #[inline]
161 pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
162 false
163 }
164
165 #[inline]
167 pub fn is_marker(&self) -> bool {
168 *self == PseudoElement::Marker
169 }
170
171 #[inline]
173 pub fn is_selection(&self) -> bool {
174 *self == PseudoElement::Selection
175 }
176
177 #[inline]
179 pub fn is_before(&self) -> bool {
180 *self == PseudoElement::Before
181 }
182
183 #[inline]
185 pub fn is_after(&self) -> bool {
186 *self == PseudoElement::After
187 }
188
189 #[inline]
191 pub fn is_first_letter(&self) -> bool {
192 *self == PseudoElement::FirstLetter
193 }
194
195 #[inline]
197 pub fn is_first_line(&self) -> bool {
198 *self == PseudoElement::FirstLine
199 }
200
201 #[inline]
204 pub fn is_color_swatch(&self) -> bool {
205 *self == PseudoElement::ColorSwatch
206 }
207
208 #[inline]
210 pub fn is_eager(&self) -> bool {
211 self.cascade_type() == PseudoElementCascadeType::Eager
212 }
213
214 #[inline]
216 pub fn is_lazy(&self) -> bool {
217 self.cascade_type() == PseudoElementCascadeType::Lazy
218 }
219
220 pub fn is_anon_box(&self) -> bool {
222 self.is_precomputed()
223 }
224
225 #[inline]
228 pub fn skip_item_display_fixup(&self) -> bool {
229 !self.is_before_or_after()
230 }
231
232 #[inline]
234 pub fn is_precomputed(&self) -> bool {
235 self.cascade_type() == PseudoElementCascadeType::Precomputed
236 }
237
238 #[inline]
246 pub fn cascade_type(&self) -> PseudoElementCascadeType {
247 match *self {
248 PseudoElement::After
249 | PseudoElement::Before
250 | PseudoElement::FirstLetter
251 | PseudoElement::FirstLine
252 | PseudoElement::Selection => PseudoElementCascadeType::Eager,
253 PseudoElement::Backdrop
254 | PseudoElement::ColorSwatch
255 | PseudoElement::FileSelectorButton
256 | PseudoElement::Marker
257 | PseudoElement::Placeholder
258 | PseudoElement::DetailsContent
259 | PseudoElement::SliderFill
260 | PseudoElement::SliderThumb
261 | PseudoElement::SliderTrack
262 | PseudoElement::MozProgressBar
263 | PseudoElement::ServoTextControlInnerContainer
264 | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy,
265 PseudoElement::ServoAnonymousBox
266 | PseudoElement::ServoAnonymousTable
267 | PseudoElement::ServoAnonymousTableCell
268 | PseudoElement::ServoAnonymousTableRow
269 | PseudoElement::ServoTableGrid
270 | PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed,
271 }
272 }
273
274 pub fn canonical(&self) -> PseudoElement {
277 self.clone()
278 }
279
280 pub fn pseudo_info(&self) {
282 ()
283 }
284
285 #[inline]
287 pub fn property_restriction(&self) -> PropertyFlags {
288 match self {
289 PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,
290 PseudoElement::FirstLine => PropertyFlags::APPLIES_TO_FIRST_LINE,
291 PseudoElement::Marker if crate::pref!("layout.css.marker.restricted") => {
292 PropertyFlags::APPLIES_TO_MARKER
293 },
294 PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
295 PseudoElement::Selection => PropertyFlags::APPLIES_TO_HIGHLIGHT,
296 _ => PropertyFlags::empty(),
297 }
298 }
299
300 pub fn should_exist(&self, style: &ComputedValues) -> bool {
303 let display = *style.get_box().get_display();
304 if display == Display::None {
305 return false;
306 }
307 if self.is_before_or_after() && style.ineffective_content_property() {
308 return false;
309 }
310
311 true
312 }
313
314 pub fn is_highlight(&self) -> bool {
316 false
317 }
318
319 #[inline]
321 pub fn has_argument(&self) -> bool {
322 false
323 }
324
325 #[inline]
327 pub fn is_target_text(&self) -> bool {
328 false
329 }
330
331 #[inline]
335 pub fn is_lazy_painted_highlight_pseudo(&self) -> bool {
336 self.is_selection() || self.is_highlight() || self.is_target_text()
337 }
338
339 #[inline]
342 pub fn is_element_backed(&self) -> bool {
343 use selectors::parser::PseudoElement;
344 self.parses_as_element_backed()
345 || matches!(
346 self,
347 Self::Placeholder
348 | Self::ColorSwatch
349 | Self::FileSelectorButton
350 | Self::SliderFill
351 | Self::SliderThumb
352 | Self::SliderTrack
353 | Self::MozProgressBar
354 | Self::ServoTextControlInnerContainer
355 | Self::ServoTextControlInnerEditor,
356 )
357 }
358}
359
360pub type Lang = Box<str>;
362
363#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
365pub struct CustomState(pub AtomIdent);
366
367#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
370#[allow(missing_docs)]
371pub enum NonTSPseudoClass {
372 Active,
373 AnyLink,
374 Autofill,
375 Checked,
376 CustomState(CustomState),
378 Default,
379 Defined,
380 Disabled,
381 Enabled,
382 Focus,
383 FocusWithin,
384 FocusVisible,
385 Fullscreen,
386 Hover,
387 InRange,
388 Indeterminate,
389 Invalid,
390 Lang(Lang),
391 Link,
392 Modal,
393 MozInert,
394 MozMeterOptimum,
395 MozMeterSubOptimum,
396 MozMeterSubSubOptimum,
397 Open,
398 Optional,
399 OutOfRange,
400 PlaceholderShown,
401 PopoverOpen,
402 ReadOnly,
403 ReadWrite,
404 Required,
405 ServoNonZeroBorder,
406 Target,
407 UserInvalid,
408 UserValid,
409 Valid,
410 Visited,
411}
412
413impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
414 #[inline]
415 fn is_active_or_hover(&self) -> bool {
416 matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
417 }
418
419 #[inline]
420 fn is_user_action_state(&self) -> bool {
421 matches!(
422 *self,
423 NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
424 )
425 }
426
427 fn visit<V>(&self, _: &mut V) -> bool {
428 true
429 }
430}
431
432impl ToCss for NonTSPseudoClass {
433 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
434 where
435 W: fmt::Write,
436 {
437 use self::NonTSPseudoClass::*;
438 if let Lang(ref lang) = *self {
439 dest.write_str(":lang(")?;
440 serialize_identifier(lang, dest)?;
441 return dest.write_char(')');
442 }
443
444 dest.write_str(match *self {
445 Self::Active => ":active",
446 Self::AnyLink => ":any-link",
447 Self::Autofill => ":autofill",
448 Self::Checked => ":checked",
449 Self::CustomState(ref state) => {
450 dest.write_str(":state(")?;
451 state.0.to_css(dest)?;
452 return dest.write_char(')');
453 },
454 Self::Default => ":default",
455 Self::Defined => ":defined",
456 Self::Disabled => ":disabled",
457 Self::Enabled => ":enabled",
458 Self::Focus => ":focus",
459 Self::FocusVisible => ":focus-visible",
460 Self::FocusWithin => ":focus-within",
461 Self::Fullscreen => ":fullscreen",
462 Self::Hover => ":hover",
463 Self::InRange => ":in-range",
464 Self::Indeterminate => ":indeterminate",
465 Self::Invalid => ":invalid",
466 Self::Link => ":link",
467 Self::Modal => ":modal",
468 Self::MozInert => ":-moz-inert",
469 Self::MozMeterOptimum => ":-moz-meter-optimum",
470 Self::MozMeterSubOptimum => ":-moz-meter-sub-optimum",
471 Self::MozMeterSubSubOptimum => ":-moz-meter-sub-sub-optimum",
472 Self::Open => ":open",
473 Self::Optional => ":optional",
474 Self::OutOfRange => ":out-of-range",
475 Self::PlaceholderShown => ":placeholder-shown",
476 Self::PopoverOpen => ":popover-open",
477 Self::ReadOnly => ":read-only",
478 Self::ReadWrite => ":read-write",
479 Self::Required => ":required",
480 Self::ServoNonZeroBorder => ":-servo-nonzero-border",
481 Self::Target => ":target",
482 Self::UserInvalid => ":user-invalid",
483 Self::UserValid => ":user-valid",
484 Self::Valid => ":valid",
485 Self::Visited => ":visited",
486 Self::Lang(_) => unreachable!(),
487 })
488 }
489}
490
491impl NonTSPseudoClass {
492 pub fn state_flag(&self) -> ElementState {
495 match *self {
496 Self::Active => ElementState::ACTIVE,
497 Self::AnyLink => ElementState::VISITED_OR_UNVISITED,
498 Self::Autofill => ElementState::AUTOFILL,
499 Self::Checked => ElementState::CHECKED,
500 Self::Default => ElementState::DEFAULT,
501 Self::Defined => ElementState::DEFINED,
502 Self::Disabled => ElementState::DISABLED,
503 Self::Enabled => ElementState::ENABLED,
504 Self::Focus => ElementState::FOCUS,
505 Self::FocusVisible => ElementState::FOCUSRING,
506 Self::FocusWithin => ElementState::FOCUS_WITHIN,
507 Self::Fullscreen => ElementState::FULLSCREEN,
508 Self::Hover => ElementState::HOVER,
509 Self::InRange => ElementState::INRANGE,
510 Self::Indeterminate => ElementState::INDETERMINATE,
511 Self::Invalid => ElementState::INVALID,
512 Self::Link => ElementState::UNVISITED,
513 Self::Modal => ElementState::MODAL,
514 Self::MozInert => ElementState::INERT,
515 Self::MozMeterOptimum => ElementState::OPTIMUM,
516 Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM,
517 Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM,
518 Self::Open => ElementState::OPEN,
519 Self::Optional => ElementState::OPTIONAL_,
520 Self::OutOfRange => ElementState::OUTOFRANGE,
521 Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
522 Self::PopoverOpen => ElementState::POPOVER_OPEN,
523 Self::ReadOnly => ElementState::READONLY,
524 Self::ReadWrite => ElementState::READWRITE,
525 Self::Required => ElementState::REQUIRED,
526 Self::Target => ElementState::URLTARGET,
527 Self::UserInvalid => ElementState::USER_INVALID,
528 Self::UserValid => ElementState::USER_VALID,
529 Self::Valid => ElementState::VALID,
530 Self::Visited => ElementState::VISITED,
531 Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => {
532 ElementState::empty()
533 },
534 }
535 }
536
537 pub fn document_state_flag(&self) -> DocumentState {
539 DocumentState::empty()
540 }
541
542 pub fn needs_cache_revalidation(&self) -> bool {
544 self.state_flag().is_empty()
545 }
546}
547
548#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
551pub struct SelectorImpl;
552
553#[derive(Debug, Default)]
556pub struct ExtraMatchingData<'a> {
557 pub invalidation_data: InvalidationMatchingData,
559
560 pub cascade_input_flags: ComputedValueFlags,
563
564 pub originating_element_style: Option<&'a ComputedValues>,
567}
568
569impl ::selectors::SelectorImpl for SelectorImpl {
570 type PseudoElement = PseudoElement;
571 type NonTSPseudoClass = NonTSPseudoClass;
572
573 type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
574 type AttrValue = AtomString;
575 type Identifier = AtomIdent;
576 type LocalName = LocalName;
577 type NamespacePrefix = Prefix;
578 type NamespaceUrl = Namespace;
579 type BorrowedLocalName = web_atoms::LocalName;
580 type BorrowedNamespaceUrl = web_atoms::Namespace;
581}
582
583impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
584 type Impl = SelectorImpl;
585 type Error = StyleParseErrorKind;
586
587 #[inline]
588 fn parse_nth_child_of(&self) -> bool {
589 crate::pref!("layout.css.nth-child-of.enabled")
590 }
591
592 #[inline]
593 fn parse_is_and_where(&self) -> bool {
594 true
595 }
596
597 #[inline]
598 fn parse_has(&self) -> bool {
599 crate::pref!("layout.css.has-selector.enabled")
600 }
601
602 #[inline]
603 fn parse_parent_selector(&self) -> bool {
604 true
605 }
606
607 #[inline]
608 fn parse_part(&self) -> bool {
609 true
610 }
611
612 #[inline]
613 fn allow_forgiving_selectors(&self) -> bool {
614 !self.for_supports_rule
615 }
616
617 fn parse_non_ts_pseudo_class(&self, name: &str) -> Result<NonTSPseudoClass, ParseError> {
618 let pseudo_class = match_ignore_ascii_case! { &name,
619 "active" => NonTSPseudoClass::Active,
620 "any-link" => NonTSPseudoClass::AnyLink,
621 "autofill" => NonTSPseudoClass::Autofill,
622 "checked" => NonTSPseudoClass::Checked,
623 "default" => NonTSPseudoClass::Default,
624 "defined" => NonTSPseudoClass::Defined,
625 "disabled" => NonTSPseudoClass::Disabled,
626 "enabled" => NonTSPseudoClass::Enabled,
627 "focus" => NonTSPseudoClass::Focus,
628 "focus-visible" => NonTSPseudoClass::FocusVisible,
629 "focus-within" => NonTSPseudoClass::FocusWithin,
630 "fullscreen" => NonTSPseudoClass::Fullscreen,
631 "hover" => NonTSPseudoClass::Hover,
632 "indeterminate" => NonTSPseudoClass::Indeterminate,
633 "invalid" => NonTSPseudoClass::Invalid,
634 "link" => NonTSPseudoClass::Link,
635 "modal" => NonTSPseudoClass::Modal,
636 "open" => NonTSPseudoClass::Open,
637 "optional" => NonTSPseudoClass::Optional,
638 "out-of-range" => NonTSPseudoClass::OutOfRange,
639 "placeholder-shown" => NonTSPseudoClass::PlaceholderShown,
640 "popover-open" => NonTSPseudoClass::PopoverOpen,
641 "read-only" => NonTSPseudoClass::ReadOnly,
642 "read-write" => NonTSPseudoClass::ReadWrite,
643 "required" => NonTSPseudoClass::Required,
644 "target" => NonTSPseudoClass::Target,
645 "user-invalid" => NonTSPseudoClass::UserInvalid,
646 "user-valid" => NonTSPseudoClass::UserValid,
647 "valid" => NonTSPseudoClass::Valid,
648 "visited" => NonTSPseudoClass::Visited,
649 "-moz-inert" => {
650 if !self.in_user_agent_stylesheet() {
651 return Err(ParseError::custom(
652 SelectorParseErrorKind::UnexpectedIdent
653 ))
654 }
655 NonTSPseudoClass::MozInert
656 },
657 "-moz-meter-optimum" => NonTSPseudoClass::MozMeterOptimum,
658 "-moz-meter-sub-optimum" => NonTSPseudoClass::MozMeterSubOptimum,
659 "-moz-meter-sub-sub-optimum" => NonTSPseudoClass::MozMeterSubSubOptimum,
660 "-servo-nonzero-border" => {
661 if !self.in_user_agent_stylesheet() {
662 return Err(ParseError::custom(
663 SelectorParseErrorKind::UnexpectedIdent
664 ))
665 }
666 NonTSPseudoClass::ServoNonZeroBorder
667 },
668 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
669 };
670
671 Ok(pseudo_class)
672 }
673
674 fn parse_non_ts_functional_pseudo_class(
675 &self,
676 name: &str,
677 parser: &mut CssParser<'i>,
678 after_part: bool,
679 ) -> Result<NonTSPseudoClass, ParseError> {
680 let pseudo_class = match_ignore_ascii_case! { &name,
681 "lang" if !after_part => {
682 NonTSPseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().into())
683 },
684 "state" => {
685 let result = AtomIdent::from(parser.expect_ident()?.as_ref());
686 NonTSPseudoClass::CustomState(CustomState(result))
687 },
688 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
689 };
690
691 Ok(pseudo_class)
692 }
693
694 fn parse_pseudo_element(&self, name: &str) -> Result<PseudoElement, ParseError> {
695 use self::PseudoElement::*;
696 let pseudo_element = match_ignore_ascii_case! { &name,
697 "before" => Before,
698 "after" => After,
699 "backdrop" => Backdrop,
700 "selection" => Selection,
701 "file-selector-button" => FileSelectorButton,
702 "first-letter" => FirstLetter,
703 "first-line" if crate::pref!("layout.css.first-line.enabled") => FirstLine,
704 "marker" => Marker,
705 "details-content" => DetailsContent,
706 "color-swatch" => ColorSwatch,
707 "placeholder" => Placeholder,
708 "-servo-text-control-inner-container" => {
709 if !self.in_user_agent_stylesheet() {
710 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
711 }
712 ServoTextControlInnerContainer
713 },
714 "-servo-text-control-inner-editor" => {
715 if !self.in_user_agent_stylesheet() {
716 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
717 }
718 ServoTextControlInnerEditor
719 },
720 "slider-fill" => SliderFill,
721 "slider-thumb" => SliderThumb,
722 "slider-track" => SliderTrack,
723 "-moz-progress-bar" => MozProgressBar,
724 "-servo-anonymous-box" => {
725 if !self.in_user_agent_stylesheet() {
726 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
727 }
728 ServoAnonymousBox
729 },
730 "-servo-anonymous-table" => {
731 if !self.in_user_agent_stylesheet() {
732 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
733 }
734 ServoAnonymousTable
735 },
736 "-servo-anonymous-table-row" => {
737 if !self.in_user_agent_stylesheet() {
738 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
739 }
740 ServoAnonymousTableRow
741 },
742 "-servo-anonymous-table-cell" => {
743 if !self.in_user_agent_stylesheet() {
744 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
745 }
746 ServoAnonymousTableCell
747 },
748 "-servo-table-grid" => {
749 if !self.in_user_agent_stylesheet() {
750 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
751 }
752 ServoTableGrid
753 },
754 "-servo-table-wrapper" => {
755 if !self.in_user_agent_stylesheet() {
756 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
757 }
758 ServoTableWrapper
759 },
760 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
761
762 };
763
764 Ok(pseudo_element)
765 }
766
767 fn default_namespace(&self) -> Option<Namespace> {
768 self.namespaces.default.as_ref().map(|ns| ns.clone())
769 }
770
771 fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
772 self.namespaces.prefixes.get(prefix).cloned()
773 }
774
775 fn parse_host(&self) -> bool {
776 true
777 }
778
779 fn parse_slotted(&self) -> bool {
780 true
781 }
782}
783
784impl SelectorImpl {
785 #[inline]
788 pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
789 where
790 F: FnMut(PseudoElement),
791 {
792 for i in 0..EAGER_PSEUDO_COUNT {
793 fun(PseudoElement::from_eager_index(i));
794 }
795 }
796}
797
798#[derive(Debug)]
800pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
801
802impl SnapshotMap {
803 pub fn new() -> Self {
805 SnapshotMap(FxHashMap::default())
806 }
807
808 pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
810 self.0.get(&el.as_node().opaque())
811 }
812}
813
814impl Deref for SnapshotMap {
815 type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
816
817 fn deref(&self) -> &Self::Target {
818 &self.0
819 }
820}
821
822impl DerefMut for SnapshotMap {
823 fn deref_mut(&mut self) -> &mut Self::Target {
824 &mut self.0
825 }
826}
827
828#[derive(Debug, Default, MallocSizeOf)]
830pub struct ServoElementSnapshot {
831 pub state: Option<ElementState>,
833 pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
835 pub changed_attrs: Vec<LocalName>,
837 pub class_changed: bool,
839 pub id_changed: bool,
841 pub other_attributes_changed: bool,
843}
844
845impl ServoElementSnapshot {
846 pub fn new() -> Self {
848 Self::default()
849 }
850
851 pub fn id_changed(&self) -> bool {
853 self.id_changed
854 }
855
856 pub fn class_changed(&self) -> bool {
858 self.class_changed
859 }
860
861 pub fn other_attr_changed(&self) -> bool {
863 self.other_attributes_changed
864 }
865
866 fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
867 self.attrs
868 .as_ref()?
869 .iter()
870 .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
871 .map(|&(_, ref v)| v)
872 }
873
874 #[inline]
876 pub fn each_attr_changed<F>(&self, mut callback: F)
877 where
878 F: FnMut(&LocalName),
879 {
880 for name in &self.changed_attrs {
881 callback(name)
882 }
883 }
884
885 fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
886 where
887 F: FnMut(&AttrValue) -> bool,
888 {
889 self.attrs.as_ref().is_some_and(|attrs| {
890 attrs
891 .iter()
892 .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
893 })
894 }
895}
896
897impl ElementSnapshot for ServoElementSnapshot {
898 fn state(&self) -> Option<ElementState> {
899 self.state.clone()
900 }
901
902 fn has_attrs(&self) -> bool {
903 self.attrs.is_some()
904 }
905
906 fn id_attr(&self) -> Option<&Atom> {
907 self.get_attr(&ns!(), &local_name!("id"))
908 .map(|v| v.as_atom())
909 }
910
911 fn is_part(&self, part_name: &AtomIdent) -> bool {
912 self.get_attr(&ns!(), &local_name!("part"))
913 .is_some_and(|v| {
914 v.as_tokens()
915 .iter()
916 .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))
917 })
918 }
919
920 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
921 None
922 }
923
924 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
925 self.get_attr(&ns!(), &local_name!("class"))
926 .map_or(false, |v| {
927 v.as_tokens()
928 .iter()
929 .any(|atom| case_sensitivity.eq_atom(atom, name))
930 })
931 }
932
933 fn each_class<F>(&self, mut callback: F)
934 where
935 F: FnMut(&AtomIdent),
936 {
937 if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
938 for class in v.as_tokens() {
939 callback(AtomIdent::cast(class));
940 }
941 }
942 }
943
944 fn lang_attr(&self) -> Option<SelectorAttrValue> {
945 self.get_attr(&ns!(xml), &local_name!("lang"))
946 .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
947 .map(|v| SelectorAttrValue::from(v as &str))
948 }
949
950 #[inline]
952 fn has_custom_states(&self) -> bool {
953 false
954 }
955
956 #[inline]
958 fn has_custom_state(&self, _state: &AtomIdent) -> bool {
959 false
960 }
961
962 #[inline]
963 fn each_custom_state<F>(&self, mut _callback: F)
964 where
965 F: FnMut(&AtomIdent),
966 {
967 }
968}
969
970impl ServoElementSnapshot {
971 pub fn attr_matches(
973 &self,
974 ns: &NamespaceConstraint<&Namespace>,
975 local_name: &LocalName,
976 operation: &AttrSelectorOperation<&AtomString>,
977 ) -> bool {
978 match *ns {
979 NamespaceConstraint::Specific(ref ns) => self
980 .get_attr(ns, local_name)
981 .map_or(false, |value| value.eval_selector(operation)),
982 NamespaceConstraint::Any => {
983 self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
984 },
985 }
986 }
987}
988
989pub fn extended_filtering(tag: &str, range: &str) -> bool {
992 range.split(',').any(|lang_range| {
993 let mut range_subtags = lang_range.split('\x2d');
995 let mut tag_subtags = tag.split('\x2d');
996
997 if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
1000 if !(range_subtag.eq_ignore_ascii_case(tag_subtag)
1001 || range_subtag.eq_ignore_ascii_case("*"))
1002 {
1003 return false;
1004 }
1005 }
1006
1007 let mut current_tag_subtag = tag_subtags.next();
1008
1009 for range_subtag in range_subtags {
1011 if range_subtag == "*" {
1013 continue;
1014 }
1015 match current_tag_subtag.clone() {
1016 Some(tag_subtag) => {
1017 if range_subtag.eq_ignore_ascii_case(tag_subtag) {
1019 current_tag_subtag = tag_subtags.next();
1020 continue;
1021 }
1022 if tag_subtag.len() == 1 {
1024 return false;
1025 }
1026 current_tag_subtag = tag_subtags.next();
1028 if current_tag_subtag.is_none() {
1029 return false;
1030 }
1031 },
1032 None => {
1034 return false;
1035 },
1036 }
1037 }
1038 true
1040 })
1041}