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