1#![deny(missing_docs)]
6
7use crate::FxHashMap;
10use crate::attr::{AttrIdentifier, AttrValue};
11use crate::computed_value_flags::ComputedValueFlags;
12use crate::derives::*;
13use crate::dom::{OpaqueNode, TElement, TNode};
14use crate::invalidation::element::document_state::InvalidationMatchingData;
15use crate::invalidation::element::element_wrapper::ElementSnapshot;
16use crate::properties::longhands::display::computed_value::T as Display;
17use crate::properties::{ComputedValues, PropertyFlags};
18use crate::selector_parser::AttrValue as SelectorAttrValue;
19use crate::selector_parser::{PseudoElementCascadeType, SelectorParser};
20use crate::values::{AtomIdent, AtomString};
21use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
22use cssparser::{Parser as CssParser, ToCss, match_ignore_ascii_case, serialize_identifier};
23use dom::{DocumentState, ElementState};
24use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
25use selectors::parser::SelectorParseErrorKind;
26use std::fmt;
27use std::mem;
28use std::ops::{Deref, DerefMut};
29use style_traits::{ParseError, StyleParseErrorKind};
30
31#[derive(
35 Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToShmem,
36)]
37#[allow(missing_docs)]
38#[repr(u8)]
39pub enum PseudoElement {
40 After = 0,
42 Before,
43 Selection,
44 FirstLetter,
53
54 Backdrop,
56 DetailsContent,
57 Marker,
58
59 ColorSwatch,
63 FileSelectorButton,
64 Placeholder,
65 SliderFill,
66 SliderThumb,
67 SliderTrack,
68 MozProgressBar,
69
70 ServoTextControlInnerContainer,
72 ServoTextControlInnerEditor,
73
74 ServoAnonymousBox,
76 ServoAnonymousTable,
77 ServoAnonymousTableCell,
78 ServoAnonymousTableRow,
79 ServoTableGrid,
80 ServoTableWrapper,
81}
82
83pub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1;
85
86impl ToCss for PseudoElement {
87 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
88 where
89 W: fmt::Write,
90 {
91 use self::PseudoElement::*;
92 dest.write_str(match *self {
93 After => "::after",
94 Before => "::before",
95 Selection => "::selection",
96 FirstLetter => "::first-letter",
97 Backdrop => "::backdrop",
98 DetailsContent => "::details-content",
99 Marker => "::marker",
100 ColorSwatch => "::color-swatch",
101 FileSelectorButton => "::file-selector-button",
102 Placeholder => "::placeholder",
103 SliderFill => "::slider-fill",
104 SliderTrack => "::slider-track",
105 SliderThumb => "::slider-thumb",
106 MozProgressBar => "::-moz-progress-bar",
107 ServoTextControlInnerContainer => "::-servo-text-control-inner-container",
108 ServoTextControlInnerEditor => "::-servo-text-control-inner-editor",
109 ServoAnonymousBox => "::-servo-anonymous-box",
110 ServoAnonymousTable => "::-servo-anonymous-table",
111 ServoAnonymousTableCell => "::-servo-anonymous-table-cell",
112 ServoAnonymousTableRow => "::-servo-anonymous-table-row",
113 ServoTableGrid => "::-servo-table-grid",
114 ServoTableWrapper => "::-servo-table-wrapper",
115 })
116 }
117}
118
119impl ::selectors::parser::PseudoElement for PseudoElement {
120 fn parses_as_element_backed(&self) -> bool {
121 matches!(self, Self::DetailsContent)
122 }
123}
124
125pub const EAGER_PSEUDO_COUNT: usize = 4;
127
128impl PseudoElement {
129 #[inline]
131 pub fn eager_index(&self) -> usize {
132 debug_assert!(self.is_eager());
133 self.clone() as usize
134 }
135
136 #[inline]
138 pub fn index(&self) -> usize {
139 self.clone() as usize
140 }
141
142 pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
144 Default::default()
145 }
146
147 #[inline]
149 pub fn from_eager_index(i: usize) -> Self {
150 const _: () = assert!(EAGER_PSEUDO_COUNT <= (u8::MAX as usize));
151 assert!(i < EAGER_PSEUDO_COUNT);
152 let result: PseudoElement = unsafe { mem::transmute(i as u8) };
153 debug_assert!(result.is_eager());
154 result
155 }
156
157 #[inline]
159 pub fn is_before_or_after(&self) -> bool {
160 self.is_before() || self.is_after()
161 }
162
163 #[inline]
165 pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
166 false
167 }
168
169 #[inline]
171 pub fn is_marker(&self) -> bool {
172 *self == PseudoElement::Marker
173 }
174
175 #[inline]
177 pub fn is_selection(&self) -> bool {
178 *self == PseudoElement::Selection
179 }
180
181 #[inline]
183 pub fn is_before(&self) -> bool {
184 *self == PseudoElement::Before
185 }
186
187 #[inline]
189 pub fn is_after(&self) -> bool {
190 *self == PseudoElement::After
191 }
192
193 #[inline]
195 pub fn is_first_letter(&self) -> bool {
196 *self == PseudoElement::FirstLetter
197 }
198
199 #[inline]
201 pub fn is_first_line(&self) -> bool {
202 false
203 }
204
205 #[inline]
208 pub fn is_color_swatch(&self) -> bool {
209 *self == PseudoElement::ColorSwatch
210 }
211
212 #[inline]
214 pub fn is_eager(&self) -> bool {
215 self.cascade_type() == PseudoElementCascadeType::Eager
216 }
217
218 #[inline]
220 pub fn is_lazy(&self) -> bool {
221 self.cascade_type() == PseudoElementCascadeType::Lazy
222 }
223
224 pub fn is_anon_box(&self) -> bool {
226 self.is_precomputed()
227 }
228
229 #[inline]
232 pub fn skip_item_display_fixup(&self) -> bool {
233 !self.is_before_or_after()
234 }
235
236 #[inline]
238 pub fn is_precomputed(&self) -> bool {
239 self.cascade_type() == PseudoElementCascadeType::Precomputed
240 }
241
242 #[inline]
250 pub fn cascade_type(&self) -> PseudoElementCascadeType {
251 match *self {
252 PseudoElement::After
253 | PseudoElement::Before
254 | PseudoElement::FirstLetter
255 | PseudoElement::Selection => PseudoElementCascadeType::Eager,
256 PseudoElement::Backdrop
257 | PseudoElement::ColorSwatch
258 | PseudoElement::FileSelectorButton
259 | PseudoElement::Marker
260 | PseudoElement::Placeholder
261 | PseudoElement::DetailsContent
262 | PseudoElement::SliderFill
263 | PseudoElement::SliderThumb
264 | PseudoElement::SliderTrack
265 | PseudoElement::MozProgressBar
266 | PseudoElement::ServoTextControlInnerContainer
267 | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy,
268 PseudoElement::ServoAnonymousBox
269 | PseudoElement::ServoAnonymousTable
270 | PseudoElement::ServoAnonymousTableCell
271 | PseudoElement::ServoAnonymousTableRow
272 | PseudoElement::ServoTableGrid
273 | PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed,
274 }
275 }
276
277 pub fn canonical(&self) -> PseudoElement {
280 self.clone()
281 }
282
283 pub fn pseudo_info(&self) {
285 ()
286 }
287
288 #[inline]
290 pub fn property_restriction(&self) -> PropertyFlags {
291 match self {
292 PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,
293 PseudoElement::Marker if crate::pref!("layout.css.marker.restricted") => {
294 PropertyFlags::APPLIES_TO_MARKER
295 },
296 PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
297 PseudoElement::Selection => PropertyFlags::APPLIES_TO_HIGHLIGHT,
298 _ => PropertyFlags::empty(),
299 }
300 }
301
302 pub fn should_exist(&self, style: &ComputedValues) -> bool {
305 let display = *style.get_box().get_display();
306 if display == Display::None {
307 return false;
308 }
309 if self.is_before_or_after() && style.ineffective_content_property() {
310 return false;
311 }
312
313 true
314 }
315
316 pub fn is_highlight(&self) -> bool {
318 false
319 }
320
321 #[inline]
323 pub fn has_argument(&self) -> bool {
324 false
325 }
326
327 #[inline]
329 pub fn is_target_text(&self) -> bool {
330 false
331 }
332
333 #[inline]
337 pub fn is_lazy_painted_highlight_pseudo(&self) -> bool {
338 self.is_selection() || self.is_highlight() || self.is_target_text()
339 }
340
341 #[inline]
344 pub fn is_element_backed(&self) -> bool {
345 use selectors::parser::PseudoElement;
346 self.parses_as_element_backed()
347 || matches!(
348 self,
349 Self::Placeholder
350 | Self::ColorSwatch
351 | Self::FileSelectorButton
352 | Self::SliderFill
353 | Self::SliderThumb
354 | Self::SliderTrack
355 | Self::MozProgressBar
356 | Self::ServoTextControlInnerContainer
357 | Self::ServoTextControlInnerEditor,
358 )
359 }
360}
361
362pub type Lang = Box<str>;
364
365#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
367pub struct CustomState(pub AtomIdent);
368
369#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
372#[allow(missing_docs)]
373pub enum NonTSPseudoClass {
374 Active,
375 AnyLink,
376 Autofill,
377 Checked,
378 CustomState(CustomState),
380 Default,
381 Defined,
382 Disabled,
383 Enabled,
384 Focus,
385 FocusWithin,
386 FocusVisible,
387 Fullscreen,
388 Hover,
389 InRange,
390 Indeterminate,
391 Invalid,
392 Lang(Lang),
393 Link,
394 Modal,
395 MozMeterOptimum,
396 MozMeterSubOptimum,
397 MozMeterSubSubOptimum,
398 Open,
399 Optional,
400 OutOfRange,
401 PlaceholderShown,
402 PopoverOpen,
403 ReadOnly,
404 ReadWrite,
405 Required,
406 ServoNonZeroBorder,
407 Target,
408 UserInvalid,
409 UserValid,
410 Valid,
411 Visited,
412}
413
414impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
415 #[inline]
416 fn is_active_or_hover(&self) -> bool {
417 matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
418 }
419
420 #[inline]
421 fn is_user_action_state(&self) -> bool {
422 matches!(
423 *self,
424 NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
425 )
426 }
427
428 fn visit<V>(&self, _: &mut V) -> bool {
429 true
430 }
431}
432
433impl ToCss for NonTSPseudoClass {
434 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
435 where
436 W: fmt::Write,
437 {
438 use self::NonTSPseudoClass::*;
439 if let Lang(ref lang) = *self {
440 dest.write_str(":lang(")?;
441 serialize_identifier(lang, dest)?;
442 return dest.write_char(')');
443 }
444
445 dest.write_str(match *self {
446 Self::Active => ":active",
447 Self::AnyLink => ":any-link",
448 Self::Autofill => ":autofill",
449 Self::Checked => ":checked",
450 Self::CustomState(ref state) => {
451 dest.write_str(":state(")?;
452 state.0.to_css(dest)?;
453 return dest.write_char(')');
454 },
455 Self::Default => ":default",
456 Self::Defined => ":defined",
457 Self::Disabled => ":disabled",
458 Self::Enabled => ":enabled",
459 Self::Focus => ":focus",
460 Self::FocusVisible => ":focus-visible",
461 Self::FocusWithin => ":focus-within",
462 Self::Fullscreen => ":fullscreen",
463 Self::Hover => ":hover",
464 Self::InRange => ":in-range",
465 Self::Indeterminate => ":indeterminate",
466 Self::Invalid => ":invalid",
467 Self::Link => ":link",
468 Self::Modal => ":modal",
469 Self::MozMeterOptimum => ":-moz-meter-optimum",
470 Self::MozMeterSubOptimum => ":-moz-meter-sub-optimum",
471 Self::MozMeterSubSubOptimum => ":-moz-meter-sub-sub-optimum",
472 Self::Open => ":open",
473 Self::Optional => ":optional",
474 Self::OutOfRange => ":out-of-range",
475 Self::PlaceholderShown => ":placeholder-shown",
476 Self::PopoverOpen => ":popover-open",
477 Self::ReadOnly => ":read-only",
478 Self::ReadWrite => ":read-write",
479 Self::Required => ":required",
480 Self::ServoNonZeroBorder => ":-servo-nonzero-border",
481 Self::Target => ":target",
482 Self::UserInvalid => ":user-invalid",
483 Self::UserValid => ":user-valid",
484 Self::Valid => ":valid",
485 Self::Visited => ":visited",
486 Self::Lang(_) => unreachable!(),
487 })
488 }
489}
490
491impl NonTSPseudoClass {
492 pub fn state_flag(&self) -> ElementState {
495 match *self {
496 Self::Active => ElementState::ACTIVE,
497 Self::AnyLink => ElementState::VISITED_OR_UNVISITED,
498 Self::Autofill => ElementState::AUTOFILL,
499 Self::Checked => ElementState::CHECKED,
500 Self::Default => ElementState::DEFAULT,
501 Self::Defined => ElementState::DEFINED,
502 Self::Disabled => ElementState::DISABLED,
503 Self::Enabled => ElementState::ENABLED,
504 Self::Focus => ElementState::FOCUS,
505 Self::FocusVisible => ElementState::FOCUSRING,
506 Self::FocusWithin => ElementState::FOCUS_WITHIN,
507 Self::Fullscreen => ElementState::FULLSCREEN,
508 Self::Hover => ElementState::HOVER,
509 Self::InRange => ElementState::INRANGE,
510 Self::Indeterminate => ElementState::INDETERMINATE,
511 Self::Invalid => ElementState::INVALID,
512 Self::Link => ElementState::UNVISITED,
513 Self::Modal => ElementState::MODAL,
514 Self::MozMeterOptimum => ElementState::OPTIMUM,
515 Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM,
516 Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM,
517 Self::Open => ElementState::OPEN,
518 Self::Optional => ElementState::OPTIONAL_,
519 Self::OutOfRange => ElementState::OUTOFRANGE,
520 Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
521 Self::PopoverOpen => ElementState::POPOVER_OPEN,
522 Self::ReadOnly => ElementState::READONLY,
523 Self::ReadWrite => ElementState::READWRITE,
524 Self::Required => ElementState::REQUIRED,
525 Self::Target => ElementState::URLTARGET,
526 Self::UserInvalid => ElementState::USER_INVALID,
527 Self::UserValid => ElementState::USER_VALID,
528 Self::Valid => ElementState::VALID,
529 Self::Visited => ElementState::VISITED,
530 Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => {
531 ElementState::empty()
532 },
533 }
534 }
535
536 pub fn document_state_flag(&self) -> DocumentState {
538 DocumentState::empty()
539 }
540
541 pub fn needs_cache_revalidation(&self) -> bool {
543 self.state_flag().is_empty()
544 }
545}
546
547#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
550pub struct SelectorImpl;
551
552#[derive(Debug, Default)]
555pub struct ExtraMatchingData<'a> {
556 pub invalidation_data: InvalidationMatchingData,
558
559 pub cascade_input_flags: ComputedValueFlags,
562
563 pub originating_element_style: Option<&'a ComputedValues>,
566}
567
568impl ::selectors::SelectorImpl for SelectorImpl {
569 type PseudoElement = PseudoElement;
570 type NonTSPseudoClass = NonTSPseudoClass;
571
572 type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
573 type AttrValue = AtomString;
574 type Identifier = AtomIdent;
575 type LocalName = LocalName;
576 type NamespacePrefix = Prefix;
577 type NamespaceUrl = Namespace;
578 type BorrowedLocalName = web_atoms::LocalName;
579 type BorrowedNamespaceUrl = web_atoms::Namespace;
580}
581
582impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
583 type Impl = SelectorImpl;
584 type Error = StyleParseErrorKind;
585
586 #[inline]
587 fn parse_nth_child_of(&self) -> bool {
588 crate::pref!("layout.css.nth-child-of.enabled")
589 }
590
591 #[inline]
592 fn parse_is_and_where(&self) -> bool {
593 true
594 }
595
596 #[inline]
597 fn parse_has(&self) -> bool {
598 crate::pref!("layout.css.has-selector.enabled")
599 }
600
601 #[inline]
602 fn parse_parent_selector(&self) -> bool {
603 true
604 }
605
606 #[inline]
607 fn parse_part(&self) -> bool {
608 true
609 }
610
611 #[inline]
612 fn allow_forgiving_selectors(&self) -> bool {
613 !self.for_supports_rule
614 }
615
616 fn parse_non_ts_pseudo_class(&self, name: &str) -> Result<NonTSPseudoClass, ParseError> {
617 let pseudo_class = match_ignore_ascii_case! { &name,
618 "active" => NonTSPseudoClass::Active,
619 "any-link" => NonTSPseudoClass::AnyLink,
620 "autofill" => NonTSPseudoClass::Autofill,
621 "checked" => NonTSPseudoClass::Checked,
622 "default" => NonTSPseudoClass::Default,
623 "defined" => NonTSPseudoClass::Defined,
624 "disabled" => NonTSPseudoClass::Disabled,
625 "enabled" => NonTSPseudoClass::Enabled,
626 "focus" => NonTSPseudoClass::Focus,
627 "focus-visible" => NonTSPseudoClass::FocusVisible,
628 "focus-within" => NonTSPseudoClass::FocusWithin,
629 "fullscreen" => NonTSPseudoClass::Fullscreen,
630 "hover" => NonTSPseudoClass::Hover,
631 "indeterminate" => NonTSPseudoClass::Indeterminate,
632 "invalid" => NonTSPseudoClass::Invalid,
633 "link" => NonTSPseudoClass::Link,
634 "modal" => NonTSPseudoClass::Modal,
635 "open" => NonTSPseudoClass::Open,
636 "optional" => NonTSPseudoClass::Optional,
637 "out-of-range" => NonTSPseudoClass::OutOfRange,
638 "placeholder-shown" => NonTSPseudoClass::PlaceholderShown,
639 "popover-open" => NonTSPseudoClass::PopoverOpen,
640 "read-only" => NonTSPseudoClass::ReadOnly,
641 "read-write" => NonTSPseudoClass::ReadWrite,
642 "required" => NonTSPseudoClass::Required,
643 "target" => NonTSPseudoClass::Target,
644 "user-invalid" => NonTSPseudoClass::UserInvalid,
645 "user-valid" => NonTSPseudoClass::UserValid,
646 "valid" => NonTSPseudoClass::Valid,
647 "visited" => NonTSPseudoClass::Visited,
648 "-moz-meter-optimum" => NonTSPseudoClass::MozMeterOptimum,
649 "-moz-meter-sub-optimum" => NonTSPseudoClass::MozMeterSubOptimum,
650 "-moz-meter-sub-sub-optimum" => NonTSPseudoClass::MozMeterSubSubOptimum,
651 "-servo-nonzero-border" => {
652 if !self.in_user_agent_stylesheet() {
653 return Err(ParseError::custom(
654 SelectorParseErrorKind::UnexpectedIdent
655 ))
656 }
657 NonTSPseudoClass::ServoNonZeroBorder
658 },
659 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
660 };
661
662 Ok(pseudo_class)
663 }
664
665 fn parse_non_ts_functional_pseudo_class(
666 &self,
667 name: &str,
668 parser: &mut CssParser<'i>,
669 after_part: bool,
670 ) -> Result<NonTSPseudoClass, ParseError> {
671 let pseudo_class = match_ignore_ascii_case! { &name,
672 "lang" if !after_part => {
673 NonTSPseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().into())
674 },
675 "state" => {
676 let result = AtomIdent::from(parser.expect_ident()?.as_ref());
677 NonTSPseudoClass::CustomState(CustomState(result))
678 },
679 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent)),
680 };
681
682 Ok(pseudo_class)
683 }
684
685 fn parse_pseudo_element(&self, name: &str) -> Result<PseudoElement, ParseError> {
686 use self::PseudoElement::*;
687 let pseudo_element = match_ignore_ascii_case! { &name,
688 "before" => Before,
689 "after" => After,
690 "backdrop" => Backdrop,
691 "selection" => Selection,
692 "file-selector-button" => FileSelectorButton,
693 "first-letter" => FirstLetter,
694 "marker" => Marker,
695 "details-content" => DetailsContent,
696 "color-swatch" => ColorSwatch,
697 "placeholder" => Placeholder,
698 "-servo-text-control-inner-container" => {
699 if !self.in_user_agent_stylesheet() {
700 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
701 }
702 ServoTextControlInnerContainer
703 },
704 "-servo-text-control-inner-editor" => {
705 if !self.in_user_agent_stylesheet() {
706 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
707 }
708 ServoTextControlInnerEditor
709 },
710 "slider-fill" => SliderFill,
711 "slider-thumb" => SliderThumb,
712 "slider-track" => SliderTrack,
713 "-moz-progress-bar" => MozProgressBar,
714 "-servo-anonymous-box" => {
715 if !self.in_user_agent_stylesheet() {
716 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
717 }
718 ServoAnonymousBox
719 },
720 "-servo-anonymous-table" => {
721 if !self.in_user_agent_stylesheet() {
722 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
723 }
724 ServoAnonymousTable
725 },
726 "-servo-anonymous-table-row" => {
727 if !self.in_user_agent_stylesheet() {
728 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
729 }
730 ServoAnonymousTableRow
731 },
732 "-servo-anonymous-table-cell" => {
733 if !self.in_user_agent_stylesheet() {
734 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
735 }
736 ServoAnonymousTableCell
737 },
738 "-servo-table-grid" => {
739 if !self.in_user_agent_stylesheet() {
740 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
741 }
742 ServoTableGrid
743 },
744 "-servo-table-wrapper" => {
745 if !self.in_user_agent_stylesheet() {
746 return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
747 }
748 ServoTableWrapper
749 },
750 _ => return Err(ParseError::custom(SelectorParseErrorKind::UnexpectedIdent))
751
752 };
753
754 Ok(pseudo_element)
755 }
756
757 fn default_namespace(&self) -> Option<Namespace> {
758 self.namespaces.default.as_ref().map(|ns| ns.clone())
759 }
760
761 fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
762 self.namespaces.prefixes.get(prefix).cloned()
763 }
764
765 fn parse_host(&self) -> bool {
766 true
767 }
768
769 fn parse_slotted(&self) -> bool {
770 true
771 }
772}
773
774impl SelectorImpl {
775 #[inline]
778 pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
779 where
780 F: FnMut(PseudoElement),
781 {
782 for i in 0..EAGER_PSEUDO_COUNT {
783 fun(PseudoElement::from_eager_index(i));
784 }
785 }
786}
787
788#[derive(Debug)]
790pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
791
792impl SnapshotMap {
793 pub fn new() -> Self {
795 SnapshotMap(FxHashMap::default())
796 }
797
798 pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
800 self.0.get(&el.as_node().opaque())
801 }
802}
803
804impl Deref for SnapshotMap {
805 type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
806
807 fn deref(&self) -> &Self::Target {
808 &self.0
809 }
810}
811
812impl DerefMut for SnapshotMap {
813 fn deref_mut(&mut self) -> &mut Self::Target {
814 &mut self.0
815 }
816}
817
818#[derive(Debug, Default, MallocSizeOf)]
820pub struct ServoElementSnapshot {
821 pub state: Option<ElementState>,
823 pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
825 pub changed_attrs: Vec<LocalName>,
827 pub class_changed: bool,
829 pub id_changed: bool,
831 pub other_attributes_changed: bool,
833}
834
835impl ServoElementSnapshot {
836 pub fn new() -> Self {
838 Self::default()
839 }
840
841 pub fn id_changed(&self) -> bool {
843 self.id_changed
844 }
845
846 pub fn class_changed(&self) -> bool {
848 self.class_changed
849 }
850
851 pub fn other_attr_changed(&self) -> bool {
853 self.other_attributes_changed
854 }
855
856 fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
857 self.attrs
858 .as_ref()?
859 .iter()
860 .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
861 .map(|&(_, ref v)| v)
862 }
863
864 #[inline]
866 pub fn each_attr_changed<F>(&self, mut callback: F)
867 where
868 F: FnMut(&LocalName),
869 {
870 for name in &self.changed_attrs {
871 callback(name)
872 }
873 }
874
875 fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
876 where
877 F: FnMut(&AttrValue) -> bool,
878 {
879 self.attrs.as_ref().is_some_and(|attrs| {
880 attrs
881 .iter()
882 .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
883 })
884 }
885}
886
887impl ElementSnapshot for ServoElementSnapshot {
888 fn state(&self) -> Option<ElementState> {
889 self.state.clone()
890 }
891
892 fn has_attrs(&self) -> bool {
893 self.attrs.is_some()
894 }
895
896 fn id_attr(&self) -> Option<&Atom> {
897 self.get_attr(&ns!(), &local_name!("id"))
898 .map(|v| v.as_atom())
899 }
900
901 fn is_part(&self, part_name: &AtomIdent) -> bool {
902 self.get_attr(&ns!(), &local_name!("part"))
903 .is_some_and(|v| {
904 v.as_tokens()
905 .iter()
906 .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))
907 })
908 }
909
910 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
911 None
912 }
913
914 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
915 self.get_attr(&ns!(), &local_name!("class"))
916 .map_or(false, |v| {
917 v.as_tokens()
918 .iter()
919 .any(|atom| case_sensitivity.eq_atom(atom, name))
920 })
921 }
922
923 fn each_class<F>(&self, mut callback: F)
924 where
925 F: FnMut(&AtomIdent),
926 {
927 if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
928 for class in v.as_tokens() {
929 callback(AtomIdent::cast(class));
930 }
931 }
932 }
933
934 fn lang_attr(&self) -> Option<SelectorAttrValue> {
935 self.get_attr(&ns!(xml), &local_name!("lang"))
936 .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
937 .map(|v| SelectorAttrValue::from(v as &str))
938 }
939
940 #[inline]
942 fn has_custom_states(&self) -> bool {
943 false
944 }
945
946 #[inline]
948 fn has_custom_state(&self, _state: &AtomIdent) -> bool {
949 false
950 }
951
952 #[inline]
953 fn each_custom_state<F>(&self, mut _callback: F)
954 where
955 F: FnMut(&AtomIdent),
956 {
957 }
958}
959
960impl ServoElementSnapshot {
961 pub fn attr_matches(
963 &self,
964 ns: &NamespaceConstraint<&Namespace>,
965 local_name: &LocalName,
966 operation: &AttrSelectorOperation<&AtomString>,
967 ) -> bool {
968 match *ns {
969 NamespaceConstraint::Specific(ref ns) => self
970 .get_attr(ns, local_name)
971 .map_or(false, |value| value.eval_selector(operation)),
972 NamespaceConstraint::Any => {
973 self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
974 },
975 }
976 }
977}
978
979pub fn extended_filtering(tag: &str, range: &str) -> bool {
982 range.split(',').any(|lang_range| {
983 let mut range_subtags = lang_range.split('\x2d');
985 let mut tag_subtags = tag.split('\x2d');
986
987 if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
990 if !(range_subtag.eq_ignore_ascii_case(tag_subtag)
991 || range_subtag.eq_ignore_ascii_case("*"))
992 {
993 return false;
994 }
995 }
996
997 let mut current_tag_subtag = tag_subtags.next();
998
999 for range_subtag in range_subtags {
1001 if range_subtag == "*" {
1003 continue;
1004 }
1005 match current_tag_subtag.clone() {
1006 Some(tag_subtag) => {
1007 if range_subtag.eq_ignore_ascii_case(tag_subtag) {
1009 current_tag_subtag = tag_subtags.next();
1010 continue;
1011 }
1012 if tag_subtag.len() == 1 {
1014 return false;
1015 }
1016 current_tag_subtag = tag_subtags.next();
1018 if current_tag_subtag.is_none() {
1019 return false;
1020 }
1021 },
1022 None => {
1024 return false;
1025 },
1026 }
1027 }
1028 true
1030 })
1031}