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