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, PartialEq)]
549#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
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}