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