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:
Emilio Cobos Álvarez 2017-04-07 01:37:55 +02:00
parent 63988b9103
commit 1748150497
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
4 changed files with 154 additions and 120 deletions

View file

@ -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;

View file

@ -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(..) |

View file

@ -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 &current.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 &current.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)]

View 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
}
}