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