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