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(u8)]
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 const _: () = assert!(EAGER_PSEUDO_COUNT <= (u8::MAX as usize));
155 assert!(i < EAGER_PSEUDO_COUNT);
156 let result: PseudoElement = unsafe { mem::transmute(i as u8) };
157 debug_assert!(result.is_eager());
158 result
159 }
160
161 #[inline]
163 pub fn is_before_or_after(&self) -> bool {
164 self.is_before() || self.is_after()
165 }
166
167 #[inline]
169 pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
170 false
171 }
172
173 #[inline]
175 pub fn is_marker(&self) -> bool {
176 *self == PseudoElement::Marker
177 }
178
179 #[inline]
181 pub fn is_selection(&self) -> bool {
182 *self == PseudoElement::Selection
183 }
184
185 #[inline]
187 pub fn is_before(&self) -> bool {
188 *self == PseudoElement::Before
189 }
190
191 #[inline]
193 pub fn is_after(&self) -> bool {
194 *self == PseudoElement::After
195 }
196
197 #[inline]
199 pub fn is_first_letter(&self) -> bool {
200 *self == PseudoElement::FirstLetter
201 }
202
203 #[inline]
205 pub fn is_first_line(&self) -> bool {
206 false
207 }
208
209 #[inline]
212 pub fn is_color_swatch(&self) -> bool {
213 *self == PseudoElement::ColorSwatch
214 }
215
216 #[inline]
218 pub fn is_eager(&self) -> bool {
219 self.cascade_type() == PseudoElementCascadeType::Eager
220 }
221
222 #[inline]
224 pub fn is_lazy(&self) -> bool {
225 self.cascade_type() == PseudoElementCascadeType::Lazy
226 }
227
228 pub fn is_anon_box(&self) -> bool {
230 self.is_precomputed()
231 }
232
233 #[inline]
236 pub fn skip_item_display_fixup(&self) -> bool {
237 !self.is_before_or_after()
238 }
239
240 #[inline]
242 pub fn is_precomputed(&self) -> bool {
243 self.cascade_type() == PseudoElementCascadeType::Precomputed
244 }
245
246 #[inline]
254 pub fn cascade_type(&self) -> PseudoElementCascadeType {
255 match *self {
256 PseudoElement::After
257 | PseudoElement::Before
258 | PseudoElement::FirstLetter
259 | PseudoElement::Selection => PseudoElementCascadeType::Eager,
260 PseudoElement::Backdrop
261 | PseudoElement::ColorSwatch
262 | PseudoElement::FileSelectorButton
263 | PseudoElement::Marker
264 | PseudoElement::Placeholder
265 | PseudoElement::DetailsContent
266 | PseudoElement::SliderFill
267 | PseudoElement::SliderThumb
268 | PseudoElement::SliderTrack
269 | PseudoElement::ServoTextControlInnerContainer
270 | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy,
271 PseudoElement::ServoAnonymousBox
272 | PseudoElement::ServoAnonymousTable
273 | PseudoElement::ServoAnonymousTableCell
274 | PseudoElement::ServoAnonymousTableRow
275 | PseudoElement::ServoTableGrid
276 | PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed,
277 }
278 }
279
280 pub fn canonical(&self) -> PseudoElement {
283 self.clone()
284 }
285
286 pub fn pseudo_info(&self) {
288 ()
289 }
290
291 #[inline]
293 pub fn property_restriction(&self) -> Option<PropertyFlags> {
294 Some(match self {
295 PseudoElement::FirstLetter => PropertyFlags::APPLIES_TO_FIRST_LETTER,
296 PseudoElement::Marker if static_prefs::pref!("layout.css.marker.restricted") => {
297 PropertyFlags::APPLIES_TO_MARKER
298 },
299 PseudoElement::Placeholder => PropertyFlags::APPLIES_TO_PLACEHOLDER,
300 _ => return None,
301 })
302 }
303
304 pub fn should_exist(&self, style: &ComputedValues) -> bool {
307 let display = style.get_box().clone_display();
308 if display == Display::None {
309 return false;
310 }
311 if self.is_before_or_after() && style.ineffective_content_property() {
312 return false;
313 }
314
315 true
316 }
317
318 pub fn is_highlight(&self) -> bool {
320 false
321 }
322
323 #[inline]
325 pub fn is_target_text(&self) -> bool {
326 false
327 }
328
329 #[inline]
333 pub fn is_lazy_painted_highlight_pseudo(&self) -> bool {
334 self.is_selection() || self.is_highlight() || self.is_target_text()
335 }
336
337 #[inline]
340 pub fn is_element_backed(&self) -> bool {
341 use selectors::parser::PseudoElement;
342 self.parses_as_element_backed()
343 || matches!(
344 self,
345 Self::Placeholder
346 | Self::ColorSwatch
347 | Self::FileSelectorButton
348 | Self::SliderFill
349 | Self::SliderThumb
350 | Self::SliderTrack
351 | Self::ServoTextControlInnerContainer
352 | Self::ServoTextControlInnerEditor,
353 )
354 }
355}
356
357pub type Lang = Box<str>;
359
360#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
362pub struct CustomState(pub AtomIdent);
363
364#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
367#[allow(missing_docs)]
368pub enum NonTSPseudoClass {
369 Active,
370 AnyLink,
371 Autofill,
372 Checked,
373 CustomState(CustomState),
375 Default,
376 Defined,
377 Disabled,
378 Enabled,
379 Focus,
380 FocusWithin,
381 FocusVisible,
382 Fullscreen,
383 Hover,
384 InRange,
385 Indeterminate,
386 Invalid,
387 Lang(Lang),
388 Link,
389 Modal,
390 MozMeterOptimum,
391 MozMeterSubOptimum,
392 MozMeterSubSubOptimum,
393 Open,
394 Optional,
395 OutOfRange,
396 PlaceholderShown,
397 PopoverOpen,
398 ReadOnly,
399 ReadWrite,
400 Required,
401 ServoNonZeroBorder,
402 Target,
403 UserInvalid,
404 UserValid,
405 Valid,
406 Visited,
407}
408
409impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
410 type Impl = SelectorImpl;
411
412 #[inline]
413 fn is_active_or_hover(&self) -> bool {
414 matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
415 }
416
417 #[inline]
418 fn is_user_action_state(&self) -> bool {
419 matches!(
420 *self,
421 NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
422 )
423 }
424
425 fn visit<V>(&self, _: &mut V) -> bool
426 where
427 V: SelectorVisitor<Impl = Self::Impl>,
428 {
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<'i>;
585
586 #[inline]
587 fn parse_nth_child_of(&self) -> bool {
588 false
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 false
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(
617 &self,
618 location: SourceLocation,
619 name: CowRcStr<'i>,
620 ) -> Result<NonTSPseudoClass, ParseError<'i>> {
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(location.new_custom_error(
658 SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
659 ))
660 }
661 NonTSPseudoClass::ServoNonZeroBorder
662 },
663 _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
664 };
665
666 Ok(pseudo_class)
667 }
668
669 fn parse_non_ts_functional_pseudo_class<'t>(
670 &self,
671 name: CowRcStr<'i>,
672 parser: &mut CssParser<'i, 't>,
673 after_part: bool,
674 ) -> Result<NonTSPseudoClass, ParseError<'i>> {
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(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
684 };
685
686 Ok(pseudo_class)
687 }
688
689 fn parse_pseudo_element(
690 &self,
691 location: SourceLocation,
692 name: CowRcStr<'i>,
693 ) -> Result<PseudoElement, ParseError<'i>> {
694 use self::PseudoElement::*;
695 let pseudo_element = match_ignore_ascii_case! { &name,
696 "before" => Before,
697 "after" => After,
698 "backdrop" => Backdrop,
699 "selection" => Selection,
700 "file-selector-button" => FileSelectorButton,
701 "first-letter" => FirstLetter,
702 "marker" => Marker,
703 "details-content" => DetailsContent,
704 "color-swatch" => ColorSwatch,
705 "placeholder" => Placeholder,
706 "-servo-text-control-inner-container" => {
707 if !self.in_user_agent_stylesheet() {
708 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
709 }
710 ServoTextControlInnerContainer
711 },
712 "-servo-text-control-inner-editor" => {
713 if !self.in_user_agent_stylesheet() {
714 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
715 }
716 ServoTextControlInnerEditor
717 },
718 "slider-fill" => SliderFill,
719 "slider-thumb" => SliderThumb,
720 "slider-track" => SliderTrack,
721 "-servo-anonymous-box" => {
722 if !self.in_user_agent_stylesheet() {
723 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
724 }
725 ServoAnonymousBox
726 },
727 "-servo-anonymous-table" => {
728 if !self.in_user_agent_stylesheet() {
729 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
730 }
731 ServoAnonymousTable
732 },
733 "-servo-anonymous-table-row" => {
734 if !self.in_user_agent_stylesheet() {
735 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
736 }
737 ServoAnonymousTableRow
738 },
739 "-servo-anonymous-table-cell" => {
740 if !self.in_user_agent_stylesheet() {
741 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
742 }
743 ServoAnonymousTableCell
744 },
745 "-servo-table-grid" => {
746 if !self.in_user_agent_stylesheet() {
747 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
748 }
749 ServoTableGrid
750 },
751 "-servo-table-wrapper" => {
752 if !self.in_user_agent_stylesheet() {
753 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
754 }
755 ServoTableWrapper
756 },
757 _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
758
759 };
760
761 Ok(pseudo_element)
762 }
763
764 fn default_namespace(&self) -> Option<Namespace> {
765 self.namespaces.default.as_ref().map(|ns| ns.clone())
766 }
767
768 fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
769 self.namespaces.prefixes.get(prefix).cloned()
770 }
771
772 fn parse_host(&self) -> bool {
773 true
774 }
775
776 fn parse_slotted(&self) -> bool {
777 true
778 }
779}
780
781impl SelectorImpl {
782 #[inline]
785 pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
786 where
787 F: FnMut(PseudoElement),
788 {
789 for i in 0..EAGER_PSEUDO_COUNT {
790 fun(PseudoElement::from_eager_index(i));
791 }
792 }
793}
794
795#[derive(Debug)]
797pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
798
799impl SnapshotMap {
800 pub fn new() -> Self {
802 SnapshotMap(FxHashMap::default())
803 }
804
805 pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
807 self.0.get(&el.as_node().opaque())
808 }
809}
810
811impl Deref for SnapshotMap {
812 type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
813
814 fn deref(&self) -> &Self::Target {
815 &self.0
816 }
817}
818
819impl DerefMut for SnapshotMap {
820 fn deref_mut(&mut self) -> &mut Self::Target {
821 &mut self.0
822 }
823}
824
825#[derive(Debug, Default, MallocSizeOf)]
827pub struct ServoElementSnapshot {
828 pub state: Option<ElementState>,
830 pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
832 pub changed_attrs: Vec<LocalName>,
834 pub class_changed: bool,
836 pub id_changed: bool,
838 pub other_attributes_changed: bool,
840}
841
842impl ServoElementSnapshot {
843 pub fn new() -> Self {
845 Self::default()
846 }
847
848 pub fn id_changed(&self) -> bool {
850 self.id_changed
851 }
852
853 pub fn class_changed(&self) -> bool {
855 self.class_changed
856 }
857
858 pub fn other_attr_changed(&self) -> bool {
860 self.other_attributes_changed
861 }
862
863 fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
864 self.attrs
865 .as_ref()
866 .unwrap()
867 .iter()
868 .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
869 .map(|&(_, ref v)| v)
870 }
871
872 #[inline]
874 pub fn each_attr_changed<F>(&self, mut callback: F)
875 where
876 F: FnMut(&LocalName),
877 {
878 for name in &self.changed_attrs {
879 callback(name)
880 }
881 }
882
883 fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
884 where
885 F: FnMut(&AttrValue) -> bool,
886 {
887 self.attrs
888 .as_ref()
889 .unwrap()
890 .iter()
891 .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
892 }
893}
894
895impl ElementSnapshot for ServoElementSnapshot {
896 fn state(&self) -> Option<ElementState> {
897 self.state.clone()
898 }
899
900 fn has_attrs(&self) -> bool {
901 self.attrs.is_some()
902 }
903
904 fn id_attr(&self) -> Option<&Atom> {
905 self.get_attr(&ns!(), &local_name!("id"))
906 .map(|v| v.as_atom())
907 }
908
909 fn is_part(&self, part_name: &AtomIdent) -> bool {
910 self.get_attr(&ns!(), &local_name!("part"))
911 .is_some_and(|v| {
912 v.as_tokens()
913 .iter()
914 .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))
915 })
916 }
917
918 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
919 None
920 }
921
922 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
923 self.get_attr(&ns!(), &local_name!("class"))
924 .map_or(false, |v| {
925 v.as_tokens()
926 .iter()
927 .any(|atom| case_sensitivity.eq_atom(atom, name))
928 })
929 }
930
931 fn each_class<F>(&self, mut callback: F)
932 where
933 F: FnMut(&AtomIdent),
934 {
935 if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
936 for class in v.as_tokens() {
937 callback(AtomIdent::cast(class));
938 }
939 }
940 }
941
942 fn lang_attr(&self) -> Option<SelectorAttrValue> {
943 self.get_attr(&ns!(xml), &local_name!("lang"))
944 .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
945 .map(|v| SelectorAttrValue::from(v as &str))
946 }
947
948 #[inline]
950 fn has_custom_states(&self) -> bool {
951 false
952 }
953
954 #[inline]
956 fn has_custom_state(&self, _state: &AtomIdent) -> bool {
957 false
958 }
959
960 #[inline]
961 fn each_custom_state<F>(&self, mut _callback: F)
962 where
963 F: FnMut(&AtomIdent),
964 {
965 }
966}
967
968impl ServoElementSnapshot {
969 pub fn attr_matches(
971 &self,
972 ns: &NamespaceConstraint<&Namespace>,
973 local_name: &LocalName,
974 operation: &AttrSelectorOperation<&AtomString>,
975 ) -> bool {
976 match *ns {
977 NamespaceConstraint::Specific(ref ns) => self
978 .get_attr(ns, local_name)
979 .map_or(false, |value| value.eval_selector(operation)),
980 NamespaceConstraint::Any => {
981 self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
982 },
983 }
984 }
985}
986
987pub fn extended_filtering(tag: &str, range: &str) -> bool {
990 range.split(',').any(|lang_range| {
991 let mut range_subtags = lang_range.split('\x2d');
993 let mut tag_subtags = tag.split('\x2d');
994
995 if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
998 if !(range_subtag.eq_ignore_ascii_case(tag_subtag)
999 || range_subtag.eq_ignore_ascii_case("*"))
1000 {
1001 return false;
1002 }
1003 }
1004
1005 let mut current_tag_subtag = tag_subtags.next();
1006
1007 for range_subtag in range_subtags {
1009 if range_subtag == "*" {
1011 continue;
1012 }
1013 match current_tag_subtag.clone() {
1014 Some(tag_subtag) => {
1015 if range_subtag.eq_ignore_ascii_case(tag_subtag) {
1017 current_tag_subtag = tag_subtags.next();
1018 continue;
1019 }
1020 if tag_subtag.len() == 1 {
1022 return false;
1023 }
1024 current_tag_subtag = tag_subtags.next();
1026 if current_tag_subtag.is_none() {
1027 return false;
1028 }
1029 },
1030 None => {
1032 return false;
1033 },
1034 }
1035 }
1036 true
1038 })
1039}