mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Hoist specificity computation into a new private builder module.
This patch doesn't modify any of the code because making a few things pub. I did this first to make the next patch easier to audit. MozReview-Commit-ID: 7PYxoS5bVGN
This commit is contained in:
parent
1d242ad760
commit
1fc1d64e80
3 changed files with 144 additions and 136 deletions
141
components/selectors/builder.rs
Normal file
141
components/selectors/builder.rs
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
use parser::{Component, SelectorImpl, SelectorIter};
|
||||||
|
use std::cmp;
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct SpecificityAndFlags(pub u32);
|
||||||
|
|
||||||
|
pub const HAS_PSEUDO_BIT: u32 = 1 << 30;
|
||||||
|
|
||||||
|
impl SpecificityAndFlags {
|
||||||
|
pub fn specificity(&self) -> u32 {
|
||||||
|
self.0 & !HAS_PSEUDO_BIT
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_pseudo_element(&self) -> bool {
|
||||||
|
(self.0 & HAS_PSEUDO_BIT) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_10BIT: u32 = (1u32 << 10) - 1;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
|
struct Specificity {
|
||||||
|
id_selectors: u32,
|
||||||
|
class_like_selectors: u32,
|
||||||
|
element_selectors: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Specificity {
|
||||||
|
type Output = Specificity;
|
||||||
|
|
||||||
|
fn add(self, rhs: Specificity) -> Specificity {
|
||||||
|
Specificity {
|
||||||
|
id_selectors: self.id_selectors + rhs.id_selectors,
|
||||||
|
class_like_selectors:
|
||||||
|
self.class_like_selectors + rhs.class_like_selectors,
|
||||||
|
element_selectors:
|
||||||
|
self.element_selectors + rhs.element_selectors,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Specificity {
|
||||||
|
fn default() -> Specificity {
|
||||||
|
Specificity {
|
||||||
|
id_selectors: 0,
|
||||||
|
class_like_selectors: 0,
|
||||||
|
element_selectors: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u32> for Specificity {
|
||||||
|
fn from(value: u32) -> Specificity {
|
||||||
|
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
|
||||||
|
Specificity {
|
||||||
|
id_selectors: value >> 20,
|
||||||
|
class_like_selectors: (value >> 10) & MAX_10BIT,
|
||||||
|
element_selectors: value & MAX_10BIT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Specificity> for u32 {
|
||||||
|
fn from(specificity: Specificity) -> u32 {
|
||||||
|
cmp::min(specificity.id_selectors, MAX_10BIT) << 20
|
||||||
|
| cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
|
||||||
|
| cmp::min(specificity.element_selectors, MAX_10BIT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn specificity<Impl>(iter: SelectorIter<Impl>) -> u32
|
||||||
|
where Impl: SelectorImpl
|
||||||
|
{
|
||||||
|
complex_selector_specificity(iter).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complex_selector_specificity<Impl>(mut iter: SelectorIter<Impl>)
|
||||||
|
-> Specificity
|
||||||
|
where Impl: SelectorImpl
|
||||||
|
{
|
||||||
|
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
|
||||||
|
specificity: &mut Specificity)
|
||||||
|
where Impl: SelectorImpl
|
||||||
|
{
|
||||||
|
match *simple_selector {
|
||||||
|
Component::Combinator(..) => unreachable!(),
|
||||||
|
Component::PseudoElement(..) |
|
||||||
|
Component::LocalName(..) => {
|
||||||
|
specificity.element_selectors += 1
|
||||||
|
}
|
||||||
|
Component::ID(..) => {
|
||||||
|
specificity.id_selectors += 1
|
||||||
|
}
|
||||||
|
Component::Class(..) |
|
||||||
|
Component::AttributeInNoNamespace { .. } |
|
||||||
|
Component::AttributeInNoNamespaceExists { .. } |
|
||||||
|
Component::AttributeOther(..) |
|
||||||
|
|
||||||
|
Component::FirstChild | Component::LastChild |
|
||||||
|
Component::OnlyChild | Component::Root |
|
||||||
|
Component::Empty |
|
||||||
|
Component::NthChild(..) |
|
||||||
|
Component::NthLastChild(..) |
|
||||||
|
Component::NthOfType(..) |
|
||||||
|
Component::NthLastOfType(..) |
|
||||||
|
Component::FirstOfType | Component::LastOfType |
|
||||||
|
Component::OnlyOfType |
|
||||||
|
Component::NonTSPseudoClass(..) => {
|
||||||
|
specificity.class_like_selectors += 1
|
||||||
|
}
|
||||||
|
Component::ExplicitUniversalType |
|
||||||
|
Component::ExplicitAnyNamespace |
|
||||||
|
Component::ExplicitNoNamespace |
|
||||||
|
Component::DefaultNamespace(..) |
|
||||||
|
Component::Namespace(..) => {
|
||||||
|
// Does not affect specificity
|
||||||
|
}
|
||||||
|
Component::Negation(ref negated) => {
|
||||||
|
for ss in negated.iter() {
|
||||||
|
simple_selector_specificity(&ss, specificity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut specificity = Default::default();
|
||||||
|
loop {
|
||||||
|
for simple_selector in &mut iter {
|
||||||
|
simple_selector_specificity(&simple_selector, &mut specificity);
|
||||||
|
}
|
||||||
|
if iter.next_sequence().is_none() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
specificity
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ extern crate smallvec;
|
||||||
|
|
||||||
pub mod attr;
|
pub mod attr;
|
||||||
pub mod bloom;
|
pub mod bloom;
|
||||||
|
mod builder;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod matching;
|
pub mod matching;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
use attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator};
|
use attr::{AttrSelectorWithNamespace, ParsedAttrSelectorOperation, AttrSelectorOperator};
|
||||||
use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint};
|
use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE, NamespaceConstraint};
|
||||||
use bloom::BLOOM_HASH_MASK;
|
use bloom::BLOOM_HASH_MASK;
|
||||||
|
use builder::{HAS_PSEUDO_BIT, SpecificityAndFlags};
|
||||||
|
use builder::specificity;
|
||||||
use cssparser::{ParseError, BasicParseError, CompactCowStr};
|
use cssparser::{ParseError, BasicParseError, CompactCowStr};
|
||||||
use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
|
use cssparser::{Token, Parser as CssParser, parse_nth, ToCss, serialize_identifier, CssStringWriter};
|
||||||
use precomputed_hash::PrecomputedHash;
|
use precomputed_hash::PrecomputedHash;
|
||||||
|
@ -13,10 +15,8 @@ use sink::Push;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::{Borrow, Cow};
|
||||||
use std::cmp;
|
|
||||||
use std::fmt::{self, Display, Debug, Write};
|
use std::fmt::{self, Display, Debug, Write};
|
||||||
use std::iter::Rev;
|
use std::iter::Rev;
|
||||||
use std::ops::Add;
|
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use visitor::SelectorVisitor;
|
use visitor::SelectorVisitor;
|
||||||
|
|
||||||
|
@ -266,8 +266,6 @@ impl AncestorHashes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const HAS_PSEUDO_BIT: u32 = 1 << 30;
|
|
||||||
|
|
||||||
pub trait SelectorMethods {
|
pub trait SelectorMethods {
|
||||||
type Impl: SelectorImpl;
|
type Impl: SelectorImpl;
|
||||||
|
|
||||||
|
@ -370,19 +368,6 @@ pub fn namespace_empty_string<Impl: SelectorImpl>() -> Impl::NamespaceUrl {
|
||||||
Impl::NamespaceUrl::default()
|
Impl::NamespaceUrl::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
|
||||||
struct SpecificityAndFlags(u32);
|
|
||||||
|
|
||||||
impl SpecificityAndFlags {
|
|
||||||
fn specificity(&self) -> u32 {
|
|
||||||
self.0 & !HAS_PSEUDO_BIT
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_pseudo_element(&self) -> bool {
|
|
||||||
(self.0 & HAS_PSEUDO_BIT) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Selector stores a sequence of simple selectors and combinators. The
|
/// A Selector stores a sequence of simple selectors and combinators. The
|
||||||
/// iterator classes allow callers to iterate at either the raw sequence level or
|
/// iterator classes allow callers to iterate at either the raw sequence level or
|
||||||
/// at the level of sequences of simple selectors separated by combinators. Most
|
/// at the level of sequences of simple selectors separated by combinators. Most
|
||||||
|
@ -920,125 +905,6 @@ fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) ->
|
||||||
serialize_identifier(&string, dest)
|
serialize_identifier(&string, dest)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_10BIT: u32 = (1u32 << 10) - 1;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
|
|
||||||
struct Specificity {
|
|
||||||
id_selectors: u32,
|
|
||||||
class_like_selectors: u32,
|
|
||||||
element_selectors: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Specificity {
|
|
||||||
type Output = Specificity;
|
|
||||||
|
|
||||||
fn add(self, rhs: Specificity) -> Specificity {
|
|
||||||
Specificity {
|
|
||||||
id_selectors: self.id_selectors + rhs.id_selectors,
|
|
||||||
class_like_selectors:
|
|
||||||
self.class_like_selectors + rhs.class_like_selectors,
|
|
||||||
element_selectors:
|
|
||||||
self.element_selectors + rhs.element_selectors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Specificity {
|
|
||||||
fn default() -> Specificity {
|
|
||||||
Specificity {
|
|
||||||
id_selectors: 0,
|
|
||||||
class_like_selectors: 0,
|
|
||||||
element_selectors: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u32> for Specificity {
|
|
||||||
fn from(value: u32) -> Specificity {
|
|
||||||
assert!(value <= MAX_10BIT << 20 | MAX_10BIT << 10 | MAX_10BIT);
|
|
||||||
Specificity {
|
|
||||||
id_selectors: value >> 20,
|
|
||||||
class_like_selectors: (value >> 10) & MAX_10BIT,
|
|
||||||
element_selectors: value & MAX_10BIT,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Specificity> for u32 {
|
|
||||||
fn from(specificity: Specificity) -> u32 {
|
|
||||||
cmp::min(specificity.id_selectors, MAX_10BIT) << 20
|
|
||||||
| cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10
|
|
||||||
| cmp::min(specificity.element_selectors, MAX_10BIT)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn specificity<Impl>(iter: SelectorIter<Impl>) -> u32
|
|
||||||
where Impl: SelectorImpl
|
|
||||||
{
|
|
||||||
complex_selector_specificity(iter).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complex_selector_specificity<Impl>(mut iter: SelectorIter<Impl>)
|
|
||||||
-> Specificity
|
|
||||||
where Impl: SelectorImpl
|
|
||||||
{
|
|
||||||
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
|
|
||||||
specificity: &mut Specificity)
|
|
||||||
where Impl: SelectorImpl
|
|
||||||
{
|
|
||||||
match *simple_selector {
|
|
||||||
Component::Combinator(..) => unreachable!(),
|
|
||||||
Component::PseudoElement(..) |
|
|
||||||
Component::LocalName(..) => {
|
|
||||||
specificity.element_selectors += 1
|
|
||||||
}
|
|
||||||
Component::ID(..) => {
|
|
||||||
specificity.id_selectors += 1
|
|
||||||
}
|
|
||||||
Component::Class(..) |
|
|
||||||
Component::AttributeInNoNamespace { .. } |
|
|
||||||
Component::AttributeInNoNamespaceExists { .. } |
|
|
||||||
Component::AttributeOther(..) |
|
|
||||||
|
|
||||||
Component::FirstChild | Component::LastChild |
|
|
||||||
Component::OnlyChild | Component::Root |
|
|
||||||
Component::Empty |
|
|
||||||
Component::NthChild(..) |
|
|
||||||
Component::NthLastChild(..) |
|
|
||||||
Component::NthOfType(..) |
|
|
||||||
Component::NthLastOfType(..) |
|
|
||||||
Component::FirstOfType | Component::LastOfType |
|
|
||||||
Component::OnlyOfType |
|
|
||||||
Component::NonTSPseudoClass(..) => {
|
|
||||||
specificity.class_like_selectors += 1
|
|
||||||
}
|
|
||||||
Component::ExplicitUniversalType |
|
|
||||||
Component::ExplicitAnyNamespace |
|
|
||||||
Component::ExplicitNoNamespace |
|
|
||||||
Component::DefaultNamespace(..) |
|
|
||||||
Component::Namespace(..) => {
|
|
||||||
// Does not affect specificity
|
|
||||||
}
|
|
||||||
Component::Negation(ref negated) => {
|
|
||||||
for ss in negated.iter() {
|
|
||||||
simple_selector_specificity(&ss, specificity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut specificity = Default::default();
|
|
||||||
loop {
|
|
||||||
for simple_selector in &mut iter {
|
|
||||||
simple_selector_specificity(&simple_selector, &mut specificity);
|
|
||||||
}
|
|
||||||
if iter.next_sequence().is_none() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
specificity
|
|
||||||
}
|
|
||||||
|
|
||||||
/// We make this large because the result of parsing a selector is fed into a new
|
/// We make this large because the result of parsing a selector is fed into a new
|
||||||
/// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
|
/// Arc-ed allocation, so any spilled vec would be a wasted allocation. Also,
|
||||||
/// Components are large enough that we don't have much cache locality benefit
|
/// Components are large enough that we don't have much cache locality benefit
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue