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