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 => "::-servo-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::ServoTextControlInnerContainer
252 | PseudoElement::ServoTextControlInnerEditor => PseudoElementCascadeType::Lazy,
253 PseudoElement::DetailsContent
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
295pub type Lang = Box<str>;
297
298#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
300pub struct CustomState(pub AtomIdent);
301
302#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
305#[allow(missing_docs)]
306pub enum NonTSPseudoClass {
307 Active,
308 AnyLink,
309 Autofill,
310 Checked,
311 CustomState(CustomState),
313 Default,
314 Defined,
315 Disabled,
316 Enabled,
317 Focus,
318 FocusWithin,
319 FocusVisible,
320 Fullscreen,
321 Hover,
322 InRange,
323 Indeterminate,
324 Invalid,
325 Lang(Lang),
326 Link,
327 Modal,
328 MozMeterOptimum,
329 MozMeterSubOptimum,
330 MozMeterSubSubOptimum,
331 Optional,
332 OutOfRange,
333 PlaceholderShown,
334 PopoverOpen,
335 ReadOnly,
336 ReadWrite,
337 Required,
338 ServoNonZeroBorder,
339 Target,
340 UserInvalid,
341 UserValid,
342 Valid,
343 Visited,
344}
345
346impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
347 type Impl = SelectorImpl;
348
349 #[inline]
350 fn is_active_or_hover(&self) -> bool {
351 matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
352 }
353
354 #[inline]
355 fn is_user_action_state(&self) -> bool {
356 matches!(
357 *self,
358 NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
359 )
360 }
361
362 fn visit<V>(&self, _: &mut V) -> bool
363 where
364 V: SelectorVisitor<Impl = Self::Impl>,
365 {
366 true
367 }
368}
369
370impl ToCss for NonTSPseudoClass {
371 fn to_css<W>(&self, dest: &mut W) -> fmt::Result
372 where
373 W: fmt::Write,
374 {
375 use self::NonTSPseudoClass::*;
376 if let Lang(ref lang) = *self {
377 dest.write_str(":lang(")?;
378 serialize_identifier(lang, dest)?;
379 return dest.write_char(')');
380 }
381
382 dest.write_str(match *self {
383 Self::Active => ":active",
384 Self::AnyLink => ":any-link",
385 Self::Autofill => ":autofill",
386 Self::Checked => ":checked",
387 Self::CustomState(ref state) => {
388 dest.write_str(":state(")?;
389 state.0.to_css(dest)?;
390 return dest.write_char(')');
391 },
392 Self::Default => ":default",
393 Self::Defined => ":defined",
394 Self::Disabled => ":disabled",
395 Self::Enabled => ":enabled",
396 Self::Focus => ":focus",
397 Self::FocusVisible => ":focus-visible",
398 Self::FocusWithin => ":focus-within",
399 Self::Fullscreen => ":fullscreen",
400 Self::Hover => ":hover",
401 Self::InRange => ":in-range",
402 Self::Indeterminate => ":indeterminate",
403 Self::Invalid => ":invalid",
404 Self::Link => ":link",
405 Self::Modal => ":modal",
406 Self::MozMeterOptimum => ":-moz-meter-optimum",
407 Self::MozMeterSubOptimum => ":-moz-meter-sub-optimum",
408 Self::MozMeterSubSubOptimum => ":-moz-meter-sub-sub-optimum",
409 Self::Optional => ":optional",
410 Self::OutOfRange => ":out-of-range",
411 Self::PlaceholderShown => ":placeholder-shown",
412 Self::PopoverOpen => ":popover-open",
413 Self::ReadOnly => ":read-only",
414 Self::ReadWrite => ":read-write",
415 Self::Required => ":required",
416 Self::ServoNonZeroBorder => ":-servo-nonzero-border",
417 Self::Target => ":target",
418 Self::UserInvalid => ":user-invalid",
419 Self::UserValid => ":user-valid",
420 Self::Valid => ":valid",
421 Self::Visited => ":visited",
422 Self::Lang(_) => unreachable!(),
423 })
424 }
425}
426
427impl NonTSPseudoClass {
428 pub fn state_flag(&self) -> ElementState {
431 match *self {
432 Self::Active => ElementState::ACTIVE,
433 Self::AnyLink => ElementState::VISITED_OR_UNVISITED,
434 Self::Autofill => ElementState::AUTOFILL,
435 Self::Checked => ElementState::CHECKED,
436 Self::Default => ElementState::DEFAULT,
437 Self::Defined => ElementState::DEFINED,
438 Self::Disabled => ElementState::DISABLED,
439 Self::Enabled => ElementState::ENABLED,
440 Self::Focus => ElementState::FOCUS,
441 Self::FocusVisible => ElementState::FOCUSRING,
442 Self::FocusWithin => ElementState::FOCUS_WITHIN,
443 Self::Fullscreen => ElementState::FULLSCREEN,
444 Self::Hover => ElementState::HOVER,
445 Self::InRange => ElementState::INRANGE,
446 Self::Indeterminate => ElementState::INDETERMINATE,
447 Self::Invalid => ElementState::INVALID,
448 Self::Link => ElementState::UNVISITED,
449 Self::Modal => ElementState::MODAL,
450 Self::MozMeterOptimum => ElementState::OPTIMUM,
451 Self::MozMeterSubOptimum => ElementState::SUB_OPTIMUM,
452 Self::MozMeterSubSubOptimum => ElementState::SUB_SUB_OPTIMUM,
453 Self::Optional => ElementState::OPTIONAL_,
454 Self::OutOfRange => ElementState::OUTOFRANGE,
455 Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
456 Self::PopoverOpen => ElementState::POPOVER_OPEN,
457 Self::ReadOnly => ElementState::READONLY,
458 Self::ReadWrite => ElementState::READWRITE,
459 Self::Required => ElementState::REQUIRED,
460 Self::Target => ElementState::URLTARGET,
461 Self::UserInvalid => ElementState::USER_INVALID,
462 Self::UserValid => ElementState::USER_VALID,
463 Self::Valid => ElementState::VALID,
464 Self::Visited => ElementState::VISITED,
465 Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => {
466 ElementState::empty()
467 },
468 }
469 }
470
471 pub fn document_state_flag(&self) -> DocumentState {
473 DocumentState::empty()
474 }
475
476 pub fn needs_cache_revalidation(&self) -> bool {
478 self.state_flag().is_empty()
479 }
480}
481
482#[derive(Clone, Debug, PartialEq)]
485#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
486pub struct SelectorImpl;
487
488#[derive(Debug, Default)]
491pub struct ExtraMatchingData<'a> {
492 pub invalidation_data: InvalidationMatchingData,
494
495 pub cascade_input_flags: ComputedValueFlags,
498
499 pub originating_element_style: Option<&'a ComputedValues>,
502}
503
504impl ::selectors::SelectorImpl for SelectorImpl {
505 type PseudoElement = PseudoElement;
506 type NonTSPseudoClass = NonTSPseudoClass;
507
508 type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
509 type AttrValue = AtomString;
510 type Identifier = AtomIdent;
511 type LocalName = LocalName;
512 type NamespacePrefix = Prefix;
513 type NamespaceUrl = Namespace;
514 type BorrowedLocalName = web_atoms::LocalName;
515 type BorrowedNamespaceUrl = web_atoms::Namespace;
516}
517
518impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
519 type Impl = SelectorImpl;
520 type Error = StyleParseErrorKind<'i>;
521
522 #[inline]
523 fn parse_nth_child_of(&self) -> bool {
524 false
525 }
526
527 #[inline]
528 fn parse_is_and_where(&self) -> bool {
529 true
530 }
531
532 #[inline]
533 fn parse_has(&self) -> bool {
534 false
535 }
536
537 #[inline]
538 fn parse_parent_selector(&self) -> bool {
539 true
540 }
541
542 #[inline]
543 fn parse_part(&self) -> bool {
544 true
545 }
546
547 #[inline]
548 fn allow_forgiving_selectors(&self) -> bool {
549 !self.for_supports_rule
550 }
551
552 fn parse_non_ts_pseudo_class(
553 &self,
554 location: SourceLocation,
555 name: CowRcStr<'i>,
556 ) -> Result<NonTSPseudoClass, ParseError<'i>> {
557 let pseudo_class = match_ignore_ascii_case! { &name,
558 "active" => NonTSPseudoClass::Active,
559 "any-link" => NonTSPseudoClass::AnyLink,
560 "autofill" => NonTSPseudoClass::Autofill,
561 "checked" => NonTSPseudoClass::Checked,
562 "default" => NonTSPseudoClass::Default,
563 "defined" => NonTSPseudoClass::Defined,
564 "disabled" => NonTSPseudoClass::Disabled,
565 "enabled" => NonTSPseudoClass::Enabled,
566 "focus" => NonTSPseudoClass::Focus,
567 "focus-visible" => NonTSPseudoClass::FocusVisible,
568 "focus-within" => NonTSPseudoClass::FocusWithin,
569 "fullscreen" => NonTSPseudoClass::Fullscreen,
570 "hover" => NonTSPseudoClass::Hover,
571 "indeterminate" => NonTSPseudoClass::Indeterminate,
572 "invalid" => NonTSPseudoClass::Invalid,
573 "link" => NonTSPseudoClass::Link,
574 "optional" => NonTSPseudoClass::Optional,
575 "out-of-range" => NonTSPseudoClass::OutOfRange,
576 "placeholder-shown" => NonTSPseudoClass::PlaceholderShown,
577 "popover-open" => NonTSPseudoClass::PopoverOpen,
578 "read-only" => NonTSPseudoClass::ReadOnly,
579 "read-write" => NonTSPseudoClass::ReadWrite,
580 "required" => NonTSPseudoClass::Required,
581 "target" => NonTSPseudoClass::Target,
582 "user-invalid" => NonTSPseudoClass::UserInvalid,
583 "user-valid" => NonTSPseudoClass::UserValid,
584 "valid" => NonTSPseudoClass::Valid,
585 "visited" => NonTSPseudoClass::Visited,
586 "-moz-meter-optimum" => NonTSPseudoClass::MozMeterOptimum,
587 "-moz-meter-sub-optimum" => NonTSPseudoClass::MozMeterSubOptimum,
588 "-moz-meter-sub-sub-optimum" => NonTSPseudoClass::MozMeterSubSubOptimum,
589 "-servo-nonzero-border" => {
590 if !self.in_user_agent_stylesheet() {
591 return Err(location.new_custom_error(
592 SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
593 ))
594 }
595 NonTSPseudoClass::ServoNonZeroBorder
596 },
597 _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
598 };
599
600 Ok(pseudo_class)
601 }
602
603 fn parse_non_ts_functional_pseudo_class<'t>(
604 &self,
605 name: CowRcStr<'i>,
606 parser: &mut CssParser<'i, 't>,
607 after_part: bool,
608 ) -> Result<NonTSPseudoClass, ParseError<'i>> {
609 let pseudo_class = match_ignore_ascii_case! { &name,
610 "lang" if !after_part => {
611 NonTSPseudoClass::Lang(parser.expect_ident_or_string()?.as_ref().into())
612 },
613 "state" => {
614 let result = AtomIdent::from(parser.expect_ident()?.as_ref());
615 NonTSPseudoClass::CustomState(CustomState(result))
616 },
617 _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
618 };
619
620 Ok(pseudo_class)
621 }
622
623 fn parse_pseudo_element(
624 &self,
625 location: SourceLocation,
626 name: CowRcStr<'i>,
627 ) -> Result<PseudoElement, ParseError<'i>> {
628 use self::PseudoElement::*;
629 let pseudo_element = match_ignore_ascii_case! { &name,
630 "before" => Before,
631 "after" => After,
632 "backdrop" => Backdrop,
633 "selection" => Selection,
634 "marker" => Marker,
635 "-servo-details-summary" => {
636 if !self.in_user_agent_stylesheet() {
637 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
638 }
639 DetailsSummary
640 },
641 "-servo-details-content" => {
642 if !self.in_user_agent_stylesheet() {
643 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
644 }
645 DetailsContent
646 },
647 "color-swatch" => ColorSwatch,
648 "placeholder" => {
649 if !self.in_user_agent_stylesheet() {
650 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
651 }
652 Placeholder
653 },
654 "-servo-text-control-inner-container" => {
655 if !self.in_user_agent_stylesheet() {
656 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
657 }
658 ServoTextControlInnerContainer
659 },
660 "-servo-text-control-inner-editor" => {
661 if !self.in_user_agent_stylesheet() {
662 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
663 }
664 ServoTextControlInnerEditor
665 },
666 "-servo-anonymous-box" => {
667 if !self.in_user_agent_stylesheet() {
668 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
669 }
670 ServoAnonymousBox
671 },
672 "-servo-anonymous-table" => {
673 if !self.in_user_agent_stylesheet() {
674 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
675 }
676 ServoAnonymousTable
677 },
678 "-servo-anonymous-table-row" => {
679 if !self.in_user_agent_stylesheet() {
680 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
681 }
682 ServoAnonymousTableRow
683 },
684 "-servo-anonymous-table-cell" => {
685 if !self.in_user_agent_stylesheet() {
686 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
687 }
688 ServoAnonymousTableCell
689 },
690 "-servo-table-grid" => {
691 if !self.in_user_agent_stylesheet() {
692 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
693 }
694 ServoTableGrid
695 },
696 "-servo-table-wrapper" => {
697 if !self.in_user_agent_stylesheet() {
698 return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
699 }
700 ServoTableWrapper
701 },
702 _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
703
704 };
705
706 Ok(pseudo_element)
707 }
708
709 fn default_namespace(&self) -> Option<Namespace> {
710 self.namespaces.default.as_ref().map(|ns| ns.clone())
711 }
712
713 fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
714 self.namespaces.prefixes.get(prefix).cloned()
715 }
716
717 fn parse_host(&self) -> bool {
718 true
719 }
720
721 fn parse_slotted(&self) -> bool {
722 true
723 }
724}
725
726impl SelectorImpl {
727 #[inline]
730 pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
731 where
732 F: FnMut(PseudoElement),
733 {
734 for i in 0..EAGER_PSEUDO_COUNT {
735 fun(PseudoElement::from_eager_index(i));
736 }
737 }
738}
739
740#[derive(Debug)]
742pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
743
744impl SnapshotMap {
745 pub fn new() -> Self {
747 SnapshotMap(FxHashMap::default())
748 }
749
750 pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
752 self.0.get(&el.as_node().opaque())
753 }
754}
755
756impl Deref for SnapshotMap {
757 type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
758
759 fn deref(&self) -> &Self::Target {
760 &self.0
761 }
762}
763
764impl DerefMut for SnapshotMap {
765 fn deref_mut(&mut self) -> &mut Self::Target {
766 &mut self.0
767 }
768}
769
770#[derive(Debug, Default, MallocSizeOf)]
772pub struct ServoElementSnapshot {
773 pub state: Option<ElementState>,
775 pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
777 pub changed_attrs: Vec<LocalName>,
779 pub class_changed: bool,
781 pub id_changed: bool,
783 pub other_attributes_changed: bool,
785}
786
787impl ServoElementSnapshot {
788 pub fn new() -> Self {
790 Self::default()
791 }
792
793 pub fn id_changed(&self) -> bool {
795 self.id_changed
796 }
797
798 pub fn class_changed(&self) -> bool {
800 self.class_changed
801 }
802
803 pub fn other_attr_changed(&self) -> bool {
805 self.other_attributes_changed
806 }
807
808 fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
809 self.attrs
810 .as_ref()
811 .unwrap()
812 .iter()
813 .find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
814 .map(|&(_, ref v)| v)
815 }
816
817 #[inline]
819 pub fn each_attr_changed<F>(&self, mut callback: F)
820 where
821 F: FnMut(&LocalName),
822 {
823 for name in &self.changed_attrs {
824 callback(name)
825 }
826 }
827
828 fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
829 where
830 F: FnMut(&AttrValue) -> bool,
831 {
832 self.attrs
833 .as_ref()
834 .unwrap()
835 .iter()
836 .any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
837 }
838}
839
840impl ElementSnapshot for ServoElementSnapshot {
841 fn state(&self) -> Option<ElementState> {
842 self.state.clone()
843 }
844
845 fn has_attrs(&self) -> bool {
846 self.attrs.is_some()
847 }
848
849 fn id_attr(&self) -> Option<&Atom> {
850 self.get_attr(&ns!(), &local_name!("id"))
851 .map(|v| v.as_atom())
852 }
853
854 fn is_part(&self, part_name: &AtomIdent) -> bool {
855 self.get_attr(&ns!(), &local_name!("part"))
856 .is_some_and(|v| {
857 v.as_tokens()
858 .iter()
859 .any(|atom| CaseSensitivity::CaseSensitive.eq_atom(atom, part_name))
860 })
861 }
862
863 fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
864 None
865 }
866
867 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
868 self.get_attr(&ns!(), &local_name!("class"))
869 .map_or(false, |v| {
870 v.as_tokens()
871 .iter()
872 .any(|atom| case_sensitivity.eq_atom(atom, name))
873 })
874 }
875
876 fn each_class<F>(&self, mut callback: F)
877 where
878 F: FnMut(&AtomIdent),
879 {
880 if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
881 for class in v.as_tokens() {
882 callback(AtomIdent::cast(class));
883 }
884 }
885 }
886
887 fn lang_attr(&self) -> Option<SelectorAttrValue> {
888 self.get_attr(&ns!(xml), &local_name!("lang"))
889 .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
890 .map(|v| SelectorAttrValue::from(v as &str))
891 }
892
893 #[inline]
895 fn has_custom_states(&self) -> bool {
896 false
897 }
898
899 #[inline]
901 fn has_custom_state(&self, _state: &AtomIdent) -> bool {
902 false
903 }
904
905 #[inline]
906 fn each_custom_state<F>(&self, mut _callback: F)
907 where
908 F: FnMut(&AtomIdent),
909 {
910 }
911}
912
913impl ServoElementSnapshot {
914 pub fn attr_matches(
916 &self,
917 ns: &NamespaceConstraint<&Namespace>,
918 local_name: &LocalName,
919 operation: &AttrSelectorOperation<&AtomString>,
920 ) -> bool {
921 match *ns {
922 NamespaceConstraint::Specific(ref ns) => self
923 .get_attr(ns, local_name)
924 .map_or(false, |value| value.eval_selector(operation)),
925 NamespaceConstraint::Any => {
926 self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
927 },
928 }
929 }
930}
931
932pub fn extended_filtering(tag: &str, range: &str) -> bool {
935 range.split(',').any(|lang_range| {
936 let mut range_subtags = lang_range.split('\x2d');
938 let mut tag_subtags = tag.split('\x2d');
939
940 if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
943 if !(range_subtag.eq_ignore_ascii_case(tag_subtag)
944 || range_subtag.eq_ignore_ascii_case("*"))
945 {
946 return false;
947 }
948 }
949
950 let mut current_tag_subtag = tag_subtags.next();
951
952 for range_subtag in range_subtags {
954 if range_subtag == "*" {
956 continue;
957 }
958 match current_tag_subtag.clone() {
959 Some(tag_subtag) => {
960 if range_subtag.eq_ignore_ascii_case(tag_subtag) {
962 current_tag_subtag = tag_subtags.next();
963 continue;
964 }
965 if tag_subtag.len() == 1 {
967 return false;
968 }
969 current_tag_subtag = tag_subtags.next();
971 if current_tag_subtag.is_none() {
972 return false;
973 }
974 },
975 None => {
977 return false;
978 },
979 }
980 }
981 true
983 })
984}