summaryrefslogtreecommitdiffstats
path: root/servo/components/selectors/visitor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/selectors/visitor.rs')
-rw-r--r--servo/components/selectors/visitor.rs136
1 files changed, 136 insertions, 0 deletions
diff --git a/servo/components/selectors/visitor.rs b/servo/components/selectors/visitor.rs
new file mode 100644
index 0000000000..d5befbc68b
--- /dev/null
+++ b/servo/components/selectors/visitor.rs
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Visitor traits for selectors.
+
+#![deny(missing_docs)]
+
+use crate::attr::NamespaceConstraint;
+use crate::parser::{Combinator, Component, RelativeSelector, Selector, SelectorImpl};
+
+/// A trait to visit selector properties.
+///
+/// All the `visit_foo` methods return a boolean indicating whether the
+/// traversal should continue or not.
+pub trait SelectorVisitor: Sized {
+ /// The selector implementation this visitor wants to visit.
+ type Impl: SelectorImpl;
+
+ /// Visit an attribute selector that may match (there are other selectors
+ /// that may never match, like those containing whitespace or the empty
+ /// string).
+ fn visit_attribute_selector(
+ &mut self,
+ _namespace: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
+ _local_name: &<Self::Impl as SelectorImpl>::LocalName,
+ _local_name_lower: &<Self::Impl as SelectorImpl>::LocalName,
+ ) -> bool {
+ true
+ }
+
+ /// Visit a simple selector.
+ fn visit_simple_selector(&mut self, _: &Component<Self::Impl>) -> bool {
+ true
+ }
+
+ /// Visit a nested relative selector list. The caller is responsible to call visit
+ /// into the internal selectors if / as needed.
+ ///
+ /// The default implementation skips it altogether.
+ fn visit_relative_selector_list(&mut self, _list: &[RelativeSelector<Self::Impl>]) -> bool {
+ true
+ }
+
+ /// Visit a nested selector list. The caller is responsible to call visit
+ /// into the internal selectors if / as needed.
+ ///
+ /// The default implementation does this.
+ fn visit_selector_list(
+ &mut self,
+ _list_kind: SelectorListKind,
+ list: &[Selector<Self::Impl>],
+ ) -> bool {
+ for nested in list {
+ if !nested.visit(self) {
+ return false;
+ }
+ }
+ true
+ }
+
+ /// Visits a complex selector.
+ ///
+ /// Gets the combinator to the right of the selector, or `None` if the
+ /// selector is the rightmost one.
+ fn visit_complex_selector(&mut self, _combinator_to_right: Option<Combinator>) -> bool {
+ true
+ }
+}
+
+bitflags! {
+ /// The kinds of components the visitor is visiting the selector list of, if any
+ #[derive(Clone, Copy, Default)]
+ pub struct SelectorListKind: u8 {
+ /// The visitor is inside :not(..)
+ const NEGATION = 1 << 0;
+ /// The visitor is inside :is(..)
+ const IS = 1 << 1;
+ /// The visitor is inside :where(..)
+ const WHERE = 1 << 2;
+ /// The visitor is inside :nth-child(.. of <selector list>) or
+ /// :nth-last-child(.. of <selector list>)
+ const NTH_OF = 1 << 3;
+ /// The visitor is inside :has(..)
+ const HAS = 1 << 4;
+ }
+}
+
+impl SelectorListKind {
+ /// Construct a SelectorListKind for the corresponding component.
+ pub fn from_component<Impl: SelectorImpl>(component: &Component<Impl>) -> Self {
+ match component {
+ Component::Negation(_) => SelectorListKind::NEGATION,
+ Component::Is(_) => SelectorListKind::IS,
+ Component::Where(_) => SelectorListKind::WHERE,
+ Component::NthOf(_) => SelectorListKind::NTH_OF,
+ _ => SelectorListKind::empty(),
+ }
+ }
+
+ /// Whether the visitor is inside :not(..)
+ pub fn in_negation(&self) -> bool {
+ self.intersects(SelectorListKind::NEGATION)
+ }
+
+ /// Whether the visitor is inside :is(..)
+ pub fn in_is(&self) -> bool {
+ self.intersects(SelectorListKind::IS)
+ }
+
+ /// Whether the visitor is inside :where(..)
+ pub fn in_where(&self) -> bool {
+ self.intersects(SelectorListKind::WHERE)
+ }
+
+ /// Whether the visitor is inside :nth-child(.. of <selector list>) or
+ /// :nth-last-child(.. of <selector list>)
+ pub fn in_nth_of(&self) -> bool {
+ self.intersects(SelectorListKind::NTH_OF)
+ }
+
+ /// Whether the visitor is inside :has(..)
+ pub fn in_has(&self) -> bool {
+ self.intersects(SelectorListKind::HAS)
+ }
+
+ /// Whether this nested selector is relevant for nth-of dependencies.
+ pub fn relevant_to_nth_of_dependencies(&self) -> bool {
+ // Order of nesting for `:has` and `:nth-child(.. of ..)` doesn't matter, because:
+ // * `:has(:nth-child(.. of ..))`: The location of the anchoring element is
+ // independent from where `:nth-child(.. of ..)` is applied.
+ // * `:nth-child(.. of :has(..))`: Invalidations inside `:has` must first use the
+ // `:has` machinary to find the anchor, then carry out the remaining invalidation.
+ self.in_nth_of() && !self.in_has()
+ }
+}