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