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