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            Self::Lang(_) | Self::CustomState(_) => unreachable!(),
414        })
415    }
416}
417
418impl NonTSPseudoClass {
419    /// Gets a given state flag for this pseudo-class. This is used to do
420    /// selector matching, and it's set from the DOM.
421    pub fn state_flag(&self) -> ElementState {
422        match *self {
423            Self::Active => ElementState::ACTIVE,
424            Self::AnyLink => ElementState::VISITED_OR_UNVISITED,
425            Self::Autofill => ElementState::AUTOFILL,
426            Self::Checked => ElementState::CHECKED,
427            Self::Default => ElementState::DEFAULT,
428            Self::Defined => ElementState::DEFINED,
429            Self::Disabled => ElementState::DISABLED,
430            Self::Enabled => ElementState::ENABLED,
431            Self::Focus => ElementState::FOCUS,
432            Self::FocusVisible => ElementState::FOCUSRING,
433            Self::FocusWithin => ElementState::FOCUS_WITHIN,
434            Self::Fullscreen => ElementState::FULLSCREEN,
435            Self::Hover => ElementState::HOVER,
436            Self::InRange => ElementState::INRANGE,
437            Self::Indeterminate => ElementState::INDETERMINATE,
438            Self::Invalid => ElementState::INVALID,
439            Self::Link => ElementState::UNVISITED,
440            Self::Modal => ElementState::MODAL,
441            Self::MozMeterOptimum => ElementState::OPTIMUM,
442            Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM,
443            Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM,
444            Self::Optional => ElementState::OPTIONAL_,
445            Self::OutOfRange => ElementState::OUTOFRANGE,
446            Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
447            Self::PopoverOpen => ElementState::POPOVER_OPEN,
448            Self::ReadOnly => ElementState::READONLY,
449            Self::ReadWrite => ElementState::READWRITE,
450            Self::Required => ElementState::REQUIRED,
451            Self::Target => ElementState::URLTARGET,
452            Self::UserInvalid => ElementState::USER_INVALID,
453            Self::UserValid => ElementState::USER_VALID,
454            Self::Valid => ElementState::VALID,
455            Self::Visited => ElementState::VISITED,
456            Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => ElementState::empty(),
457        }
458    }
459
460    /// Get the document state flag associated with a pseudo-class, if any.
461    pub fn document_state_flag(&self) -> DocumentState {
462        DocumentState::empty()
463    }
464
465    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
466    pub fn needs_cache_revalidation(&self) -> bool {
467        self.state_flag().is_empty()
468    }
469}
470
471/// The abstract struct we implement the selector parser implementation on top
472/// of.
473#[derive(Clone, Debug, PartialEq)]
474#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
475pub struct SelectorImpl;
476
477/// A set of extra data to carry along with the matching context, either for
478/// selector-matching or invalidation.
479#[derive(Debug, Default)]
480pub struct ExtraMatchingData<'a> {
481    /// The invalidation data to invalidate doc-state pseudo-classes correctly.
482    pub invalidation_data: InvalidationMatchingData,
483
484    /// The invalidation bits from matching container queries. These are here
485    /// just for convenience mostly.
486    pub cascade_input_flags: ComputedValueFlags,
487
488    /// The style of the originating element in order to evaluate @container
489    /// size queries affecting pseudo-elements.
490    pub originating_element_style: Option<&'a ComputedValues>,
491}
492
493impl ::selectors::SelectorImpl for SelectorImpl {
494    type PseudoElement = PseudoElement;
495    type NonTSPseudoClass = NonTSPseudoClass;
496
497    type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
498    type AttrValue = AtomString;
499    type Identifier = AtomIdent;
500    type LocalName = LocalName;
501    type NamespacePrefix = Prefix;
502    type NamespaceUrl = Namespace;
503    type BorrowedLocalName = web_atoms::LocalName;
504    type BorrowedNamespaceUrl = web_atoms::Namespace;
505}
506
507impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
508    type Impl = SelectorImpl;
509    type Error = StyleParseErrorKind<'i>;
510
511    #[inline]
512    fn parse_nth_child_of(&self) -> bool {
513        false
514    }
515
516    #[inline]
517    fn parse_is_and_where(&self) -> bool {
518        true
519    }
520
521    #[inline]
522    fn parse_has(&self) -> bool {
523        false
524    }
525
526    #[inline]
527    fn parse_parent_selector(&self) -> bool {
528        true
529    }
530
531    #[inline]
532    fn parse_part(&self) -> bool {
533        true
534    }
535
536    #[inline]
537    fn allow_forgiving_selectors(&self) -> bool {
538        !self.for_supports_rule
539    }
540
541    fn parse_non_ts_pseudo_class(
542        &self,
543        location: SourceLocation,
544        name: CowRcStr<'i>,
545    ) -> Result<NonTSPseudoClass, ParseError<'i>> {
546        let pseudo_class = match_ignore_ascii_case! { &name,
547            "active" => NonTSPseudoClass::Active,
548            "any-link" => NonTSPseudoClass::AnyLink,
549            "autofill" => NonTSPseudoClass::Autofill,
550            "checked" => NonTSPseudoClass::Checked,
551            "default" => NonTSPseudoClass::Default,
552            "defined" => NonTSPseudoClass::Defined,
553            "disabled" => NonTSPseudoClass::Disabled,
554            "enabled" => NonTSPseudoClass::Enabled,
555            "focus" => NonTSPseudoClass::Focus,
556            "focus-visible" => NonTSPseudoClass::FocusVisible,
557            "focus-within" => NonTSPseudoClass::FocusWithin,
558            "fullscreen" => NonTSPseudoClass::Fullscreen,
559            "hover" => NonTSPseudoClass::Hover,
560            "indeterminate" => NonTSPseudoClass::Indeterminate,
561            "invalid" => NonTSPseudoClass::Invalid,
562            "link" => NonTSPseudoClass::Link,
563            "optional" => NonTSPseudoClass::Optional,
564            "out-of-range" => NonTSPseudoClass::OutOfRange,
565            "placeholder-shown" => NonTSPseudoClass::PlaceholderShown,
566            "popover-open" => NonTSPseudoClass::PopoverOpen,
567            "read-only" => NonTSPseudoClass::ReadOnly,
568            "read-write" => NonTSPseudoClass::ReadWrite,
569            "required" => NonTSPseudoClass::Required,
570            "target" => NonTSPseudoClass::Target,
571            "user-invalid" => NonTSPseudoClass::UserInvalid,
572            "user-valid" => NonTSPseudoClass::UserValid,
573            "valid" => NonTSPseudoClass::Valid,
574            "visited" => NonTSPseudoClass::Visited,
575            "-moz-meter-optimum" => NonTSPseudoClass::MozMeterOptimum,
576            "-moz-meter-sub-optimum" => NonTSPseudoClass::MozMeterSubOptimum,
577            "-moz-meter-sub-sub-optimum" => NonTSPseudoClass::MozMeterSubSubOptimum,
578            "-servo-nonzero-border" => {
579                if !self.in_user_agent_stylesheet() {
580                    return Err(location.new_custom_error(
581                        SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
582                    ))
583                }
584                NonTSPseudoClass::ServoNonZeroBorder
585            },
586            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
587        };
588
589        Ok(pseudo_class)
590    }
591
592    fn parse_non_ts_functional_pseudo_class<'t>(
593        &self,
594        name: CowRcStr<'i>,
595        parser: &mut CssParser<'i, 't>,
596        after_part: bool,
597    ) -> Result<NonTSPseudoClass, ParseError<'i>> {
598        use self::NonTSPseudoClass::*;
599        let pseudo_class = match_ignore_ascii_case! { &name,
600            "lang" if !after_part => {
601                Lang(parser.expect_ident_or_string()?.as_ref().into())
602            },
603            _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
604        };
605
606        Ok(pseudo_class)
607    }
608
609    fn parse_pseudo_element(
610        &self,
611        location: SourceLocation,
612        name: CowRcStr<'i>,
613    ) -> Result<PseudoElement, ParseError<'i>> {
614        use self::PseudoElement::*;
615        let pseudo_element = match_ignore_ascii_case! { &name,
616            "before" => Before,
617            "after" => After,
618            "backdrop" => Backdrop,
619            "selection" => Selection,
620            "marker" => Marker,
621            "-servo-details-summary" => {
622                if !self.in_user_agent_stylesheet() {
623                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
624                }
625                DetailsSummary
626            },
627            "-servo-details-content" => {
628                if !self.in_user_agent_stylesheet() {
629                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
630                }
631                DetailsContent
632            },
633            "color-swatch" => ColorSwatch,
634            "placeholder" => {
635                if !self.in_user_agent_stylesheet() {
636                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
637                }
638                Placeholder
639            },
640            "-servo-text-control-inner-container" => {
641                if !self.in_user_agent_stylesheet() {
642                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
643                }
644                ServoTextControlInnerContainer
645            },
646            "-servo-text-control-inner-editor" => {
647                if !self.in_user_agent_stylesheet() {
648                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
649                }
650                ServoTextControlInnerEditor
651            },
652            "-servo-anonymous-box" => {
653                if !self.in_user_agent_stylesheet() {
654                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
655                }
656                ServoAnonymousBox
657            },
658            "-servo-anonymous-table" => {
659                if !self.in_user_agent_stylesheet() {
660                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
661                }
662                ServoAnonymousTable
663            },
664            "-servo-anonymous-table-row" => {
665                if !self.in_user_agent_stylesheet() {
666                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
667                }
668                ServoAnonymousTableRow
669            },
670            "-servo-anonymous-table-cell" => {
671                if !self.in_user_agent_stylesheet() {
672                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
673                }
674                ServoAnonymousTableCell
675            },
676            "-servo-table-grid" => {
677                if !self.in_user_agent_stylesheet() {
678                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
679                }
680                ServoTableGrid
681            },
682            "-servo-table-wrapper" => {
683                if !self.in_user_agent_stylesheet() {
684                    return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
685                }
686                ServoTableWrapper
687            },
688            _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
689
690        };
691
692        Ok(pseudo_element)
693    }
694
695    fn default_namespace(&self) -> Option<Namespace> {
696        self.namespaces.default.as_ref().map(|ns| ns.clone())
697    }
698
699    fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
700        self.namespaces.prefixes.get(prefix).cloned()
701    }
702
703    fn parse_host(&self) -> bool {
704        true
705    }
706
707    fn parse_slotted(&self) -> bool {
708        true
709    }
710}
711
712impl SelectorImpl {
713    /// A helper to traverse each eagerly cascaded pseudo-element, executing
714    /// `fun` on it.
715    #[inline]
716    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
717    where
718        F: FnMut(PseudoElement),
719    {
720        for i in 0..EAGER_PSEUDO_COUNT {
721            fun(PseudoElement::from_eager_index(i));
722        }
723    }
724}
725
726/// A map from elements to snapshots for the Servo style back-end.
727#[derive(Debug)]
728pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
729
730impl SnapshotMap {
731    /// Create a new empty `SnapshotMap`.
732    pub fn new() -> Self {
733        SnapshotMap(FxHashMap::default())
734    }
735
736    /// Get a snapshot given an element.
737    pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
738        self.0.get(&el.as_node().opaque())
739    }
740}
741
742impl Deref for SnapshotMap {
743    type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
744
745    fn deref(&self) -> &Self::Target {
746        &self.0
747    }
748}
749
750impl DerefMut for SnapshotMap {
751    fn deref_mut(&mut self) -> &mut Self::Target {
752        &mut self.0
753    }
754}
755
756/// Servo's version of an element snapshot.
757#[derive(Debug, Default, MallocSizeOf)]
758pub struct ServoElementSnapshot {
759    /// The stored state of the element.
760    pub state: Option<ElementState>,
761    /// The set of stored attributes and its values.
762    pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
763    /// The set of changed attributes and its values.
764    pub changed_attrs: Vec<LocalName>,
765    /// Whether the class attribute changed or not.
766    pub class_changed: bool,
767    /// Whether the id attribute changed or not.
768    pub id_changed: bool,
769    /// Whether other attributes other than id or class changed or not.
770    pub other_attributes_changed: bool,
771}
772
773impl ServoElementSnapshot {
774    /// Create an empty element snapshot.
775    pub fn new() -> Self {
776        Self::default()
777    }
778
779    /// Returns whether the id attribute changed or not.
780    pub fn id_changed(&self) -> bool {
781        self.id_changed
782    }
783
784    /// Returns whether the class attribute changed or not.
785    pub fn class_changed(&self) -> bool {
786        self.class_changed
787    }
788
789    /// Returns whether other attributes other than id or class changed or not.
790    pub fn other_attr_changed(&self) -> bool {
791        self.other_attributes_changed
792    }
793
794    fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
795        self.attrs
796            .as_ref()
797            .unwrap()
798            .iter()
799            .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
800            .map(|&(_, ref v)| v)
801    }
802
803    /// Executes the callback once for each attribute that changed.
804    #[inline]
805    pub fn each_attr_changed<F>(&self, mut callback: F)
806    where
807        F: FnMut(&LocalName),
808    {
809        for name in &self.changed_attrs {
810            callback(name)
811        }
812    }
813
814    fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
815    where
816        F: FnMut(&AttrValue) -> bool,
817    {
818        self.attrs
819            .as_ref()
820            .unwrap()
821            .iter()
822            .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
823    }
824}
825
826impl ElementSnapshot for ServoElementSnapshot {
827    fn state(&self) -> Option<ElementState> {
828        self.state.clone()
829    }
830
831    fn has_attrs(&self) -> bool {
832        self.attrs.is_some()
833    }
834
835    fn id_attr(&self) -> Option<&Atom> {
836        self.get_attr(&ns!(), &local_name!("id"))
837            .map(|v| v.as_atom())
838    }
839
840    fn is_part(&self, part_name: &AtomIdent) -> bool {
841        self.get_attr(&ns!(), &local_name!("part"))
842            .is_some_and(|v| {
843                v.as_tokens()
844                    .iter()
845                    .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))
846            })
847    }
848
849    fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
850        None
851    }
852
853    fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
854        self.get_attr(&ns!(), &local_name!("class"))
855            .map_or(false, |v| {
856                v.as_tokens()
857                    .iter()
858                    .any(|atom| case_sensitivity.eq_atom(atom, name))
859            })
860    }
861
862    fn each_class<F>(&self, mut callback: F)
863    where
864        F: FnMut(&AtomIdent),
865    {
866        if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
867            for class in v.as_tokens() {
868                callback(AtomIdent::cast(class));
869            }
870        }
871    }
872
873    fn lang_attr(&self) -> Option<SelectorAttrValue> {
874        self.get_attr(&ns!(xml), &local_name!("lang"))
875            .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
876            .map(|v| SelectorAttrValue::from(v as &str))
877    }
878
879    /// Returns true if the snapshot has stored state for custom states
880    #[inline]
881    fn has_custom_states(&self) -> bool {
882        false
883    }
884
885    /// Returns true if the snapshot has a given CustomState
886    #[inline]
887    fn has_custom_state(&self, _state: &AtomIdent) -> bool {
888        false
889    }
890
891    #[inline]
892    fn each_custom_state<F>(&self, mut _callback: F)
893    where
894        F: FnMut(&AtomIdent),
895    {
896    }
897}
898
899impl ServoElementSnapshot {
900    /// selectors::Element::attr_matches
901    pub fn attr_matches(
902        &self,
903        ns: &NamespaceConstraint<&Namespace>,
904        local_name: &LocalName,
905        operation: &AttrSelectorOperation<&AtomString>,
906    ) -> bool {
907        match *ns {
908            NamespaceConstraint::Specific(ref ns) => self
909                .get_attr(ns, local_name)
910                .map_or(false, |value| value.eval_selector(operation)),
911            NamespaceConstraint::Any => {
912                self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
913            },
914        }
915    }
916}
917
918/// Returns whether the language is matched, as defined by
919/// [RFC 4647](https://tools.ietf.org/html/rfc4647#section-3.3.2).
920pub fn extended_filtering(tag: &str, range: &str) -> bool {
921    range.split(',').any(|lang_range| {
922        // step 1
923        let mut range_subtags = lang_range.split('\x2d');
924        let mut tag_subtags = tag.split('\x2d');
925
926        // step 2
927        // Note: [Level-4 spec](https://drafts.csswg.org/selectors/#lang-pseudo) check for wild card
928        if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
929            if !(range_subtag.eq_ignore_ascii_case(tag_subtag) ||
930                range_subtag.eq_ignore_ascii_case("*"))
931            {
932                return false;
933            }
934        }
935
936        let mut current_tag_subtag = tag_subtags.next();
937
938        // step 3
939        for range_subtag in range_subtags {
940            // step 3a
941            if range_subtag == "*" {
942                continue;
943            }
944            match current_tag_subtag.clone() {
945                Some(tag_subtag) => {
946                    // step 3c
947                    if range_subtag.eq_ignore_ascii_case(tag_subtag) {
948                        current_tag_subtag = tag_subtags.next();
949                        continue;
950                    }
951                    // step 3d
952                    if tag_subtag.len() == 1 {
953                        return false;
954                    }
955                    // else step 3e - continue with loop
956                    current_tag_subtag = tag_subtags.next();
957                    if current_tag_subtag.is_none() {
958                        return false;
959                    }
960                },
961                // step 3b
962                None => {
963                    return false;
964                },
965            }
966        }
967        // step 4
968        true
969    })
970}