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