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