mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
selectors: Remove custom attribute-affecting logic and sibling-affecting logic from rust-selectors.
MozReview-Commit-ID: BjZY6TjJbcb Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
63988b9103
commit
1748150497
4 changed files with 154 additions and 120 deletions
|
@ -12,6 +12,7 @@ pub mod bloom;
|
|||
pub mod matching;
|
||||
pub mod parser;
|
||||
mod tree;
|
||||
pub mod visitor;
|
||||
|
||||
pub use parser::{SelectorImpl, Parser, SelectorList};
|
||||
pub use tree::Element;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
use bloom::BloomFilter;
|
||||
use parser::{CaseSensitivity, Combinator, ComplexSelector, LocalName};
|
||||
use parser::{SimpleSelector, Selector, SelectorImpl};
|
||||
use parser::{SimpleSelector, Selector};
|
||||
use precomputed_hash::PrecomputedHash;
|
||||
use std::borrow::Borrow;
|
||||
use tree::Element;
|
||||
|
@ -35,9 +35,8 @@ bitflags! {
|
|||
/// Whether this element is affected by an ID selector.
|
||||
const AFFECTED_BY_ID_SELECTOR = 1 << 3,
|
||||
|
||||
/// Whether this element is affected by a non-common style-affecting
|
||||
/// attribute.
|
||||
const AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR = 1 << 4,
|
||||
/// Whether this element matches attribute selectors.
|
||||
const AFFECTED_BY_ATTRIBUTE_SELECTOR = 1 << 4,
|
||||
|
||||
/// Whether this element matches the :empty pseudo class.
|
||||
const AFFECTED_BY_EMPTY = 1 << 5,
|
||||
|
@ -364,48 +363,38 @@ fn matches_simple_selector<E, F>(
|
|||
AFFECTED_BY_ID_SELECTOR)
|
||||
}
|
||||
SimpleSelector::Class(ref class) => {
|
||||
element.has_class(class)
|
||||
relation_if!(element.has_class(class),
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrExists(ref attr) => {
|
||||
let matches = element.match_attr_has(attr);
|
||||
|
||||
if matches && !E::Impl::attr_exists_selector_is_shareable(attr) {
|
||||
*relations |= AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR;
|
||||
}
|
||||
|
||||
matches
|
||||
relation_if!(element.match_attr_has(attr),
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrEqual(ref attr, ref value, case_sensitivity) => {
|
||||
let matches = match case_sensitivity {
|
||||
relation_if!(match case_sensitivity {
|
||||
CaseSensitivity::CaseSensitive => element.match_attr_equals(attr, value),
|
||||
CaseSensitivity::CaseInsensitive => element.match_attr_equals_ignore_ascii_case(attr, value),
|
||||
};
|
||||
|
||||
if matches && !E::Impl::attr_equals_selector_is_shareable(attr, value) {
|
||||
*relations |= AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR;
|
||||
}
|
||||
|
||||
matches
|
||||
}, AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrIncludes(ref attr, ref value) => {
|
||||
relation_if!(element.match_attr_includes(attr, value),
|
||||
AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR)
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrDashMatch(ref attr, ref value) => {
|
||||
relation_if!(element.match_attr_dash(attr, value),
|
||||
AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR)
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrPrefixMatch(ref attr, ref value) => {
|
||||
relation_if!(element.match_attr_prefix(attr, value),
|
||||
AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR)
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrSubstringMatch(ref attr, ref value) => {
|
||||
relation_if!(element.match_attr_substring(attr, value),
|
||||
AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR)
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrSuffixMatch(ref attr, ref value) => {
|
||||
relation_if!(element.match_attr_suffix(attr, value),
|
||||
AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE_SELECTOR)
|
||||
AFFECTED_BY_ATTRIBUTE_SELECTOR)
|
||||
}
|
||||
SimpleSelector::AttrIncludesNeverMatch(..) |
|
||||
SimpleSelector::AttrPrefixNeverMatch(..) |
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::hash::Hash;
|
|||
use std::ops::Add;
|
||||
use std::sync::Arc;
|
||||
use tree::SELECTOR_WHITESPACE;
|
||||
use visitor::SelectorVisitor;
|
||||
|
||||
macro_rules! with_all_bounds {
|
||||
(
|
||||
|
@ -50,26 +51,10 @@ macro_rules! with_all_bounds {
|
|||
|
||||
/// non tree-structural pseudo-classes
|
||||
/// (see: https://drafts.csswg.org/selectors/#structural-pseudos)
|
||||
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods;
|
||||
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>;
|
||||
|
||||
/// pseudo-elements
|
||||
type PseudoElement: $($CommonBounds)* + Sized + ToCss;
|
||||
|
||||
/// Declares if the following "attribute exists" selector is considered
|
||||
/// "common" enough to be shareable. If that's not the case, when matching
|
||||
/// over an element, the relation
|
||||
/// AFFECTED_BY_NON_COMMON_STYLE_AFFECTING_ATTRIBUTE would be set.
|
||||
fn attr_exists_selector_is_shareable(_attr_selector: &AttrSelector<Self>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Declares if the following "equals" attribute selector is considered
|
||||
/// "common" enough to be shareable.
|
||||
fn attr_equals_selector_is_shareable(_attr_selector: &AttrSelector<Self>,
|
||||
_value: &Self::AttrValue)
|
||||
-> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,93 +128,91 @@ pub struct Selector<Impl: SelectorImpl> {
|
|||
pub specificity: u32,
|
||||
}
|
||||
|
||||
fn affects_sibling<Impl: SelectorImpl>(simple_selector: &SimpleSelector<Impl>) -> bool {
|
||||
match *simple_selector {
|
||||
SimpleSelector::Negation(ref negated) => {
|
||||
negated.iter().any(|ref selector| selector.affects_siblings())
|
||||
}
|
||||
|
||||
SimpleSelector::FirstChild |
|
||||
SimpleSelector::LastChild |
|
||||
SimpleSelector::OnlyChild |
|
||||
SimpleSelector::NthChild(..) |
|
||||
SimpleSelector::NthLastChild(..) |
|
||||
SimpleSelector::NthOfType(..) |
|
||||
SimpleSelector::NthLastOfType(..) |
|
||||
SimpleSelector::FirstOfType |
|
||||
SimpleSelector::LastOfType |
|
||||
SimpleSelector::OnlyOfType => true,
|
||||
|
||||
SimpleSelector::NonTSPseudoClass(ref pseudo_class) => pseudo_class.affects_siblings(),
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_non_common_style_affecting_attribute<Impl: SelectorImpl>(simple_selector: &SimpleSelector<Impl>) -> bool {
|
||||
match *simple_selector {
|
||||
SimpleSelector::Negation(ref negated) => {
|
||||
negated.iter().any(|ref selector| selector.matches_non_common_style_affecting_attribute())
|
||||
}
|
||||
SimpleSelector::AttrEqual(ref attr, ref val, _) => {
|
||||
!Impl::attr_equals_selector_is_shareable(attr, val)
|
||||
}
|
||||
SimpleSelector::AttrExists(ref attr) => {
|
||||
!Impl::attr_exists_selector_is_shareable(attr)
|
||||
}
|
||||
SimpleSelector::AttrIncludes(..) |
|
||||
SimpleSelector::AttrDashMatch(..) |
|
||||
SimpleSelector::AttrPrefixMatch(..) |
|
||||
SimpleSelector::AttrSuffixMatch(..) |
|
||||
SimpleSelector::AttrSubstringMatch(..) => true,
|
||||
|
||||
SimpleSelector::NonTSPseudoClass(ref pseudo_class) =>
|
||||
pseudo_class.matches_non_common_style_affecting_attribute(),
|
||||
|
||||
// This deliberately includes Attr*NeverMatch
|
||||
// which never match regardless of element attributes.
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SelectorMethods {
|
||||
fn affects_siblings(&self) -> bool;
|
||||
fn matches_non_common_style_affecting_attribute(&self) -> bool;
|
||||
type Impl: SelectorImpl;
|
||||
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Self::Impl>;
|
||||
}
|
||||
|
||||
impl<Impl: SelectorImpl> SelectorMethods for Selector<Impl> {
|
||||
/// Whether this selector, if matching on a set of siblings, could affect
|
||||
/// other sibling's style.
|
||||
fn affects_siblings(&self) -> bool {
|
||||
self.complex_selector.affects_siblings()
|
||||
}
|
||||
type Impl = Impl;
|
||||
|
||||
fn matches_non_common_style_affecting_attribute(&self) -> bool {
|
||||
self.complex_selector.matches_non_common_style_affecting_attribute()
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Impl>,
|
||||
{
|
||||
self.complex_selector.visit(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Impl: SelectorImpl> SelectorMethods for ComplexSelector<Impl> {
|
||||
/// Whether this complex selector, if matching on a set of siblings,
|
||||
/// could affect other sibling's style.
|
||||
fn affects_siblings(&self) -> bool {
|
||||
match self.next {
|
||||
Some((_, Combinator::NextSibling)) |
|
||||
Some((_, Combinator::LaterSibling)) => return true,
|
||||
_ => {},
|
||||
type Impl = Impl;
|
||||
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Impl>,
|
||||
{
|
||||
let mut current = self;
|
||||
loop {
|
||||
if !visitor.visit_complex_selector(current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for selector in ¤t.compound_selector {
|
||||
if !selector.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
match current.next {
|
||||
Some((ref next, _)) => current = next,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
match self.compound_selector.last() {
|
||||
Some(ref selector) => affects_sibling(selector),
|
||||
None => false,
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn matches_non_common_style_affecting_attribute(&self) -> bool {
|
||||
match self.compound_selector.last() {
|
||||
Some(ref selector) => matches_non_common_style_affecting_attribute(selector),
|
||||
None => false,
|
||||
impl<Impl: SelectorImpl> SelectorMethods for SimpleSelector<Impl> {
|
||||
type Impl = Impl;
|
||||
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Impl>,
|
||||
{
|
||||
use self::SimpleSelector::*;
|
||||
|
||||
if !visitor.visit_simple_selector(self) {
|
||||
return false;
|
||||
}
|
||||
|
||||
match *self {
|
||||
Negation(ref negated) => {
|
||||
for selector in negated {
|
||||
if !selector.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
AttrExists(ref selector) |
|
||||
AttrEqual(ref selector, _, _) |
|
||||
AttrIncludes(ref selector, _) |
|
||||
AttrDashMatch(ref selector, _) |
|
||||
AttrPrefixMatch(ref selector, _) |
|
||||
AttrSubstringMatch(ref selector, _) |
|
||||
AttrSuffixMatch(ref selector, _) => {
|
||||
if !visitor.visit_attribute_selector(selector) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
NonTSPseudoClass(ref pseudo_class) => {
|
||||
if !pseudo_class.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,6 +222,30 @@ pub struct ComplexSelector<Impl: SelectorImpl> {
|
|||
pub next: Option<(Arc<ComplexSelector<Impl>>, Combinator)>, // c.next is left of c
|
||||
}
|
||||
|
||||
impl<Impl: SelectorImpl> ComplexSelector<Impl> {
|
||||
/// Visits this selectors and all the ones to the left of it, until a
|
||||
/// visitor method returns `false`.
|
||||
pub fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Impl>,
|
||||
{
|
||||
let mut current = self;
|
||||
loop {
|
||||
for selector in ¤t.compound_selector {
|
||||
if !selector.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
match current.next {
|
||||
Some((ref next, _)) => current = next,
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)]
|
||||
pub enum Combinator {
|
||||
Child, // >
|
||||
|
@ -1023,9 +1030,8 @@ fn parse_functional_pseudo_class<P, Impl>(parser: &P,
|
|||
"not" => {
|
||||
if inside_negation {
|
||||
return Err(())
|
||||
} else {
|
||||
return parse_negation(parser, input)
|
||||
}
|
||||
return parse_negation(parser, input)
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
@ -1127,7 +1133,8 @@ fn parse_simple_pseudo_class<P, Impl>(parser: &P, name: Cow<str>) -> Result<Simp
|
|||
"only-of-type" => Ok(SimpleSelector::OnlyOfType),
|
||||
_ => Err(())
|
||||
}).or_else(|()| {
|
||||
P::parse_non_ts_pseudo_class(parser, name).map(|pc| SimpleSelector::NonTSPseudoClass(pc))
|
||||
P::parse_non_ts_pseudo_class(parser, name)
|
||||
.map(SimpleSelector::NonTSPseudoClass)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1176,8 +1183,10 @@ pub mod tests {
|
|||
}
|
||||
|
||||
impl SelectorMethods for PseudoClass {
|
||||
fn affects_siblings(&self) -> bool { false }
|
||||
fn matches_non_common_style_affecting_attribute(&self) -> bool { false }
|
||||
type Impl = DummySelectorImpl;
|
||||
|
||||
fn visit<V>(&self, visitor: &mut V) -> bool
|
||||
where V: SelectorVisitor<Impl = Self::Impl> { true }
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
|
|
35
components/selectors/visitor.rs
Normal file
35
components/selectors/visitor.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Visitor traits for selectors.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use parser::{AttrSelector, ComplexSelector, SelectorImpl, SimpleSelector};
|
||||
|
||||
/// 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 {
|
||||
/// 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, _: &AttrSelector<Self::Impl>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Visits a complex selector.
|
||||
fn visit_complex_selector(&mut self, _: &ComplexSelector<Self::Impl>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Visits a simple selector.
|
||||
fn visit_simple_selector(&mut self, _: &SimpleSelector<Self::Impl>) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue