Skip to main content

style/servo/
selector_parser.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5#![deny(missing_docs)]
6
7//! Servo's selector parser.
8
9use crate::attr::{AttrIdentifier, AttrValue};
10use crate::computed_value_flags::ComputedValueFlags;
11use crate::derives::*;
12use crate::dom::{OpaqueNode, TElement, TNode};
13use crate::invalidation::element::document_state::InvalidationMatchingData;
14use crate::invalidation::element::element_wrapper::ElementSnapshot;
15use crate::properties::longhands::display::computed_value::T as Display;
16use crate::properties::{ComputedValues, PropertyFlags};
17use crate::selector_parser::AttrValue as SelectorAttrValue;
18use crate::selector_parser::{PseudoElementCascadeType, SelectorParser};
19use crate::values::{AtomIdent, AtomString};
20use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
21use cssparser::{
22    match_ignore_ascii_case, serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation,
23    ToCss,
24};
25use dom::{DocumentState, ElementState};
26use rustc_hash::FxHashMap;
27use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
28use selectors::parser::SelectorParseErrorKind;
29use selectors::visitor::SelectorVisitor;
30use std::fmt;
31use std::mem;
32use std::ops::{Deref, DerefMut};
33use style_traits::{ParseError, StyleParseErrorKind};
34
35/// A pseudo-element, both public and private.
36///
37/// NB: If you add to this list, be sure to update `each_simple_pseudo_element` too.
38#[derive(
39    Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToShmem,
40)]
41#[allow(missing_docs)]
42#[repr(usize)]
43pub enum PseudoElement {
44    // Eager pseudos. Keep these first so that eager_index() works.
45    After = 0,
46    Before,
47    Selection,
48    // If/when :first-letter is added, update is_first_letter accordingly.
49
50    // If/when :first-line is added, update is_first_line accordingly.
51
52    // If/when ::first-letter or ::first-line are added, adjust our
53    // property_restriction implementation to do property filtering for them.
54    // Also, make sure the UA sheet has the !important rules some of the
55    // APPLIES_TO_PLACEHOLDER properties expect!
56    FirstLetter,
57
58    // Non-eager pseudos.
59    Backdrop,
60    DetailsSummary,
61    DetailsContent,
62    Marker,
63
64    // Implemented pseudos. These pseudo elements are representing the
65    // elements within an UA shadow DOM, and matching the elements with
66    // their appropriate styles.
67    ColorSwatch,
68    FileSelectorButton,
69    Placeholder,
70    SliderFill,
71    SliderThumb,
72    SliderTrack,
73
74    // Private, Servo-specific implemented pseudos. Only matchable in UA sheet.
75    ServoTextControlInnerContainer,
76    ServoTextControlInnerEditor,
77
78    // Other Servo-specific pseudos.
79    ServoAnonymousBox,
80    ServoAnonymousTable,
81    ServoAnonymousTableCell,
82    ServoAnonymousTableRow,
83    ServoTableGrid,
84    ServoTableWrapper,
85}
86
87/// The count of all pseudo-elements.
88pub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1;
89
90impl ToCss for PseudoElement {
91    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
92    where
93        W: fmt::Write,
94    {
95        use self::PseudoElement::*;
96        dest.write_str(match *self {
97            After => "::after",
98            Before => "::before",
99            Selection => "::selection",
100            FirstLetter => "::first-letter",
101            Backdrop => "::backdrop",
102            DetailsSummary => "::-servo-details-summary",
103            DetailsContent => "::details-content",
104            Marker => "::marker",
105            ColorSwatch => "::color-swatch",
106            FileSelectorButton => "::file-selector-button",
107            Placeholder => "::placeholder",
108            SliderFill => "::slider-fill",
109            SliderTrack => "::slider-track",
110            SliderThumb => "::slider-thumb",
111            ServoTextControlInnerContainer => "::-servo-text-control-inner-container",
112            ServoTextControlInnerEditor => "::-servo-text-control-inner-editor",
113            ServoAnonymousBox => "::-servo-anonymous-box",
114            ServoAnonymousTable => "::-servo-anonymous-table",
115            ServoAnonymousTableCell => "::-servo-anonymous-table-cell",
116            ServoAnonymousTableRow => "::-servo-anonymous-table-row",
117            ServoTableGrid => "::-servo-table-grid",
118            ServoTableWrapper => "::-servo-table-wrapper",
119        })
120    }
121}
122
123impl ::selectors::parser::PseudoElement for PseudoElement {
124    type Impl = SelectorImpl;
125}
126
127/// The number of eager pseudo-elements. Keep this in sync with cascade_type.
128pub const EAGER_PSEUDO_COUNT: usize = 4;
129
130impl PseudoElement {
131    /// Gets the canonical index of this eagerly-cascaded pseudo-element.
132    #[inline]
133    pub fn eager_index(&self) -> usize {
134        debug_assert!(self.is_eager());
135        self.clone() as usize
136    }
137
138    /// An index for this pseudo-element to be indexed in an enumerated array.
139    #[inline]
140    pub fn index(&self) -> usize {
141        self.clone() as usize
142    }
143
144    /// An array of `None`, one per pseudo-element.
145    pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
146        Default::default()
147    }
148
149    /// Creates a pseudo-element from an eager index.
150    #[inline]
151    pub fn from_eager_index(i: usize) -> Self {
152        assert!(i < EAGER_PSEUDO_COUNT);
153        let result: PseudoElement = unsafe { mem::transmute(i) };
154        debug_assert!(result.is_eager());
155        result
156    }
157
158    /// Whether the current pseudo element is ::before or ::after.
159    #[inline]
160    pub fn is_before_or_after(&self) -> bool {
161        self.is_before() || self.is_after()
162    }
163
164    /// Whether this is an unknown ::-webkit- pseudo-element.
165    #[inline]
166    pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
167        false
168    }
169
170    /// Whether this pseudo-element is the ::marker pseudo.
171    #[inline]
172    pub fn is_marker(&self) -> bool {
173        *self == PseudoElement::Marker
174    }
175
176    /// Whether this pseudo-element is the ::selection pseudo.
177    #[inline]
178    pub fn is_selection(&self) -> bool {
179        *self == PseudoElement::Selection
180    }
181
182    /// Whether this pseudo-element is the ::before pseudo.
183    #[inline]
184    pub fn is_before(&self) -> bool {
185        *self == PseudoElement::Before
186    }
187
188    /// Whether this pseudo-element is the ::after pseudo.
189    #[inline]
190    pub fn is_after(&self) -> bool {
191        *self == PseudoElement::After
192    }
193
194    /// Whether the current pseudo element is :first-letter
195    #[inline]
196    pub fn is_first_letter(&self) -> bool {
197        *self == PseudoElement::FirstLetter
198    }
199
200    /// Whether the current pseudo element is :first-line
201    #[inline]
202    pub fn is_first_line(&self) -> bool {
203        false
204    }
205
206    /// Whether this pseudo-element is representing the color swatch
207    /// inside an `<input>` element.
208    #[inline]
209    pub fn is_color_swatch(&self) -> bool {
210        *self == PseudoElement::ColorSwatch
211    }
212
213    /// Whether this pseudo-element is eagerly-cascaded.
214    #[inline]
215    pub fn is_eager(&self) -> bool {
216        self.cascade_type() == PseudoElementCascadeType::Eager
217    }
218
219    /// Whether this pseudo-element is lazily-cascaded.
220    #[inline]
221    pub fn is_lazy(&self) -> bool {
222        self.cascade_type() == PseudoElementCascadeType::Lazy
223    }
224
225    /// Whether this pseudo-element is for an anonymous box.
226    pub fn is_anon_box(&self) -> bool {
227        self.is_precomputed()
228    }
229
230    /// Whether this pseudo-element skips flex/grid container display-based
231    /// fixup.
232    #[inline]
233    pub fn skip_item_display_fixup(&self) -> bool {
234        !self.is_before_or_after()
235    }
236
237    /// Whether this pseudo-element is precomputed.
238    #[inline]
239    pub fn is_precomputed(&self) -> bool {
240        self.cascade_type() == PseudoElementCascadeType::Precomputed
241    }
242
243    /// Returns which kind of cascade type has this pseudo.
244    ///
245    /// See the documentation for `PseudoElementCascadeType` for how we choose
246    /// which cascade type to use.
247    ///
248    /// Note: Keep eager pseudos in sync with `EAGER_PSEUDO_COUNT` and
249    /// `EMPTY_PSEUDO_ARRAY` in `style/data.rs`
250    #[inline]
251    pub fn cascade_type(&self) -> PseudoElementCascadeType {
252        match *self {
253            PseudoElement::After
254            | PseudoElement::Before
255            | PseudoElement::FirstLetter
256            | PseudoElement::Selection => PseudoElementCascadeType::Eager,
257            PseudoElement::Backdrop
258            | PseudoElement::ColorSwatch
259            | PseudoElement::FileSelectorButton
260            | PseudoElement::DetailsSummary
261            | PseudoElement::Marker
262            | PseudoElement::Placeholder
263            | PseudoElement::DetailsContent
264            | PseudoElement::SliderFill
265            | PseudoElement::SliderThumb
266            | PseudoElement::SliderTrack
267            | PseudoElement::ServoTextControlInnerContainer
268            | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy,
269            PseudoElement::ServoAnonymousBox
270            | PseudoElement::ServoAnonymousTable
271            | PseudoElement::ServoAnonymousTableCell
272            | PseudoElement::ServoAnonymousTableRow
273            | PseudoElement::ServoTableGrid
274            | PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed,
275        }
276    }
277
278    /// Covert non-canonical pseudo-element to canonical one, and keep a
279    /// canonical one as it is.
280    pub fn canonical(&self) -> PseudoElement {
281        self.clone()
282    }
283
284    /// Stub, only Gecko needs this
285    pub fn pseudo_info(&self) {
286        ()
287    }
288
289    /// Property flag that properties must have to apply to this pseudo-element.
290    #[inline]
291    pub fn property_restriction(&self) -> Option<PropertyFlags> {
292        Some(match self {
293            PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,
294            PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => {
295                PropertyFlags::APPLIES_TO_MARKER
296            },
297            PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
298            _ => return None,
299        })
300    }
301
302    /// Whether this pseudo-element should actually exist if it has
303    /// the given styles.
304    pub fn should_exist(&self, style: &ComputedValues) -> bool {
305        let display = style.get_box().clone_display();
306        if display == Display::None {
307            return false;
308        }
309        if self.is_before_or_after() && style.ineffective_content_property() {
310            return false;
311        }
312
313        true
314    }
315
316    /// Whether this pseudo-element is the ::highlight pseudo.
317    pub fn is_highlight(&self) -> bool {
318        false
319    }
320
321    /// Whether this pseudo-element is the ::target-text pseudo.
322    #[inline]
323    pub fn is_target_text(&self) -> bool {
324        false
325    }
326
327    /// Whether this is a highlight pseudo-element that is styled lazily during
328    /// painting rather than during the restyle traversal. These pseudos need
329    /// explicit repaint triggering when their styles change.
330    #[inline]
331    pub fn is_lazy_painted_highlight_pseudo(&self) -> bool {
332        self.is_selection() || self.is_highlight() || self.is_target_text()
333    }
334}
335
336/// The type used for storing `:lang` arguments.
337pub type Lang = Box<str>;
338
339/// The type used to store the state argument to the `:state` pseudo-class.
340#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
341pub struct CustomState(pub AtomIdent);
342
343/// A non tree-structural pseudo-class.
344/// See https://drafts.csswg.org/selectors-4/#structural-pseudos
345#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
346#[allow(missing_docs)]
347pub enum NonTSPseudoClass {
348    Active,
349    AnyLink,
350    Autofill,
351    Checked,
352    /// The :state` pseudo-class.
353    CustomState(CustomState),
354    Default,
355    Defined,
356    Disabled,
357    Enabled,
358    Focus,
359    FocusWithin,
360    FocusVisible,
361    Fullscreen,
362    Hover,
363    InRange,
364    Indeterminate,
365    Invalid,
366    Lang(Lang),
367    Link,
368    Modal,
369    MozMeterOptimum,
370    MozMeterSubOptimum,
371    MozMeterSubSubOptimum,
372    Open,
373    Optional,
374    OutOfRange,
375    PlaceholderShown,
376    PopoverOpen,
377    ReadOnly,
378    ReadWrite,
379    Required,
380    ServoNonZeroBorder,
381    Target,
382    UserInvalid,
383    UserValid,
384    Valid,
385    Visited,
386}
387
388impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
389    type Impl = SelectorImpl;
390
391    #[inline]
392    fn is_active_or_hover(&self) -> bool {
393        matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
394    }
395
396    #[inline]
397    fn is_user_action_state(&self) -> bool {
398        matches!(
399            *self,
400            NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
401        )
402    }
403
404    fn visit<V>(&self, _: &mut V) -> bool
405    where
406        V: SelectorVisitor<Impl = Self::Impl>,
407    {
408        true
409    }
410}
411
412impl ToCss for NonTSPseudoClass {
413    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
414    where
415        W: fmt::Write,
416    {
417        use self::NonTSPseudoClass::*;
418        if let Lang(ref lang) = *self {
419            dest.write_str(":lang(")?;
420            serialize_identifier(lang, dest)?;
421            return dest.write_char(')');
422        }
423
424        dest.write_str(match *self {
425            Self::Active => ":active",
426            Self::AnyLink => ":any-link",
427            Self::Autofill => ":autofill",
428            Self::Checked => ":checked",
429            Self::CustomState(ref state) => {
430                dest.write_str(":state(")?;
431                state.0.to_css(dest)?;
432                return dest.write_char(')');
433            },
434            Self::Default => ":default",
435            Self::Defined => ":defined",
436            Self::Disabled => ":disabled",
437            Self::Enabled => ":enabled",
438            Self::Focus => ":focus",
439            Self::FocusVisible => ":focus-visible",
440            Self::FocusWithin => ":focus-within",
441            Self::Fullscreen => ":fullscreen",
442            Self::Hover => ":hover",
443            Self::InRange => ":in-range",
444            Self::Indeterminate => ":indeterminate",
445            Self::Invalid => ":invalid",
446            Self::Link => ":link",
447            Self::Modal => ":modal",
448            Self::MozMeterOptimum => ":-moz-meter-optimum",
449            Self::MozMeterSubOptimum => ":-moz-meter-sub-optimum",
450            Self::MozMeterSubSubOptimum => ":-moz-meter-sub-sub-optimum",
451            Self::Open => ":open",
452            Self::Optional => ":optional",
453            Self::OutOfRange => ":out-of-range",
454            Self::PlaceholderShown => ":placeholder-shown",
455            Self::PopoverOpen => ":popover-open",
456            Self::ReadOnly => ":read-only",
457            Self::ReadWrite => ":read-write",
458            Self::Required => ":required",
459            Self::ServoNonZeroBorder => ":-servo-nonzero-border",
460            Self::Target => ":target",
461            Self::UserInvalid => ":user-invalid",
462            Self::UserValid => ":user-valid",
463            Self::Valid => ":valid",
464            Self::Visited => ":visited",
465            Self::Lang(_) => unreachable!(),
466        })
467    }
468}
469
470impl NonTSPseudoClass {
471    /// Gets a given state flag for this pseudo-class. This is used to do
472    /// selector matching, and it's set from the DOM.
473    pub fn state_flag(&self) -> ElementState {
474        match *self {
475            Self::Active => ElementState::ACTIVE,
476            Self::AnyLink => ElementState::VISITED_OR_UNVISITED,
477            Self::Autofill => ElementState::AUTOFILL,
478            Self::Checked => ElementState::CHECKED,
479            Self::Default => ElementState::DEFAULT,
480            Self::Defined => ElementState::DEFINED,
481            Self::Disabled => ElementState::DISABLED,
482            Self::Enabled => ElementState::ENABLED,
483            Self::Focus => ElementState::FOCUS,
484            Self::FocusVisible => ElementState::FOCUSRING,
485            Self::FocusWithin => ElementState::FOCUS_WITHIN,
486            Self::Fullscreen => ElementState::FULLSCREEN,
487            Self::Hover => ElementState::HOVER,
488            Self::InRange => ElementState::INRANGE,
489            Self::Indeterminate => ElementState::INDETERMINATE,
490            Self::Invalid => ElementState::INVALID,
491            Self::Link => ElementState::UNVISITED,
492            Self::Modal => ElementState::MODAL,
493            Self::MozMeterOptimum => ElementState::OPTIMUM,
494            Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM,
495            Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM,
496            Self::Open => ElementState::OPEN,
497            Self::Optional => ElementState::OPTIONAL_,
498            Self::OutOfRange => ElementState::OUTOFRANGE,
499            Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
500            Self::PopoverOpen => ElementState::POPOVER_OPEN,
501            Self::ReadOnly => ElementState::READONLY,
502            Self::ReadWrite => ElementState::READWRITE,
503            Self::Required => ElementState::REQUIRED,
504            Self::Target => ElementState::URLTARGET,
505            Self::UserInvalid => ElementState::USER_INVALID,
506            Self::UserValid => ElementState::USER_VALID,
507            Self::Valid => ElementState::VALID,
508            Self::Visited => ElementState::VISITED,
509            Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => {
510                ElementState::empty()
511            },
512        }
513    }
514
515    /// Get the document state flag associated with a pseudo-class, if any.
516    pub fn document_state_flag(&self) -> DocumentState {
517        DocumentState::empty()
518    }
519
520    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
521    pub fn needs_cache_revalidation(&self) -> bool {
522        self.state_flag().is_empty()
523    }
524}
525
526/// The abstract struct we implement the selector parser implementation on top
527/// of.
528#[derive(Clone, Debug, PartialEq)]
529#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
530pub struct SelectorImpl;
531
532/// A set of extra data to carry along with the matching context, either for
533/// selector-matching or invalidation.
534#[derive(Debug, Default)]
535pub struct ExtraMatchingData<'a> {
536    /// The invalidation data to invalidate doc-state pseudo-classes correctly.
537    pub invalidation_data: InvalidationMatchingData,
538
539    /// The invalidation bits from matching container queries. These are here
540    /// just for convenience mostly.
541    pub cascade_input_flags: ComputedValueFlags,
542
543    /// The style of the originating element in order to evaluate @container
544    /// size queries affecting pseudo-elements.
545    pub originating_element_style: Option<&'a ComputedValues>,
546}
547
548impl ::selectors::SelectorImpl for SelectorImpl {
549    type PseudoElement = PseudoElement;
550    type NonTSPseudoClass = NonTSPseudoClass;
551
552    type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
553    type AttrValue = AtomString;
554    type Identifier = AtomIdent;
555    type LocalName = LocalName;
556    type NamespacePrefix = Prefix;
557    type NamespaceUrl = Namespace;
558    type BorrowedLocalName = web_atoms::LocalName;
559    type BorrowedNamespaceUrl = web_atoms::Namespace;
560}
561
562impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
563    type Impl = SelectorImpl;
564    type Error = StyleParseErrorKind<'i>;
565
566    #[inline]
567    fn parse_nth_child_of(&self) -> bool {
568        false
569    }
570
571    #[inline]
572    fn parse_is_and_where(&self) -> bool {
573        true
574    }
575
576    #[inline]
577    fn parse_has(&self) -> bool {
578        false
579    }
580
581    #[inline]
582    fn parse_parent_selector(&self) -> bool {
583        true
584    }
585
586    #[inline]
587    fn parse_part(&self) -> bool {
588        true
589    }
590
591    #[inline]
592    fn allow_forgiving_selectors(&self) -> bool {
593        !self.for_supports_rule
594    }
595
596    fn parse_non_ts_pseudo_class(
597        &self,
598        location: SourceLocation,
599        name: CowRcStr<'i>,
600    ) -> Result<NonTSPseudoClass, ParseError<'i>> {
601        let pseudo_class = match_ignore_ascii_case! { &name,
602            "active" => NonTSPseudoClass::Active,
603            "any-link" => NonTSPseudoClass::AnyLink,
604            "autofill" => NonTSPseudoClass::Autofill,
605            "checked" => NonTSPseudoClass::Checked,
606            "default" => NonTSPseudoClass::Default,
607            "defined" => NonTSPseudoClass::Defined,
608            "disabled" => NonTSPseudoClass::Disabled,
609            "enabled" => NonTSPseudoClass::Enabled,
610            "focus" => NonTSPseudoClass::Focus,
611            "focus-visible" => NonTSPseudoClass::FocusVisible,
612            "focus-within" => NonTSPseudoClass::FocusWithin,
613            "fullscreen" => NonTSPseudoClass::Fullscreen,
614            "hover" => NonTSPseudoClass::Hover,
615            "indeterminate" => NonTSPseudoClass::Indeterminate,
616            "invalid" => NonTSPseudoClass::Invalid,
617            "link" => NonTSPseudoClass::Link,
618            "modal" => NonTSPseudoClass::Modal,
619            "open" => NonTSPseudoClass::Open,
620            "optional" => NonTSPseudoClass::Optional,
621            "out-of-range" => NonTSPseudoClass::OutOfRange,
622            "placeholder-shown" => NonTSPseudoClass::PlaceholderShown,
623            "popover-open" => NonTSPseudoClass::PopoverOpen,
624            "read-only" => NonTSPseudoClass::ReadOnly,
625            "read-write" => NonTSPseudoClass::ReadWrite,
626            "required" => NonTSPseudoClass::Required,
627            "target" => NonTSPseudoClass::Target,
628            "user-invalid" => NonTSPseudoClass::UserInvalid,
629            "user-valid" => NonTSPseudoClass::UserValid,
630            "valid" => NonTSPseudoClass::Valid,
631            "visited" => NonTSPseudoClass::Visited,
632            "-moz-meter-optimum" => NonTSPseudoClass::MozMeterOptimum,
633            "-moz-meter-sub-optimum" => NonTSPseudoClass::MozMeterSubOptimum,
634            "-moz-meter-sub-sub-optimum" => NonTSPseudoClass::MozMeterSubSubOptimum,
635            "-servo-nonzero-border" => {
636                if !self.in_user_agent_stylesheet() {
637                    return Err(location.new_custom_error(
638                        SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
639                    ))
640                }
641                NonTSPseudoClass::ServoNonZeroBorder
642            },
643            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
644        };
645
646        Ok(pseudo_class)
647    }
648
649    fn parse_non_ts_functional_pseudo_class<'t>(
650        &self,
651        name: CowRcStr<'i>,
652        parser: &mut CssParser<'i, 't>,
653        after_part: bool,
654    ) -> Result<NonTSPseudoClass, ParseError<'i>> {
655        let pseudo_class = match_ignore_ascii_case! { &name,
656            "lang" if !after_part => {
657                NonTSPseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().into())
658            },
659            "state" => {
660                let result = AtomIdent::from(parser.expect_ident()?.as_ref());
661                NonTSPseudoClass::CustomState(CustomState(result))
662            },
663            _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
664        };
665
666        Ok(pseudo_class)
667    }
668
669    fn parse_pseudo_element(
670        &self,
671        location: SourceLocation,
672        name: CowRcStr<'i>,
673    ) -> Result<PseudoElement, ParseError<'i>> {
674        use self::PseudoElement::*;
675        let pseudo_element = match_ignore_ascii_case! { &name,
676            "before" => Before,
677            "after" => After,
678            "backdrop" => Backdrop,
679            "selection" => Selection,
680            "file-selector-button" => FileSelectorButton,
681            "first-letter" => FirstLetter,
682            "marker" => Marker,
683            "-servo-details-summary" => {
684                if !self.in_user_agent_stylesheet() {
685                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
686                }
687                DetailsSummary
688            },
689            "details-content" => DetailsContent,
690            "color-swatch" => ColorSwatch,
691            "placeholder" => Placeholder,
692            "-servo-text-control-inner-container" => {
693                if !self.in_user_agent_stylesheet() {
694                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
695                }
696                ServoTextControlInnerContainer
697            },
698            "-servo-text-control-inner-editor" => {
699                if !self.in_user_agent_stylesheet() {
700                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
701                }
702                ServoTextControlInnerEditor
703            },
704            "slider-fill" => SliderFill,
705            "slider-thumb" => SliderThumb,
706            "slider-track" => SliderTrack,
707            "-servo-anonymous-box" => {
708                if !self.in_user_agent_stylesheet() {
709                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
710                }
711                ServoAnonymousBox
712            },
713            "-servo-anonymous-table" => {
714                if !self.in_user_agent_stylesheet() {
715                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
716                }
717                ServoAnonymousTable
718            },
719            "-servo-anonymous-table-row" => {
720                if !self.in_user_agent_stylesheet() {
721                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
722                }
723                ServoAnonymousTableRow
724            },
725            "-servo-anonymous-table-cell" => {
726                if !self.in_user_agent_stylesheet() {
727                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
728                }
729                ServoAnonymousTableCell
730            },
731            "-servo-table-grid" => {
732                if !self.in_user_agent_stylesheet() {
733                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
734                }
735                ServoTableGrid
736            },
737            "-servo-table-wrapper" => {
738                if !self.in_user_agent_stylesheet() {
739                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
740                }
741                ServoTableWrapper
742            },
743            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
744
745        };
746
747        Ok(pseudo_element)
748    }
749
750    fn default_namespace(&self) -> Option<Namespace> {
751        self.namespaces.default.as_ref().map(|ns| ns.clone())
752    }
753
754    fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
755        self.namespaces.prefixes.get(prefix).cloned()
756    }
757
758    fn parse_host(&self) -> bool {
759        true
760    }
761
762    fn parse_slotted(&self) -> bool {
763        true
764    }
765}
766
767impl SelectorImpl {
768    /// A helper to traverse each eagerly cascaded pseudo-element, executing
769    /// `fun` on it.
770    #[inline]
771    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
772    where
773        F: FnMut(PseudoElement),
774    {
775        for i in 0..EAGER_PSEUDO_COUNT {
776            fun(PseudoElement::from_eager_index(i));
777        }
778    }
779}
780
781/// A map from elements to snapshots for the Servo style back-end.
782#[derive(Debug)]
783pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
784
785impl SnapshotMap {
786    /// Create a new empty `SnapshotMap`.
787    pub fn new() -> Self {
788        SnapshotMap(FxHashMap::default())
789    }
790
791    /// Get a snapshot given an element.
792    pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
793        self.0.get(&el.as_node().opaque())
794    }
795}
796
797impl Deref for SnapshotMap {
798    type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
799
800    fn deref(&self) -> &Self::Target {
801        &self.0
802    }
803}
804
805impl DerefMut for SnapshotMap {
806    fn deref_mut(&mut self) -> &mut Self::Target {
807        &mut self.0
808    }
809}
810
811/// Servo's version of an element snapshot.
812#[derive(Debug, Default, MallocSizeOf)]
813pub struct ServoElementSnapshot {
814    /// The stored state of the element.
815    pub state: Option<ElementState>,
816    /// The set of stored attributes and its values.
817    pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
818    /// The set of changed attributes and its values.
819    pub changed_attrs: Vec<LocalName>,
820    /// Whether the class attribute changed or not.
821    pub class_changed: bool,
822    /// Whether the id attribute changed or not.
823    pub id_changed: bool,
824    /// Whether other attributes other than id or class changed or not.
825    pub other_attributes_changed: bool,
826}
827
828impl ServoElementSnapshot {
829    /// Create an empty element snapshot.
830    pub fn new() -> Self {
831        Self::default()
832    }
833
834    /// Returns whether the id attribute changed or not.
835    pub fn id_changed(&self) -> bool {
836        self.id_changed
837    }
838
839    /// Returns whether the class attribute changed or not.
840    pub fn class_changed(&self) -> bool {
841        self.class_changed
842    }
843
844    /// Returns whether other attributes other than id or class changed or not.
845    pub fn other_attr_changed(&self) -> bool {
846        self.other_attributes_changed
847    }
848
849    fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
850        self.attrs
851            .as_ref()
852            .unwrap()
853            .iter()
854            .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
855            .map(|&(_, ref v)| v)
856    }
857
858    /// Executes the callback once for each attribute that changed.
859    #[inline]
860    pub fn each_attr_changed<F>(&self, mut callback: F)
861    where
862        F: FnMut(&LocalName),
863    {
864        for name in &self.changed_attrs {
865            callback(name)
866        }
867    }
868
869    fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
870    where
871        F: FnMut(&AttrValue) -> bool,
872    {
873        self.attrs
874            .as_ref()
875            .unwrap()
876            .iter()
877            .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
878    }
879}
880
881impl ElementSnapshot for ServoElementSnapshot {
882    fn state(&self) -> Option<ElementState> {
883        self.state.clone()
884    }
885
886    fn has_attrs(&self) -> bool {
887        self.attrs.is_some()
888    }
889
890    fn id_attr(&self) -> Option<&Atom> {
891        self.get_attr(&ns!(), &local_name!("id"))
892            .map(|v| v.as_atom())
893    }
894
895    fn is_part(&self, part_name: &AtomIdent) -> bool {
896        self.get_attr(&ns!(), &local_name!("part"))
897            .is_some_and(|v| {
898                v.as_tokens()
899                    .iter()
900                    .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))
901            })
902    }
903
904    fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
905        None
906    }
907
908    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
909        self.get_attr(&ns!(), &local_name!("class"))
910            .map_or(false, |v| {
911                v.as_tokens()
912                    .iter()
913                    .any(|atom| case_sensitivity.eq_atom(atom, name))
914            })
915    }
916
917    fn each_class<F>(&self, mut callback: F)
918    where
919        F: FnMut(&AtomIdent),
920    {
921        if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
922            for class in v.as_tokens() {
923                callback(AtomIdent::cast(class));
924            }
925        }
926    }
927
928    fn lang_attr(&self) -> Option<SelectorAttrValue> {
929        self.get_attr(&ns!(xml), &local_name!("lang"))
930            .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
931            .map(|v| SelectorAttrValue::from(v as &str))
932    }
933
934    /// Returns true if the snapshot has stored state for custom states
935    #[inline]
936    fn has_custom_states(&self) -> bool {
937        false
938    }
939
940    /// Returns true if the snapshot has a given CustomState
941    #[inline]
942    fn has_custom_state(&self, _state: &AtomIdent) -> bool {
943        false
944    }
945
946    #[inline]
947    fn each_custom_state<F>(&self, mut _callback: F)
948    where
949        F: FnMut(&AtomIdent),
950    {
951    }
952}
953
954impl ServoElementSnapshot {
955    /// selectors::Element::attr_matches
956    pub fn attr_matches(
957        &self,
958        ns: &NamespaceConstraint<&Namespace>,
959        local_name: &LocalName,
960        operation: &AttrSelectorOperation<&AtomString>,
961    ) -> bool {
962        match *ns {
963            NamespaceConstraint::Specific(ref ns) => self
964                .get_attr(ns, local_name)
965                .map_or(false, |value| value.eval_selector(operation)),
966            NamespaceConstraint::Any => {
967                self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
968            },
969        }
970    }
971}
972
973/// Returns whether the language is matched, as defined by
974/// [RFC 4647](https://tools.ietf.org/html/rfc4647#section-3.3.2).
975pub fn extended_filtering(tag: &str, range: &str) -> bool {
976    range.split(',').any(|lang_range| {
977        // step 1
978        let mut range_subtags = lang_range.split('\x2d');
979        let mut tag_subtags = tag.split('\x2d');
980
981        // step 2
982        // Note: [Level-4 spec](https://drafts.csswg.org/selectors/#lang-pseudo) check for wild card
983        if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
984            if !(range_subtag.eq_ignore_ascii_case(tag_subtag)
985                || range_subtag.eq_ignore_ascii_case("*"))
986            {
987                return false;
988            }
989        }
990
991        let mut current_tag_subtag = tag_subtags.next();
992
993        // step 3
994        for range_subtag in range_subtags {
995            // step 3a
996            if range_subtag == "*" {
997                continue;
998            }
999            match current_tag_subtag.clone() {
1000                Some(tag_subtag) => {
1001                    // step 3c
1002                    if range_subtag.eq_ignore_ascii_case(tag_subtag) {
1003                        current_tag_subtag = tag_subtags.next();
1004                        continue;
1005                    }
1006                    // step 3d
1007                    if tag_subtag.len() == 1 {
1008                        return false;
1009                    }
1010                    // else step 3e - continue with loop
1011                    current_tag_subtag = tag_subtags.next();
1012                    if current_tag_subtag.is_none() {
1013                        return false;
1014                    }
1015                },
1016                // step 3b
1017                None => {
1018                    return false;
1019                },
1020            }
1021        }
1022        // step 4
1023        true
1024    })
1025}