servo: Merge #16900 - Bug 1364850: Move PseudoElement to be just another combinator in selectors. r=bholley (from emilio:pseudos); r=bholley

On top of https://github.com/servo/servo/pull/16890.

Source-Repo: https://github.com/servo/servo
Source-Revision: d8b7013fcddff79a9c879077e1a564d83201359c

--HG--
extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear
extra : subtree_revision : 12c4ec9e73c7d9b747303a5629b20719db05f03a
This commit is contained in:
Emilio Cobos Álvarez 2017-05-17 05:40:14 -05:00
Родитель 4b561945c6
Коммит b9a37058c8
19 изменённых файлов: 675 добавлений и 541 удалений

Просмотреть файл

@ -86,7 +86,7 @@ use net_traits::request::CorsSettings;
use ref_filter_map::ref_filter_map; use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType; use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable; use script_thread::Runnable;
use selectors::matching::{ElementSelectorFlags, MatchingContext, matches_selector_list}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, matches_selector_list};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::parser::{AttrSelector, NamespaceConstraint}; use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom; use servo_atoms::Atom;
@ -104,7 +104,7 @@ use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBloc
use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x}; use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x};
use style::restyle_hints::RESTYLE_SELF; use style::restyle_hints::RESTYLE_SELF;
use style::rule_tree::CascadeLevel; use style::rule_tree::CascadeLevel;
use style::selector_parser::{NonTSPseudoClass, RestyleDamage, SelectorImpl, SelectorParser}; use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
use style::shared_lock::{SharedRwLock, Locked}; use style::shared_lock::{SharedRwLock, Locked};
use style::sink::Push; use style::sink::Push;
use style::stylearc::Arc; use style::stylearc::Arc;
@ -2058,7 +2058,8 @@ impl ElementMethods for Element {
match SelectorParser::parse_author_origin_no_namespace(&selectors) { match SelectorParser::parse_author_origin_no_namespace(&selectors) {
Err(()) => Err(Error::Syntax), Err(()) => Err(Error::Syntax),
Ok(selectors) => { Ok(selectors) => {
Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), None)) let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
Ok(matches_selector_list(&selectors.0, &Root::from_ref(self), &mut ctx))
} }
} }
} }
@ -2076,8 +2077,8 @@ impl ElementMethods for Element {
let root = self.upcast::<Node>(); let root = self.upcast::<Node>();
for element in root.inclusive_ancestors() { for element in root.inclusive_ancestors() {
if let Some(element) = Root::downcast::<Element>(element) { if let Some(element) = Root::downcast::<Element>(element) {
if matches_selector_list(&selectors.0, &element, None) let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
{ if matches_selector_list(&selectors.0, &element, &mut ctx) {
return Ok(Some(element)); return Ok(Some(element));
} }
} }
@ -2386,6 +2387,15 @@ impl<'a> ::selectors::Element for Root<Element> {
self.upcast::<Node>().GetParentElement() self.upcast::<Node>().GetParentElement()
} }
fn match_pseudo_element(&self,
_pseudo: &PseudoElement,
_context: &mut MatchingContext)
-> bool
{
false
}
fn first_child_element(&self) -> Option<Root<Element>> { fn first_child_element(&self) -> Option<Root<Element>> {
self.node.child_elements().next() self.node.child_elements().next()
} }

Просмотреть файл

@ -70,7 +70,7 @@ use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddr
use script_layout_interface::message::Msg; use script_layout_interface::message::Msg;
use script_traits::DocumentActivity; use script_traits::DocumentActivity;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
use selectors::matching::matches_selector_list; use selectors::matching::{matches_selector_list, MatchingContext, MatchingMode};
use selectors::parser::SelectorList; use selectors::parser::SelectorList;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::borrow::ToOwned; use std::borrow::ToOwned;
@ -346,11 +346,14 @@ impl<'a> Iterator for QuerySelectorIterator {
fn next(&mut self) -> Option<Root<Node>> { fn next(&mut self) -> Option<Root<Node>> {
let selectors = &self.selectors.0; let selectors = &self.selectors.0;
// TODO(cgaebel): Is it worth it to build a bloom filter here // TODO(cgaebel): Is it worth it to build a bloom filter here
// (instead of passing `None`)? Probably. // (instead of passing `None`)? Probably.
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
self.iterator.by_ref().filter_map(|node| { self.iterator.by_ref().filter_map(|node| {
if let Some(element) = Root::downcast(node) { if let Some(element) = Root::downcast(node) {
if matches_selector_list(selectors, &element, None) { if matches_selector_list(selectors, &element, &mut ctx) {
return Some(Root::upcast(element)); return Some(Root::upcast(element));
} }
} }
@ -717,8 +720,9 @@ impl Node {
Err(()) => Err(Error::Syntax), Err(()) => Err(Error::Syntax),
// Step 3. // Step 3.
Ok(selectors) => { Ok(selectors) => {
let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| { Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
matches_selector_list(&selectors.0, element, None) matches_selector_list(&selectors.0, element, &mut ctx)
})) }))
} }
} }

Просмотреть файл

@ -652,6 +652,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
self.element.namespace() self.element.namespace()
} }
fn match_pseudo_element(&self,
_pseudo: &PseudoElement,
_context: &mut MatchingContext)
-> bool
{
false
}
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
_: &mut MatchingContext, _: &mut MatchingContext,
@ -1150,6 +1158,14 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
self.element.get_namespace() self.element.get_namespace()
} }
fn match_pseudo_element(&self,
_pseudo: &PseudoElement,
_context: &mut MatchingContext)
-> bool
{
false
}
fn match_non_ts_pseudo_class<F>(&self, fn match_non_ts_pseudo_class<F>(&self,
_: &NonTSPseudoClass, _: &NonTSPseudoClass,
_: &mut MatchingContext, _: &mut MatchingContext,

Просмотреть файл

@ -20,7 +20,6 @@ use style::context::SharedStyleContext;
use style::data::ElementData; use style::data::ElementData;
use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode}; use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode};
use style::dom::OpaqueNode; use style::dom::OpaqueNode;
use style::element_state::ElementState;
use style::font_metrics::ServoMetricsProvider; use style::font_metrics::ServoMetricsProvider;
use style::properties::{CascadeFlags, ServoComputedValues}; use style::properties::{CascadeFlags, ServoComputedValues};
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl}; use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
@ -435,7 +434,6 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
&context.guards, &context.guards,
unsafe { &self.unsafe_get() }, unsafe { &self.unsafe_get() },
&style_pseudo, &style_pseudo,
ElementState::empty(),
data.styles().primary.values(), data.styles().primary.values(),
&ServoMetricsProvider); &ServoMetricsProvider);
data.styles_mut().cached_pseudos data.styles_mut().cached_pseudos

Просмотреть файл

@ -19,9 +19,6 @@ pub enum PseudoElement {
B, B,
} }
#[derive(Eq, PartialEq, Clone, Debug)]
pub struct PseudoElementSelector(PseudoElement, u64);
#[derive(Eq, PartialEq, Clone, Debug, Default)] #[derive(Eq, PartialEq, Clone, Debug, Default)]
pub struct Atom(usize); pub struct Atom(usize);

Просмотреть файл

@ -69,28 +69,67 @@ impl ElementSelectorFlags {
} }
} }
/// What kind of selector matching mode we should use.
///
/// There are two modes of selector matching. The difference is only noticeable
/// in presence of pseudo-elements.
#[derive(Debug, PartialEq)]
pub enum MatchingMode {
/// Don't ignore any pseudo-element selectors.
Normal,
/// Ignores any stateless pseudo-element selectors in the rightmost sequence
/// of simple selectors.
///
/// This is useful, for example, to match against ::before when you aren't a
/// pseudo-element yourself.
///
/// For example, in presence of `::before:hover`, it would never match, but
/// `::before` would be ignored as in "matching".
///
/// It's required for all the selectors you match using this mode to have a
/// pseudo-element.
ForStatelessPseudoElement,
}
/// Data associated with the matching process for a element. This context is /// Data associated with the matching process for a element. This context is
/// used across many selectors for an element, so it's not appropriate for /// used across many selectors for an element, so it's not appropriate for
/// transient data that applies to only a single selector. /// transient data that applies to only a single selector.
#[derive(Default)] pub struct MatchingContext<'a> {
pub struct MatchingContext {
/// Output that records certains relations between elements noticed during /// Output that records certains relations between elements noticed during
/// matching (and also extended after matching). /// matching (and also extended after matching).
pub relations: StyleRelations, pub relations: StyleRelations,
/// The matching mode we should use when matching selectors.
pub matching_mode: MatchingMode,
/// The bloom filter used to fast-reject selectors.
pub bloom_filter: Option<&'a BloomFilter>,
}
impl<'a> MatchingContext<'a> {
/// Constructs a new `MatchingContext`.
pub fn new(matching_mode: MatchingMode,
bloom_filter: Option<&'a BloomFilter>)
-> Self
{
Self {
relations: StyleRelations::empty(),
matching_mode: matching_mode,
bloom_filter: bloom_filter,
}
}
} }
pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>], pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>],
element: &E, element: &E,
parent_bf: Option<&BloomFilter>) context: &mut MatchingContext)
-> bool -> bool
where E: Element where E: Element
{ {
selector_list.iter().any(|selector| { selector_list.iter().any(|selector| {
selector.pseudo_element.is_none() &&
matches_selector(&selector.inner, matches_selector(&selector.inner,
element, element,
parent_bf, context,
&mut MatchingContext::default(),
&mut |_, _| {}) &mut |_, _| {})
}) })
} }
@ -115,27 +154,6 @@ fn may_match<E>(sel: &SelectorInner<E::Impl>,
true true
} }
/// Determines whether the given element matches the given complex selector.
pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
// Use the bloom filter to fast-reject.
if let Some(filter) = parent_bf {
if !may_match::<E>(selector, filter) {
return false;
}
}
// Match the selector.
matches_complex_selector(&selector.complex, element, context, flags_setter)
}
/// A result of selector matching, includes 3 failure types, /// A result of selector matching, includes 3 failure types,
/// ///
/// NotMatchedAndRestartFromClosestLaterSibling /// NotMatchedAndRestartFromClosestLaterSibling
@ -186,16 +204,65 @@ enum SelectorMatchingResult {
NotMatchedGlobally, NotMatchedGlobally,
} }
/// Matches an inner selector.
pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
element: &E,
context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
// Use the bloom filter to fast-reject.
if let Some(filter) = context.bloom_filter {
if !may_match::<E>(&selector, filter) {
return false;
}
}
matches_complex_selector(&selector.complex, element, context, flags_setter)
}
/// Matches a complex selector. /// Matches a complex selector.
pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>, ///
/// Use `matches_selector` if you need to skip pseudos.
pub fn matches_complex_selector<E, F>(complex_selector: &ComplexSelector<E::Impl>,
element: &E, element: &E,
context: &mut MatchingContext, context: &mut MatchingContext,
flags_setter: &mut F) flags_setter: &mut F)
-> bool -> bool
where E: Element, where E: Element,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
match matches_complex_selector_internal(selector.iter(), let mut iter = complex_selector.iter();
if cfg!(debug_assertions) {
if context.matching_mode == MatchingMode::ForStatelessPseudoElement {
assert!(complex_selector.iter().any(|c| {
matches!(*c, Component::PseudoElement(..))
}));
}
}
if context.matching_mode == MatchingMode::ForStatelessPseudoElement {
match *iter.next().unwrap() {
// Stateful pseudo, just don't match.
Component::NonTSPseudoClass(..) => return false,
Component::PseudoElement(..) => {
// Pseudo, just eat the whole sequence.
let next = iter.next();
debug_assert!(next.is_none(),
"Someone messed up pseudo-element parsing?");
if iter.next_sequence().is_none() {
return true;
}
}
_ => panic!("Used MatchingMode::ForStatelessPseudoElement in a non-pseudo selector"),
}
}
match matches_complex_selector_internal(iter,
element, element,
context, context,
flags_setter) { flags_setter) {
@ -229,12 +296,19 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
match combinator { match combinator {
None => SelectorMatchingResult::Matched, None => SelectorMatchingResult::Matched,
Some(c) => { Some(c) => {
let (mut next_element, candidate_not_found) = if siblings { let (mut next_element, candidate_not_found) = match c {
(element.prev_sibling_element(), Combinator::NextSibling | Combinator::LaterSibling => {
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant) (element.prev_sibling_element(),
} else { SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
(element.parent_element(), }
SelectorMatchingResult::NotMatchedGlobally) Combinator::Child | Combinator::Descendant => {
(element.parent_element(),
SelectorMatchingResult::NotMatchedGlobally)
}
Combinator::PseudoElement => {
(element.pseudo_element_originating_element(),
SelectorMatchingResult::NotMatchedGlobally)
}
}; };
loop { loop {
@ -253,6 +327,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
// Upgrade the failure status to // Upgrade the failure status to
// NotMatchedAndRestartFromClosestDescendant. // NotMatchedAndRestartFromClosestDescendant.
(_, Combinator::PseudoElement) |
(_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, (_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
// Return the status directly. // Return the status directly.
@ -306,6 +381,9 @@ fn matches_simple_selector<E, F>(
match *selector { match *selector {
Component::Combinator(_) => unreachable!(), Component::Combinator(_) => unreachable!(),
Component::PseudoElement(ref pseudo) => {
element.match_pseudo_element(pseudo, context)
}
Component::LocalName(LocalName { ref name, ref lower_name }) => { Component::LocalName(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { name }; let name = if element.is_html_element_in_html_document() { lower_name } else { name };
element.get_local_name() == name.borrow() element.get_local_name() == name.borrow()

Просмотреть файл

@ -16,6 +16,22 @@ use std::slice;
use tree::SELECTOR_WHITESPACE; use tree::SELECTOR_WHITESPACE;
use visitor::SelectorVisitor; use visitor::SelectorVisitor;
/// A trait that represents a pseudo-element.
pub trait PseudoElement : Sized + ToCss {
/// The `SelectorImpl` this pseudo-element is used for.
type Impl: SelectorImpl;
/// Whether the pseudo-element supports a given state selector to the right
/// of it.
fn supports_pseudo_class(
&self,
_pseudo_class: &<Self::Impl as SelectorImpl>::NonTSPseudoClass)
-> bool
{
false
}
}
macro_rules! with_all_bounds { macro_rules! with_all_bounds {
( (
[ $( $InSelector: tt )* ] [ $( $InSelector: tt )* ]
@ -60,7 +76,7 @@ macro_rules! with_all_bounds {
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>; type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>;
/// pseudo-elements /// pseudo-elements
type PseudoElementSelector: $($CommonBounds)* + Sized + ToCss; type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;
} }
} }
} }
@ -97,8 +113,8 @@ pub trait Parser {
Err(()) Err(())
} }
fn parse_pseudo_element(&self, _name: Cow<str>, _input: &mut CssParser) fn parse_pseudo_element(&self, _name: Cow<str>)
-> Result<<Self::Impl as SelectorImpl>::PseudoElementSelector, ()> { -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ()> {
Err(()) Err(())
} }
@ -178,18 +194,57 @@ impl<Impl: SelectorImpl> SelectorInner<Impl> {
#[derive(PartialEq, Eq, Clone)] #[derive(PartialEq, Eq, Clone)]
pub struct Selector<Impl: SelectorImpl> { pub struct Selector<Impl: SelectorImpl> {
pub inner: SelectorInner<Impl>, pub inner: SelectorInner<Impl>,
pub pseudo_element: Option<Impl::PseudoElementSelector>, specificity_and_flags: u32,
pub specificity: u32,
} }
impl<Impl: SelectorImpl> ::std::borrow::Borrow<SelectorInner<Impl>> for Selector<Impl> {
fn borrow(&self) -> &SelectorInner<Impl> {
&self.inner
}
}
const HAS_PSEUDO_BIT: u32 = 1 << 30;
impl<Impl: SelectorImpl> Selector<Impl> { impl<Impl: SelectorImpl> Selector<Impl> {
pub fn specificity(&self) -> u32 {
self.specificity_and_flags & !HAS_PSEUDO_BIT
}
pub fn new_for_unit_testing(inner: SelectorInner<Impl>, specificity: u32) -> Self {
Self {
inner: inner,
specificity_and_flags: specificity,
}
}
pub fn pseudo_element(&self) -> Option<&Impl::PseudoElement> {
if !self.has_pseudo_element() {
return None
}
for component in self.inner.complex.iter() {
if let Component::PseudoElement(ref pseudo) = *component {
return Some(pseudo)
}
}
debug_assert!(false, "has_pseudo_element lied!");
None
}
pub fn has_pseudo_element(&self) -> bool {
(self.specificity_and_flags & HAS_PSEUDO_BIT) != 0
}
/// Whether this selector (pseudo-element part excluded) matches every element. /// Whether this selector (pseudo-element part excluded) matches every element.
/// ///
/// Used for "pre-computed" pseudo-elements in components/style/stylist.rs /// Used for "pre-computed" pseudo-elements in components/style/stylist.rs
pub fn is_universal(&self) -> bool { pub fn is_universal(&self) -> bool {
self.inner.complex.iter_raw().all(|c| matches!(*c, self.inner.complex.iter_raw().all(|c| matches!(*c,
Component::ExplicitUniversalType | Component::ExplicitUniversalType |
Component::ExplicitAnyNamespace Component::ExplicitAnyNamespace |
Component::Combinator(Combinator::PseudoElement) |
Component::PseudoElement(..)
)) ))
} }
} }
@ -361,7 +416,8 @@ impl<'a, Impl: 'a + SelectorImpl> SelectorIter<'a, Impl> {
impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> { impl<'a, Impl: SelectorImpl> Iterator for SelectorIter<'a, Impl> {
type Item = &'a Component<Impl>; type Item = &'a Component<Impl>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
debug_assert!(self.next_combinator.is_none(), "Should call take_combinator!"); debug_assert!(self.next_combinator.is_none(),
"You should call next_sequence!");
match self.iter.next() { match self.iter.next() {
None => None, None => None,
Some(&Component::Combinator(c)) => { Some(&Component::Combinator(c)) => {
@ -384,12 +440,12 @@ impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> {
result result
} }
/// Skips a sequence of simple selectors and all subsequent sequences until an /// Skips a sequence of simple selectors and all subsequent sequences until
/// ancestor combinator is reached. /// a non-pseudo-element ancestor combinator is reached.
fn skip_until_ancestor(&mut self) { fn skip_until_ancestor(&mut self) {
loop { loop {
while let Some(_) = self.0.next() {} while self.0.next().is_some() {}
if self.0.next_sequence().map_or(true, |x| x.is_ancestor()) { if self.0.next_sequence().map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant)) {
break; break;
} }
} }
@ -407,7 +463,7 @@ impl<'a, Impl: SelectorImpl> Iterator for AncestorIter<'a, Impl> {
// See if there are more sequences. If so, skip any non-ancestor sequences. // See if there are more sequences. If so, skip any non-ancestor sequences.
if let Some(combinator) = self.0.next_sequence() { if let Some(combinator) = self.0.next_sequence() {
if !combinator.is_ancestor() { if !matches!(combinator, Combinator::Child | Combinator::Descendant) {
self.skip_until_ancestor(); self.skip_until_ancestor();
} }
} }
@ -422,12 +478,24 @@ pub enum Combinator {
Descendant, // space Descendant, // space
NextSibling, // + NextSibling, // +
LaterSibling, // ~ LaterSibling, // ~
/// A dummy combinator we use to the left of pseudo-elements.
///
/// It serializes as the empty string, and acts effectively as a child
/// combinator.
PseudoElement,
} }
impl Combinator { impl Combinator {
/// Returns true if this combinator is a child or descendant combinator. /// Returns true if this combinator is a child or descendant combinator.
pub fn is_ancestor(&self) -> bool { pub fn is_ancestor(&self) -> bool {
matches!(*self, Combinator::Child | Combinator::Descendant) matches!(*self, Combinator::Child |
Combinator::Descendant |
Combinator::PseudoElement)
}
/// Returns true if this combinator is a pseudo-element combinator.
pub fn is_pseudo_element(&self) -> bool {
matches!(*self, Combinator::PseudoElement)
} }
/// Returns true if this combinator is a next- or later-sibling combinator. /// Returns true if this combinator is a next- or later-sibling combinator.
@ -491,6 +559,7 @@ pub enum Component<Impl: SelectorImpl> {
LastOfType, LastOfType,
OnlyOfType, OnlyOfType,
NonTSPseudoClass(Impl::NonTSPseudoClass), NonTSPseudoClass(Impl::NonTSPseudoClass),
PseudoElement(Impl::PseudoElement),
// ... // ...
} }
@ -582,7 +651,7 @@ impl<Impl: SelectorImpl> Debug for Selector<Impl> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Selector(")?; f.write_str("Selector(")?;
self.to_css(f)?; self.to_css(f)?;
write!(f, ", specificity = 0x{:x})", self.specificity) write!(f, ", specificity = 0x{:x})", self.specificity())
} }
} }
@ -621,11 +690,7 @@ impl<Impl: SelectorImpl> ToCss for SelectorList<Impl> {
impl<Impl: SelectorImpl> ToCss for Selector<Impl> { impl<Impl: SelectorImpl> ToCss for Selector<Impl> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
self.inner.complex.to_css(dest)?; self.inner.complex.to_css(dest)
if let Some(ref pseudo) = self.pseudo_element {
pseudo.to_css(dest)?;
}
Ok(())
} }
} }
@ -646,6 +711,7 @@ impl ToCss for Combinator {
Combinator::Descendant => dest.write_str(" "), Combinator::Descendant => dest.write_str(" "),
Combinator::NextSibling => dest.write_str(" + "), Combinator::NextSibling => dest.write_str(" + "),
Combinator::LaterSibling => dest.write_str(" ~ "), Combinator::LaterSibling => dest.write_str(" ~ "),
Combinator::PseudoElement => Ok(()),
} }
} }
} }
@ -657,6 +723,9 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
Combinator(ref c) => { Combinator(ref c) => {
c.to_css(dest) c.to_css(dest)
} }
PseudoElement(ref p) => {
p.to_css(dest)
}
ID(ref s) => { ID(ref s) => {
dest.write_char('#')?; dest.write_char('#')?;
display_to_css_identifier(s, dest) display_to_css_identifier(s, dest)
@ -841,25 +910,23 @@ impl From<Specificity> for u32 {
} }
} }
fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>, fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>) -> u32
pseudo_element: Option<&Impl::PseudoElementSelector>) where Impl: SelectorImpl
-> u32 {
where Impl: SelectorImpl { complex_selector_specificity(complex_selector).into()
let mut specificity = complex_selector_specificity(complex_selector);
if pseudo_element.is_some() {
specificity.element_selectors += 1;
}
specificity.into()
} }
fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>) fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
-> Specificity -> Specificity
where Impl: SelectorImpl { where Impl: SelectorImpl
{
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>, fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
specificity: &mut Specificity) specificity: &mut Specificity)
where Impl: SelectorImpl { where Impl: SelectorImpl
{
match *simple_selector { match *simple_selector {
Component::Combinator(..) => unreachable!(), Component::Combinator(..) => unreachable!(),
Component::PseudoElement(..) |
Component::LocalName(..) => { Component::LocalName(..) => {
specificity.element_selectors += 1 specificity.element_selectors += 1
} }
@ -928,12 +995,14 @@ fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector<Impl>, ()> fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector<Impl>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
let (complex, pseudo_element) = let (complex, has_pseudo_element) = parse_complex_selector(parser, input)?;
parse_complex_selector_and_pseudo_element(parser, input)?; let mut specificity = specificity(&complex);
if has_pseudo_element {
specificity |= HAS_PSEUDO_BIT;
}
Ok(Selector { Ok(Selector {
specificity: specificity(&complex, pseudo_element.as_ref()), specificity_and_flags: specificity,
inner: SelectorInner::new(complex), inner: SelectorInner::new(complex),
pseudo_element: pseudo_element,
}) })
} }
@ -947,19 +1016,25 @@ fn parse_selector<P, Impl>(parser: &P, input: &mut CssParser) -> Result<Selector
/// If we parse N > 8 entries, we save two reallocations. /// If we parse N > 8 entries, we save two reallocations.
type ParseVec<Impl> = SmallVec<[Component<Impl>; 8]>; type ParseVec<Impl> = SmallVec<[Component<Impl>; 8]>;
fn parse_complex_selector_and_pseudo_element<P, Impl>( /// Parses a complex selector, including any pseudo-element.
///
/// For now, it always forces the pseudo-element to be at the end of the
/// selector, and the boolean represents whether the last thing parsed was a
/// pseudo-element.
fn parse_complex_selector<P, Impl>(
parser: &P, parser: &P,
input: &mut CssParser) input: &mut CssParser)
-> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElementSelector>), ()> -> Result<(ComplexSelector<Impl>, bool), ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
let mut sequence = ParseVec::new(); let mut sequence = ParseVec::new();
let mut pseudo_element; let mut parsed_pseudo_element;
'outer_loop: loop { 'outer_loop: loop {
// Parse a sequence of simple selectors. // Parse a sequence of simple selectors.
pseudo_element = parse_compound_selector(parser, input, &mut sequence, parsed_pseudo_element =
/* inside_negation = */ false)?; parse_compound_selector(parser, input, &mut sequence,
if pseudo_element.is_some() { /* inside_negation = */ false)?;
if parsed_pseudo_element {
break; break;
} }
@ -998,17 +1073,17 @@ fn parse_complex_selector_and_pseudo_element<P, Impl>(
} }
let complex = ComplexSelector(ArcSlice::new(sequence.into_vec().into_boxed_slice())); let complex = ComplexSelector(ArcSlice::new(sequence.into_vec().into_boxed_slice()));
Ok((complex, pseudo_element)) Ok((complex, parsed_pseudo_element))
} }
impl<Impl: SelectorImpl> ComplexSelector<Impl> { impl<Impl: SelectorImpl> ComplexSelector<Impl> {
/// Parse a complex selector. /// Parse a complex selector, without any pseudo-element.
pub fn parse<P>(parser: &P, input: &mut CssParser) -> Result<Self, ()> pub fn parse<P>(parser: &P, input: &mut CssParser) -> Result<Self, ()>
where P: Parser<Impl=Impl> where P: Parser<Impl=Impl>
{ {
let (complex, pseudo_element) = let (complex, has_pseudo_element) =
parse_complex_selector_and_pseudo_element(parser, input)?; parse_complex_selector(parser, input)?;
if pseudo_element.is_some() { if has_pseudo_element {
return Err(()) return Err(())
} }
Ok(complex) Ok(complex)
@ -1062,7 +1137,7 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu
#[derive(Debug)] #[derive(Debug)]
enum SimpleSelectorParseResult<Impl: SelectorImpl> { enum SimpleSelectorParseResult<Impl: SelectorImpl> {
SimpleSelector(Component<Impl>), SimpleSelector(Component<Impl>),
PseudoElement(Impl::PseudoElementSelector), PseudoElement(Impl::PseudoElement),
} }
#[derive(Debug)] #[derive(Debug)]
@ -1295,13 +1370,15 @@ fn single_simple_selector<Impl: SelectorImpl>(v: &[Component<Impl>]) -> bool {
/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* /// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]*
/// | [ HASH | class | attrib | pseudo | negation ]+ /// | [ HASH | class | attrib | pseudo | negation ]+
/// ///
/// `Err(())` means invalid selector /// `Err(())` means invalid selector.
///
/// The boolean represent whether a pseudo-element has been parsed.
fn parse_compound_selector<P, Impl>( fn parse_compound_selector<P, Impl>(
parser: &P, parser: &P,
input: &mut CssParser, input: &mut CssParser,
mut sequence: &mut ParseVec<Impl>, mut sequence: &mut ParseVec<Impl>,
inside_negation: bool) inside_negation: bool)
-> Result<Option<Impl::PseudoElementSelector>, ()> -> Result<bool, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl where P: Parser<Impl=Impl>, Impl: SelectorImpl
{ {
// Consume any leading whitespace. // Consume any leading whitespace.
@ -1328,7 +1405,7 @@ fn parse_compound_selector<P, Impl>(
empty = false; empty = false;
} }
let mut pseudo_element = None; let mut pseudo = false;
loop { loop {
match parse_one_simple_selector(parser, input, inside_negation)? { match parse_one_simple_selector(parser, input, inside_negation)? {
None => break, None => break,
@ -1337,7 +1414,41 @@ fn parse_compound_selector<P, Impl>(
empty = false empty = false
} }
Some(SimpleSelectorParseResult::PseudoElement(p)) => { Some(SimpleSelectorParseResult::PseudoElement(p)) => {
pseudo_element = Some(p); // Try to parse state to its right.
let mut state_selectors = ParseVec::new();
loop {
match input.next_including_whitespace() {
Ok(Token::Colon) => {},
Ok(Token::WhiteSpace(_)) | Err(()) => break,
_ => return Err(()),
}
// TODO(emilio): Functional pseudo-classes too?
// We don't need it for now.
let name = match input.next_including_whitespace() {
Ok(Token::Ident(name)) => name,
_ => return Err(()),
};
let pseudo_class =
P::parse_non_ts_pseudo_class(parser, name)?;
if !p.supports_pseudo_class(&pseudo_class) {
return Err(());
}
state_selectors.push(Component::NonTSPseudoClass(pseudo_class));
}
if !sequence.is_empty() {
sequence.push(Component::Combinator(Combinator::PseudoElement));
}
sequence.push(Component::PseudoElement(p));
for state_selector in state_selectors {
sequence.push(state_selector);
}
pseudo = true;
empty = false; empty = false;
break break
} }
@ -1347,7 +1458,7 @@ fn parse_compound_selector<P, Impl>(
// An empty selector is invalid. // An empty selector is invalid.
Err(()) Err(())
} else { } else {
Ok(pseudo_element) Ok(pseudo)
} }
} }
@ -1423,7 +1534,7 @@ fn parse_one_simple_selector<P, Impl>(parser: &P,
name.eq_ignore_ascii_case("after") || name.eq_ignore_ascii_case("after") ||
name.eq_ignore_ascii_case("first-line") || name.eq_ignore_ascii_case("first-line") ||
name.eq_ignore_ascii_case("first-letter") { name.eq_ignore_ascii_case("first-letter") {
let pseudo_element = P::parse_pseudo_element(parser, name, input)?; let pseudo_element = P::parse_pseudo_element(parser, name)?;
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element))) Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element)))
} else { } else {
let pseudo_class = parse_simple_pseudo_class(parser, name)?; let pseudo_class = parse_simple_pseudo_class(parser, name)?;
@ -1439,7 +1550,7 @@ fn parse_one_simple_selector<P, Impl>(parser: &P,
Ok(Token::Colon) => { Ok(Token::Colon) => {
match input.next_including_whitespace() { match input.next_including_whitespace() {
Ok(Token::Ident(name)) => { Ok(Token::Ident(name)) => {
let pseudo = P::parse_pseudo_element(parser, name, input)?; let pseudo = P::parse_pseudo_element(parser, name)?;
Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo))) Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo)))
} }
_ => Err(()) _ => Err(())
@ -1478,6 +1589,7 @@ fn parse_simple_pseudo_class<P, Impl>(parser: &P, name: Cow<str>) -> Result<Comp
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use cssparser::{Parser as CssParser, ToCss, serialize_identifier}; use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
use parser;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
@ -1486,6 +1598,7 @@ pub mod tests {
#[derive(PartialEq, Clone, Debug, Eq)] #[derive(PartialEq, Clone, Debug, Eq)]
pub enum PseudoClass { pub enum PseudoClass {
Hover, Hover,
Active,
Lang(String), Lang(String),
} }
@ -1495,10 +1608,23 @@ pub mod tests {
After, After,
} }
impl parser::PseudoElement for PseudoElement {
type Impl = DummySelectorImpl;
fn supports_pseudo_class(&self, pc: &PseudoClass) -> bool {
match *pc {
PseudoClass::Hover => true,
PseudoClass::Active |
PseudoClass::Lang(..) => false,
}
}
}
impl ToCss for PseudoClass { impl ToCss for PseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self { match *self {
PseudoClass::Hover => dest.write_str(":hover"), PseudoClass::Hover => dest.write_str(":hover"),
PseudoClass::Active => dest.write_str(":active"),
PseudoClass::Lang(ref lang) => { PseudoClass::Lang(ref lang) => {
dest.write_str(":lang(")?; dest.write_str(":lang(")?;
serialize_identifier(lang, dest)?; serialize_identifier(lang, dest)?;
@ -1543,7 +1669,7 @@ pub mod tests {
type BorrowedLocalName = DummyAtom; type BorrowedLocalName = DummyAtom;
type BorrowedNamespaceUrl = DummyAtom; type BorrowedNamespaceUrl = DummyAtom;
type NonTSPseudoClass = PseudoClass; type NonTSPseudoClass = PseudoClass;
type PseudoElementSelector = PseudoElement; type PseudoElement = PseudoElement;
} }
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)] #[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
@ -1580,6 +1706,7 @@ pub mod tests {
-> Result<PseudoClass, ()> { -> Result<PseudoClass, ()> {
match_ignore_ascii_case! { &name, match_ignore_ascii_case! { &name,
"hover" => Ok(PseudoClass::Hover), "hover" => Ok(PseudoClass::Hover),
"active" => Ok(PseudoClass::Active),
_ => Err(()) _ => Err(())
} }
} }
@ -1593,8 +1720,7 @@ pub mod tests {
} }
} }
fn parse_pseudo_element(&self, name: Cow<str>, _input: &mut CssParser) fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
-> Result<PseudoElement, ()> {
match_ignore_ascii_case! { &name, match_ignore_ascii_case! { &name,
"before" => Ok(PseudoElement::Before), "before" => Ok(PseudoElement::Before),
"after" => Ok(PseudoElement::After), "after" => Ok(PseudoElement::After),
@ -1647,11 +1773,9 @@ pub mod tests {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
Component::LocalName(LocalName { Component::LocalName(LocalName {
name: DummyAtom::from("EeÉ"), name: DummyAtom::from("EeÉ"),
lower_name: DummyAtom::from("eeÉ") lower_name: DummyAtom::from("eeÉ") })),
}), ),
)), specificity_and_flags: specificity(0, 0, 1),
pseudo_element: None,
specificity: specificity(0, 0, 1),
})))); }))));
assert_eq!(parse("|e"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("|e"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
@ -1661,8 +1785,7 @@ pub mod tests {
lower_name: DummyAtom::from("e") lower_name: DummyAtom::from("e")
}), }),
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 1),
specificity: specificity(0, 0, 1),
})))); }))));
// https://github.com/servo/servo/issues/16020 // https://github.com/servo/servo/issues/16020
assert_eq!(parse("*|e"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("*|e"), Ok(SelectorList(vec!(Selector {
@ -1673,44 +1796,38 @@ pub mod tests {
lower_name: DummyAtom::from("e") lower_name: DummyAtom::from("e")
}), }),
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 1),
specificity: specificity(0, 0, 1),
})))); }))));
assert_eq!(parse("*"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("*"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse("|*"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("|*"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
Component::ExplicitNoNamespace, Component::ExplicitNoNamespace,
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse("*|*"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("*|*"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
Component::ExplicitAnyNamespace, Component::ExplicitAnyNamespace,
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector { assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec![ inner: SelectorInner::from_vec(vec![
Component::Class(DummyAtom::from("foo")), Component::Class(DummyAtom::from("foo")),
Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned())) Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned()))
]), ]),
pseudo_element: None, specificity_and_flags: specificity(0, 2, 0),
specificity: specificity(0, 2, 0),
})))); }))));
assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::ID(DummyAtom::from("bar")))), inner: SelectorInner::from_vec(vec!(Component::ID(DummyAtom::from("bar")))),
pseudo_element: None, specificity_and_flags: specificity(1, 0, 0),
specificity: specificity(1, 0, 0),
})))); }))));
assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName { inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName {
@ -1718,8 +1835,7 @@ pub mod tests {
lower_name: DummyAtom::from("e") }), lower_name: DummyAtom::from("e") }),
Component::Class(DummyAtom::from("foo")), Component::Class(DummyAtom::from("foo")),
Component::ID(DummyAtom::from("bar")))), Component::ID(DummyAtom::from("bar")))),
pseudo_element: None, specificity_and_flags: specificity(1, 1, 1),
specificity: specificity(1, 1, 1),
})))); }))));
assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
@ -1731,8 +1847,7 @@ pub mod tests {
Component::Combinator(Combinator::Descendant), Component::Combinator(Combinator::Descendant),
Component::ID(DummyAtom::from("bar")), Component::ID(DummyAtom::from("bar")),
)), )),
pseudo_element: None, specificity_and_flags: specificity(1, 1, 1),
specificity: specificity(1, 1, 1),
})))); }))));
// Default namespace does not apply to attribute selectors // Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652 // https://github.com/mozilla/servo/pull/1652
@ -1746,8 +1861,7 @@ pub mod tests {
prefix: None, prefix: None,
url: "".into(), url: "".into(),
}) }))), }) }))),
pseudo_element: None, specificity_and_flags: specificity(0, 1, 0),
specificity: specificity(0, 1, 0),
})))); }))));
assert_eq!(parse_ns("svg|circle", &parser), Err(())); assert_eq!(parse_ns("svg|circle", &parser), Err(()));
parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into())); parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
@ -1760,8 +1874,7 @@ pub mod tests {
lower_name: DummyAtom::from("circle"), lower_name: DummyAtom::from("circle"),
}) })
]), ]),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 1),
specificity: specificity(0, 0, 1),
}]))); }])));
assert_eq!(parse_ns("svg|*", &parser), Ok(SelectorList(vec![Selector { assert_eq!(parse_ns("svg|*", &parser), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
@ -1769,8 +1882,7 @@ pub mod tests {
Component::Namespace(DummyAtom("svg".into()), SVG.into()), Component::Namespace(DummyAtom("svg".into()), SVG.into()),
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
]), ]),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
}]))); }])));
// Default namespace does not apply to attribute selectors // Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652 // https://github.com/mozilla/servo/pull/1652
@ -1790,8 +1902,7 @@ pub mod tests {
}), }),
}), }),
]), ]),
pseudo_element: None, specificity_and_flags: specificity(0, 1, 0),
specificity: specificity(0, 1, 0),
})))); }))));
// Default namespace does apply to type selectors // Default namespace does apply to type selectors
assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector {
@ -1802,8 +1913,7 @@ pub mod tests {
name: DummyAtom::from("e"), name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e") }), lower_name: DummyAtom::from("e") }),
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 1),
specificity: specificity(0, 0, 1),
})))); }))));
assert_eq!(parse_ns("*", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("*", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
@ -1811,8 +1921,7 @@ pub mod tests {
Component::DefaultNamespace(MATHML.into()), Component::DefaultNamespace(MATHML.into()),
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse_ns("*|*", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns("*|*", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
@ -1820,8 +1929,7 @@ pub mod tests {
Component::ExplicitAnyNamespace, Component::ExplicitAnyNamespace,
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
// Default namespace applies to universal and type selectors inside :not and :matches, // Default namespace applies to universal and type selectors inside :not and :matches,
// but not otherwise. // but not otherwise.
@ -1832,8 +1940,7 @@ pub mod tests {
Component::Class(DummyAtom::from("cl")) Component::Class(DummyAtom::from("cl"))
].into_boxed_slice()), ].into_boxed_slice()),
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 1, 0),
specificity: specificity(0, 1, 0),
})))); }))));
assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
@ -1843,8 +1950,7 @@ pub mod tests {
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
].into_boxed_slice()), ].into_boxed_slice()),
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse_ns(":not(e)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(e)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!( inner: SelectorInner::from_vec(vec!(
@ -1857,8 +1963,7 @@ pub mod tests {
}), }),
].into_boxed_slice()) ].into_boxed_slice())
)), )),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 1),
specificity: specificity(0, 0, 1),
})))); }))));
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector { assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
@ -1872,15 +1977,42 @@ pub mod tests {
}), }),
}, DummyAtom::from("foo")) }, DummyAtom::from("foo"))
]), ]),
pseudo_element: None, specificity_and_flags: specificity(0, 1, 0),
specificity: specificity(0, 1, 0),
}]))); }])));
// https://github.com/mozilla/servo/issues/1723 // https://github.com/mozilla/servo/issues/1723
assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec![]), inner: SelectorInner::from_vec(
pseudo_element: Some(PseudoElement::Before), vec![
specificity: specificity(0, 0, 1), Component::PseudoElement(PseudoElement::Before),
]
),
specificity_and_flags: specificity(0, 0, 1) | HAS_PSEUDO_BIT,
})))); }))));
assert_eq!(parse("::before:hover"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(
vec![
Component::PseudoElement(PseudoElement::Before),
Component::NonTSPseudoClass(PseudoClass::Hover),
]
),
specificity_and_flags: specificity(0, 1, 1) | HAS_PSEUDO_BIT,
}))));
assert_eq!(parse("::before:hover:hover"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(
vec![
Component::PseudoElement(PseudoElement::Before),
Component::NonTSPseudoClass(PseudoClass::Hover),
Component::NonTSPseudoClass(PseudoClass::Hover),
]
),
specificity_and_flags: specificity(0, 2, 1) | HAS_PSEUDO_BIT,
}))));
assert_eq!(parse("::before:hover:active"), Err(()));
assert_eq!(parse("::before:hover .foo"), Err(()));
assert_eq!(parse("::before .foo"), Err(()));
assert_eq!(parse("::before ~ bar"), Err(()));
assert_eq!(parse("::before:active"), Err(()));
// https://github.com/servo/servo/issues/15335 // https://github.com/servo/servo/issues/15335
assert_eq!(parse(":: before"), Err(())); assert_eq!(parse(":: before"), Err(()));
assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector { assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector {
@ -1890,9 +2022,10 @@ pub mod tests {
name: DummyAtom::from("div"), name: DummyAtom::from("div"),
lower_name: DummyAtom::from("div") }), lower_name: DummyAtom::from("div") }),
Component::Combinator(Combinator::Descendant), Component::Combinator(Combinator::Descendant),
Component::Combinator(Combinator::PseudoElement),
Component::PseudoElement(PseudoElement::After),
]), ]),
pseudo_element: Some(PseudoElement::After), specificity_and_flags: specificity(0, 0, 2) | HAS_PSEUDO_BIT,
specificity: specificity(0, 0, 2),
})))); }))));
assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector { assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec( inner: SelectorInner::from_vec(
@ -1901,8 +2034,7 @@ pub mod tests {
Component::Combinator(Combinator::Child), Component::Combinator(Combinator::Child),
Component::Class(DummyAtom::from("ok")), Component::Class(DummyAtom::from("ok")),
]), ]),
pseudo_element: None, specificity_and_flags: (1 << 20) + (1 << 10) + (0 << 0),
specificity: (1 << 20) + (1 << 10) + (0 << 0),
}]))); }])));
parser.default_ns = None; parser.default_ns = None;
assert_eq!(parse(":not(#provel.old)"), Err(())); assert_eq!(parse(":not(#provel.old)"), Err(()));
@ -1914,8 +2046,7 @@ pub mod tests {
Component::ID(DummyAtom::from("provel")), Component::ID(DummyAtom::from("provel")),
].into_boxed_slice() ].into_boxed_slice()
))), ))),
pseudo_element: None, specificity_and_flags: specificity(1, 0, 0),
specificity: specificity(1, 0, 0),
})))); }))));
assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation( inner: SelectorInner::from_vec(vec!(Component::Negation(
@ -1927,8 +2058,7 @@ pub mod tests {
}), }),
].into_boxed_slice() ].into_boxed_slice()
))), ))),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 1),
specificity: specificity(0, 0, 1),
})))); }))));
// https://github.com/servo/servo/issues/16017 // https://github.com/servo/servo/issues/16017
assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector {
@ -1937,8 +2067,7 @@ pub mod tests {
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
].into_boxed_slice() ].into_boxed_slice()
))), ))),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse_ns(":not(|*)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(|*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation( inner: SelectorInner::from_vec(vec!(Component::Negation(
@ -1947,8 +2076,7 @@ pub mod tests {
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
].into_boxed_slice() ].into_boxed_slice()
))), ))),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse_ns(":not(*|*)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(*|*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation( inner: SelectorInner::from_vec(vec!(Component::Negation(
@ -1957,8 +2085,7 @@ pub mod tests {
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
].into_boxed_slice() ].into_boxed_slice()
))), ))),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
assert_eq!(parse_ns(":not(svg|*)", &parser), Ok(SelectorList(vec!(Selector { assert_eq!(parse_ns(":not(svg|*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation( inner: SelectorInner::from_vec(vec!(Component::Negation(
@ -1967,11 +2094,40 @@ pub mod tests {
Component::ExplicitUniversalType, Component::ExplicitUniversalType,
].into_boxed_slice() ].into_boxed_slice()
))), ))),
pseudo_element: None, specificity_and_flags: specificity(0, 0, 0),
specificity: specificity(0, 0, 0),
})))); }))));
} }
#[test]
fn test_pseudo_iter() {
let selector = &parse("q::before").unwrap().0[0];
assert!(!selector.is_universal());
let mut iter = selector.inner.complex.iter();
assert_eq!(iter.next(), Some(&Component::PseudoElement(PseudoElement::Before)));
assert_eq!(iter.next(), None);
let combinator = iter.next_sequence();
assert_eq!(combinator, Some(Combinator::PseudoElement));
assert!(matches!(iter.next(), Some(&Component::LocalName(..))));
assert_eq!(iter.next(), None);
assert_eq!(iter.next_sequence(), None);
}
#[test]
fn test_universal() {
let selector = &parse("*|*::before").unwrap().0[0];
assert!(selector.is_universal());
}
#[test]
fn test_empty_pseudo_iter() {
let selector = &parse("::before").unwrap().0[0];
assert!(selector.is_universal());
let mut iter = selector.inner.complex.iter();
assert_eq!(iter.next(), Some(&Component::PseudoElement(PseudoElement::Before)));
assert_eq!(iter.next(), None);
assert_eq!(iter.next_sequence(), None);
}
struct TestVisitor { struct TestVisitor {
seen: Vec<String>, seen: Vec<String>,
} }
@ -1992,5 +2148,9 @@ pub mod tests {
let mut test_visitor = TestVisitor { seen: vec![], }; let mut test_visitor = TestVisitor { seen: vec![], };
parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor); parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor);
assert!(test_visitor.seen.contains(&":hover".into())); assert!(test_visitor.seen.contains(&":hover".into()));
let mut test_visitor = TestVisitor { seen: vec![], };
parse("::before:hover").unwrap().0[0].visit(&mut test_visitor);
assert!(test_visitor.seen.contains(&":hover".into()));
} }
} }

Просмотреть файл

@ -3,14 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ToCss; use cssparser::ToCss;
use gecko_like_types;
use gecko_like_types::*; use gecko_like_types::*;
use parser;
use parser::*; use parser::*;
use precomputed_hash::PrecomputedHash; use precomputed_hash::PrecomputedHash;
use std::fmt; use std::fmt;
use visitor::SelectorVisitor; use visitor::SelectorVisitor;
size_of_test!(size_of_selector, Selector<Impl>, 72); size_of_test!(size_of_selector, Selector<Impl>, 48);
size_of_test!(size_of_pseudo_element, PseudoElementSelector, 16); size_of_test!(size_of_pseudo_element, gecko_like_types::PseudoElement, 1);
size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40); size_of_test!(size_of_selector_inner, SelectorInner<Impl>, 40);
size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24); size_of_test!(size_of_complex_selector, ComplexSelector<Impl>, 24);
@ -18,6 +20,9 @@ size_of_test!(size_of_component, Component<Impl>, 64);
size_of_test!(size_of_attr_selector, AttrSelector<Impl>, 48); size_of_test!(size_of_attr_selector, AttrSelector<Impl>, 48);
size_of_test!(size_of_pseudo_class, PseudoClass, 24); size_of_test!(size_of_pseudo_class, PseudoClass, 24);
impl parser::PseudoElement for gecko_like_types::PseudoElement {
type Impl = Impl;
}
// Boilerplate // Boilerplate
@ -31,7 +36,7 @@ impl SelectorImpl for Impl {
type BorrowedLocalName = Atom; type BorrowedLocalName = Atom;
type BorrowedNamespaceUrl = Atom; type BorrowedNamespaceUrl = Atom;
type NonTSPseudoClass = PseudoClass; type NonTSPseudoClass = PseudoClass;
type PseudoElementSelector = PseudoElementSelector; type PseudoElement = gecko_like_types::PseudoElement;
} }
impl SelectorMethods for PseudoClass { impl SelectorMethods for PseudoClass {
@ -45,7 +50,7 @@ impl ToCss for PseudoClass {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() } fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() }
} }
impl ToCss for PseudoElementSelector { impl ToCss for gecko_like_types::PseudoElement {
fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() } fn to_css<W>(&self, _: &mut W) -> fmt::Result where W: fmt::Write { unimplemented!() }
} }

Просмотреть файл

@ -123,6 +123,14 @@ impl<T> MatchAttr for T where T: MatchAttrGeneric, T::Impl: SelectorImpl<AttrVal
pub trait Element: MatchAttr + Sized { pub trait Element: MatchAttr + Sized {
fn parent_element(&self) -> Option<Self>; fn parent_element(&self) -> Option<Self>;
/// The parent of a given pseudo-element, after matching a pseudo-element
/// selector.
///
/// This is guaranteed to be called in a pseudo-element.
fn pseudo_element_originating_element(&self) -> Option<Self> {
self.parent_element()
}
// Skips non-element nodes // Skips non-element nodes
fn first_child_element(&self) -> Option<Self>; fn first_child_element(&self) -> Option<Self>;
@ -145,6 +153,11 @@ pub trait Element: MatchAttr + Sized {
flags_setter: &mut F) -> bool flags_setter: &mut F) -> bool
where F: FnMut(&Self, ElementSelectorFlags); where F: FnMut(&Self, ElementSelectorFlags);
fn match_pseudo_element(&self,
pe: &<Self::Impl as SelectorImpl>::PseudoElement,
context: &mut MatchingContext)
-> bool;
fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>; fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool; fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;

Просмотреть файл

@ -10,12 +10,24 @@
use cssparser::ToCss; use cssparser::ToCss;
use gecko_bindings::structs::{self, CSSPseudoElementType}; use gecko_bindings::structs::{self, CSSPseudoElementType};
use selector_parser::PseudoElementCascadeType; use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
use std::fmt; use std::fmt;
use string_cache::Atom; use string_cache::Atom;
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs")); include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs"));
impl ::selectors::parser::PseudoElement for PseudoElement {
type Impl = SelectorImpl;
fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
if !self.supports_user_action_state() {
return false;
}
return pseudo_class.is_safe_user_action_state();
}
}
impl PseudoElement { impl PseudoElement {
/// Returns the kind of cascade type that a given pseudo is going to use. /// Returns the kind of cascade type that a given pseudo is going to use.
/// ///

Просмотреть файл

@ -5,7 +5,6 @@
//! Gecko-specific bits for selector-parsing. //! Gecko-specific bits for selector-parsing.
use cssparser::{Parser, ToCss}; use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState; use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType; use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType}; use selector_parser::{SelectorParser, PseudoElementCascadeType};
@ -132,7 +131,7 @@ impl NonTSPseudoClass {
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
/// ///
/// We intentionally skip the link-related ones. /// We intentionally skip the link-related ones.
fn is_safe_user_action_state(&self) -> bool { pub fn is_safe_user_action_state(&self) -> bool {
matches!(*self, NonTSPseudoClass::Hover | matches!(*self, NonTSPseudoClass::Hover |
NonTSPseudoClass::Active | NonTSPseudoClass::Active |
NonTSPseudoClass::Focus) NonTSPseudoClass::Focus)
@ -195,58 +194,6 @@ impl NonTSPseudoClass {
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SelectorImpl; pub struct SelectorImpl;
/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
pseudo: PseudoElement,
state: ElementState,
}
impl PseudoElementSelector {
/// Returns the pseudo-element this selector represents.
pub fn pseudo_element(&self) -> &PseudoElement {
&self.pseudo
}
/// Returns the pseudo-element selector state.
pub fn state(&self) -> ElementState {
self.state
}
}
impl ToCss for PseudoElementSelector {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where W: fmt::Write,
{
if cfg!(debug_assertions) {
let mut state = self.state;
state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
assert_eq!(state, ElementState::empty(),
"Unhandled pseudo-element state selector?");
}
self.pseudo.to_css(dest)?;
if self.state.contains(IN_HOVER_STATE) {
dest.write_str(":hover")?
}
if self.state.contains(IN_ACTIVE_STATE) {
dest.write_str(":active")?
}
if self.state.contains(IN_FOCUS_STATE) {
dest.write_str(":focus")?
}
Ok(())
}
}
impl ::selectors::SelectorImpl for SelectorImpl { impl ::selectors::SelectorImpl for SelectorImpl {
type AttrValue = Atom; type AttrValue = Atom;
type Identifier = Atom; type Identifier = Atom;
@ -257,7 +204,7 @@ impl ::selectors::SelectorImpl for SelectorImpl {
type BorrowedNamespaceUrl = WeakNamespace; type BorrowedNamespaceUrl = WeakNamespace;
type BorrowedLocalName = WeakAtom; type BorrowedLocalName = WeakAtom;
type PseudoElementSelector = PseudoElementSelector; type PseudoElement = PseudoElement;
type NonTSPseudoClass = NonTSPseudoClass; type NonTSPseudoClass = NonTSPseudoClass;
} }
@ -319,38 +266,9 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
} }
} }
fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> { fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
let pseudo = PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) { .ok_or(())
Some(pseudo) => pseudo,
None => return Err(()),
};
let state = if pseudo.supports_user_action_state() {
input.try(|input| {
let mut state = ElementState::empty();
while !input.is_exhausted() {
input.expect_colon()?;
let ident = input.expect_ident()?;
let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;
if !pseudo_class.is_safe_user_action_state() {
return Err(())
}
state.insert(pseudo_class.state_flag());
}
Ok(state)
}).ok()
} else {
None
};
Ok(PseudoElementSelector {
pseudo: pseudo,
state: state.unwrap_or(ElementState::empty()),
})
} }
fn default_namespace(&self) -> Option<Namespace> { fn default_namespace(&self) -> Option<Namespace> {

Просмотреть файл

@ -64,7 +64,7 @@ use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel; use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::ElementExt; use selector_parser::ElementExt;
use selectors::Element; use selectors::Element;
use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::parser::{AttrSelector, NamespaceConstraint}; use selectors::parser::{AttrSelector, NamespaceConstraint};
use shared_lock::Locked; use shared_lock::Locked;
use sink::Push; use sink::Push;
@ -1055,6 +1055,11 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
parent_node.and_then(|n| n.as_element()) parent_node.and_then(|n| n.as_element())
} }
fn pseudo_element_originating_element(&self) -> Option<Self> {
debug_assert!(self.implemented_pseudo_element().is_some());
self.closest_non_native_anonymous_ancestor()
}
fn first_child_element(&self) -> Option<Self> { fn first_child_element(&self) -> Option<Self> {
let mut child = self.as_node().first_child(); let mut child = self.as_node().first_child();
while let Some(child_node) = child { while let Some(child_node) = child {
@ -1244,6 +1249,20 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
} }
fn match_pseudo_element(&self,
pseudo_element: &PseudoElement,
_context: &mut MatchingContext)
-> bool
{
// TODO(emilio): I believe we could assert we are a pseudo-element and
// match the proper pseudo-element, given how we rulehash the stuff
// based on the pseudo.
match self.implemented_pseudo_element() {
Some(ref pseudo) => pseudo == pseudo_element,
None => false,
}
}
fn get_id(&self) -> Option<Atom> { fn get_id(&self) -> Option<Atom> {
let ptr = unsafe { let ptr = unsafe {
bindings::Gecko_AtomAttrValue(self.0, bindings::Gecko_AtomAttrValue(self.0,
@ -1378,8 +1397,9 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
impl<'le> ElementExt for GeckoElement<'le> { impl<'le> ElementExt for GeckoElement<'le> {
#[inline] #[inline]
fn is_link(&self) -> bool { fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut MatchingContext::default(), &mut context,
&mut |_, _| {}) &mut |_, _| {})
} }

Просмотреть файл

@ -15,7 +15,6 @@ use cascade_info::CascadeInfo;
use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext}; use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData}; use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use dom::{AnimationRules, SendElement, TElement, TNode}; use dom::{AnimationRules, SendElement, TElement, TNode};
use element_state::ElementState;
use font_metrics::FontMetricsProvider; use font_metrics::FontMetricsProvider;
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
@ -24,7 +23,7 @@ use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl}; use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use selectors::matching::{ElementSelectorFlags, MatchingContext, StyleRelations}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations};
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS; use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use shared_lock::StylesheetGuards; use shared_lock::StylesheetGuards;
use sink::ForgetfulSink; use sink::ForgetfulSink;
@ -895,10 +894,10 @@ pub trait MatchMethods : TElement {
sharing: StyleSharingBehavior) sharing: StyleSharingBehavior)
{ {
// Perform selector matching for the primary style. // Perform selector matching for the primary style.
let mut primary_matching_context = MatchingContext::default(); let mut relations = StyleRelations::empty();
let _rule_node_changed = self.match_primary(context, let _rule_node_changed = self.match_primary(context,
data, data,
&mut primary_matching_context); &mut relations);
// Cascade properties and compute primary values. // Cascade properties and compute primary values.
self.cascade_primary(context, data); self.cascade_primary(context, data);
@ -912,7 +911,7 @@ pub trait MatchMethods : TElement {
// If we have any pseudo elements, indicate so in the primary StyleRelations. // If we have any pseudo elements, indicate so in the primary StyleRelations.
if !data.styles().pseudos.is_empty() { if !data.styles().pseudos.is_empty() {
primary_matching_context.relations |= AFFECTED_BY_PSEUDO_ELEMENTS; relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
} }
// If the style is shareable, add it to the LRU cache. // If the style is shareable, add it to the LRU cache.
@ -932,7 +931,7 @@ pub trait MatchMethods : TElement {
.style_sharing_candidate_cache .style_sharing_candidate_cache
.insert_if_possible(self, .insert_if_possible(self,
data.styles().primary.values(), data.styles().primary.values(),
primary_matching_context.relations, relations,
revalidation_match_results); revalidation_match_results);
} }
} }
@ -952,7 +951,7 @@ pub trait MatchMethods : TElement {
fn match_primary(&self, fn match_primary(&self,
context: &mut StyleContext<Self>, context: &mut StyleContext<Self>,
data: &mut ElementData, data: &mut ElementData,
matching_context: &mut MatchingContext) relations: &mut StyleRelations)
-> bool -> bool
{ {
let implemented_pseudo = self.implemented_pseudo_element(); let implemented_pseudo = self.implemented_pseudo_element();
@ -1004,35 +1003,27 @@ pub trait MatchMethods : TElement {
let animation_rules = self.get_animation_rules(); let animation_rules = self.get_animation_rules();
let bloom = context.thread_local.bloom_filter.filter(); let bloom = context.thread_local.bloom_filter.filter();
let map = &mut context.thread_local.selector_flags; let map = &mut context.thread_local.selector_flags;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| { let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
self.apply_selector_flags(map, element, flags); self.apply_selector_flags(map, element, flags);
}; };
let selector_matching_target = match implemented_pseudo { let mut matching_context =
Some(..) => { MatchingContext::new(MatchingMode::Normal, Some(bloom));
self.closest_non_native_anonymous_ancestor()
.expect("Pseudo-element without non-NAC parent?")
},
None => *self,
};
let pseudo_and_state = match implemented_pseudo {
Some(ref pseudo) => Some((pseudo, self.get_state())),
None => None,
};
// Compute the primary rule node. // Compute the primary rule node.
stylist.push_applicable_declarations(&selector_matching_target, stylist.push_applicable_declarations(self,
Some(bloom), implemented_pseudo.as_ref(),
style_attribute, style_attribute,
smil_override, smil_override,
animation_rules, animation_rules,
pseudo_and_state,
&mut applicable_declarations, &mut applicable_declarations,
matching_context, &mut matching_context,
&mut set_selector_flags); &mut set_selector_flags);
*relations = matching_context.relations;
let primary_rule_node = let primary_rule_node =
compute_rule_node::<Self>(&stylist.rule_tree, compute_rule_node::<Self>(&stylist.rule_tree,
&mut applicable_declarations, &mut applicable_declarations,
@ -1041,8 +1032,8 @@ pub trait MatchMethods : TElement {
return data.set_primary_rules(primary_rule_node); return data.set_primary_rules(primary_rule_node);
} }
/// Runs selector matching to (re)compute eager pseudo-element rule nodes for this /// Runs selector matching to (re)compute eager pseudo-element rule nodes
/// element. /// for this element.
/// ///
/// Returns whether any of the pseudo rule nodes changed (including, but not /// Returns whether any of the pseudo rule nodes changed (including, but not
/// limited to, cases where we match different pseudos altogether). /// limited to, cases where we match different pseudos altogether).
@ -1070,6 +1061,10 @@ pub trait MatchMethods : TElement {
let rule_tree = &stylist.rule_tree; let rule_tree = &stylist.rule_tree;
let bloom_filter = context.thread_local.bloom_filter.filter(); let bloom_filter = context.thread_local.bloom_filter.filter();
let mut matching_context =
MatchingContext::new(MatchingMode::ForStatelessPseudoElement,
Some(bloom_filter));
// Compute rule nodes for eagerly-cascaded pseudo-elements. // Compute rule nodes for eagerly-cascaded pseudo-elements.
let mut matches_different_pseudos = false; let mut matches_different_pseudos = false;
let mut rule_nodes_changed = false; let mut rule_nodes_changed = false;
@ -1079,13 +1074,12 @@ pub trait MatchMethods : TElement {
// NB: We handle animation rules for ::before and ::after when // NB: We handle animation rules for ::before and ::after when
// traversing them. // traversing them.
stylist.push_applicable_declarations(self, stylist.push_applicable_declarations(self,
Some(bloom_filter), Some(&pseudo),
None, None,
None, None,
AnimationRules(None, None), AnimationRules(None, None),
Some((&pseudo, ElementState::empty())),
&mut applicable_declarations, &mut applicable_declarations,
&mut MatchingContext::default(), &mut matching_context,
&mut set_selector_flags); &mut set_selector_flags);
if !applicable_declarations.is_empty() { if !applicable_declarations.is_empty() {

Просмотреть файл

@ -9,14 +9,13 @@
use Atom; use Atom;
use dom::TElement; use dom::TElement;
use element_state::*; use element_state::*;
use fnv::FnvHashMap;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use gecko_bindings::structs::nsRestyleHint; use gecko_bindings::structs::nsRestyleHint;
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap}; use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap};
use selectors::{Element, MatchAttr}; use selectors::{Element, MatchAttr};
use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::matching::matches_selector; use selectors::matching::matches_selector;
use selectors::parser::{AttrSelector, Combinator, Component, Selector}; use selectors::parser::{AttrSelector, Combinator, Component, Selector};
use selectors::parser::{SelectorInner, SelectorMethods}; use selectors::parser::{SelectorInner, SelectorMethods};
@ -406,6 +405,14 @@ impl<'a, E> Element for ElementWrapper<'a, E>
} }
} }
fn match_pseudo_element(&self,
pseudo_element: &PseudoElement,
context: &mut MatchingContext)
-> bool
{
self.element.match_pseudo_element(pseudo_element, context)
}
fn parent_element(&self) -> Option<Self> { fn parent_element(&self) -> Option<Self> {
self.element.parent_element() self.element.parent_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map)) .map(|e| ElementWrapper::new(e, self.snapshot_map))
@ -475,6 +482,11 @@ impl<'a, E> Element for ElementWrapper<'a, E>
_ => self.element.each_class(callback) _ => self.element.each_class(callback)
} }
} }
fn pseudo_element_originating_element(&self) -> Option<Self> {
self.element.closest_non_native_anonymous_ancestor()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
}
} }
fn selector_to_state(sel: &Component<SelectorImpl>) -> ElementState { fn selector_to_state(sel: &Component<SelectorImpl>) -> ElementState {
@ -507,6 +519,9 @@ fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
match combinator { match combinator {
None => RESTYLE_SELF, None => RESTYLE_SELF,
Some(c) => match c { Some(c) => match c {
// NB: RESTYLE_SELF is needed to handle properly eager pseudos,
// otherwise we may leave a stale style on the parent.
Combinator::PseudoElement => RESTYLE_SELF | RESTYLE_DESCENDANTS,
Combinator::Child => RESTYLE_DESCENDANTS, Combinator::Child => RESTYLE_DESCENDANTS,
Combinator::Descendant => RESTYLE_DESCENDANTS, Combinator::Descendant => RESTYLE_DESCENDANTS,
Combinator::NextSibling => RESTYLE_LATER_SIBLINGS, Combinator::NextSibling => RESTYLE_LATER_SIBLINGS,
@ -634,13 +649,6 @@ impl SelectorVisitor for SensitivitiesVisitor {
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct DependencySet { pub struct DependencySet {
/// A map used for pseudo-element's dependencies.
///
/// Note that pseudo-elements are somewhat special, because some of them in
/// Gecko track state, and also because they don't do selector-matching as
/// normal, but against their parent element.
pseudo_dependencies: FnvHashMap<PseudoElement, SelectorMap<PseudoElementDependency>>,
/// This is for all other normal element's selectors/selector parts. /// This is for all other normal element's selectors/selector parts.
dependencies: SelectorMap<Dependency>, dependencies: SelectorMap<Dependency>,
} }
@ -668,34 +676,9 @@ impl DependencySet {
index += 1; // Account for the simple selector. index += 1; // Account for the simple selector.
} }
let pseudo_selector_is_state_dependent =
sequence_start == 0 &&
selector.pseudo_element.as_ref().map_or(false, |pseudo_selector| {
!pseudo_selector.state().is_empty()
});
if pseudo_selector_is_state_dependent {
let pseudo_selector = selector.pseudo_element.as_ref().unwrap();
self.pseudo_dependencies
.entry(pseudo_selector.pseudo_element().clone())
.or_insert_with(SelectorMap::new)
.insert(PseudoElementDependency {
selector: selector.clone(),
});
}
// If we found a sensitivity, add an entry in the dependency set. // If we found a sensitivity, add an entry in the dependency set.
if !visitor.sensitivities.is_empty() { if !visitor.sensitivities.is_empty() {
let mut hint = combinator_to_restyle_hint(combinator); let hint = combinator_to_restyle_hint(combinator);
if sequence_start == 0 && selector.pseudo_element.is_some() {
// FIXME(emilio): Be more granular about this. See the
// comment in `PseudoElementDependency` about how could this
// be modified in order to be more efficient and restyle
// less.
hint |= RESTYLE_DESCENDANTS;
}
let dep_selector = if sequence_start == 0 { let dep_selector = if sequence_start == 0 {
// Reuse the bloom hashes if this is the base selector. // Reuse the bloom hashes if this is the base selector.
@ -724,82 +707,22 @@ impl DependencySet {
pub fn new() -> Self { pub fn new() -> Self {
DependencySet { DependencySet {
dependencies: SelectorMap::new(), dependencies: SelectorMap::new(),
pseudo_dependencies: FnvHashMap::default(),
} }
} }
/// Return the total number of dependencies that this set contains. /// Return the total number of dependencies that this set contains.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.dependencies.len() + self.dependencies.len()
self.pseudo_dependencies.values().fold(0, |acc, val| acc + val.len())
} }
/// Clear this dependency set. /// Clear this dependency set.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.dependencies = SelectorMap::new(); self.dependencies = SelectorMap::new();
self.pseudo_dependencies.clear()
} }
fn compute_pseudo_hint<E>( /// Compute a restyle hint given an element and a snapshot, per the rules
&self, /// explained in the rest of the documentation.
pseudo: &E, pub fn compute_hint<E>(
pseudo_element: PseudoElement,
snapshots: &SnapshotMap)
-> RestyleHint
where E: TElement,
{
debug!("compute_pseudo_hint: {:?}, {:?}", pseudo, pseudo_element);
debug_assert!(pseudo.has_snapshot());
let map = match self.pseudo_dependencies.get(&pseudo_element) {
Some(map) => map,
None => return RestyleHint::empty(),
};
// Only pseudo-element's state is relevant.
let pseudo_state_changes =
ElementWrapper::new(*pseudo, snapshots).state_changes();
debug!("pseudo_state_changes: {:?}", pseudo_state_changes);
if pseudo_state_changes.is_empty() {
return RestyleHint::empty();
}
let selector_matching_target =
pseudo.closest_non_native_anonymous_ancestor().unwrap();
// Note that we rely on that, if the originating element changes, it'll
// post a restyle hint that would make us redo selector matching, so we
// don't need to care about that.
//
// If that ever changes, we'd need to share more code with
// `compute_element_hint`.
let mut hint = RestyleHint::empty();
map.lookup(selector_matching_target, &mut |dep| {
// If the selector didn't match before, it either doesn't match now
// either (or it doesn't matter because our parent posted a restyle
// for us above).
if !matches_selector(&dep.selector.inner, &selector_matching_target,
None, &mut MatchingContext::default(),
&mut |_, _| {}) {
return true;
}
let pseudo_selector = dep.selector.pseudo_element.as_ref().unwrap();
debug_assert!(!pseudo_selector.state().is_empty());
if pseudo_selector.state().intersects(pseudo_state_changes) {
hint = RESTYLE_SELF;
return false;
}
true
});
hint
}
fn compute_element_hint<E>(
&self, &self,
el: &E, el: &E,
snapshots: &SnapshotMap) snapshots: &SnapshotMap)
@ -838,8 +761,18 @@ impl DependencySet {
}); });
} }
// FIXME(emilio): A bloom filter here would be neat.
let mut matching_context =
MatchingContext::new(MatchingMode::Normal, None);
let lookup_element = if el.implemented_pseudo_element().is_some() {
el.closest_non_native_anonymous_ancestor().unwrap()
} else {
*el
};
self.dependencies self.dependencies
.lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| { .lookup_with_additional(lookup_element, additional_id, &additional_classes, &mut |dep| {
trace!("scanning dependency: {:?}", dep); trace!("scanning dependency: {:?}", dep);
if !dep.sensitivities.sensitive_to(attrs_changed, if !dep.sensitivities.sensitive_to(attrs_changed,
state_changes) { state_changes) {
@ -856,12 +789,12 @@ impl DependencySet {
// been set during original matching for any element that might // been set during original matching for any element that might
// change its matching behavior here. // change its matching behavior here.
let matched_then = let matched_then =
matches_selector(&dep.selector, &snapshot_el, None, matches_selector(&dep.selector, &snapshot_el,
&mut MatchingContext::default(), &mut matching_context,
&mut |_, _| {}); &mut |_, _| {});
let matches_now = let matches_now =
matches_selector(&dep.selector, el, None, matches_selector(&dep.selector, el,
&mut MatchingContext::default(), &mut matching_context,
&mut |_, _| {}); &mut |_, _| {});
if matched_then != matches_now { if matched_then != matches_now {
hint.insert(dep.hint); hint.insert(dep.hint);
@ -875,21 +808,4 @@ impl DependencySet {
hint hint
} }
/// Compute a restyle hint given an element and a snapshot, per the rules
/// explained in the rest of the documentation.
pub fn compute_hint<E>(&self,
el: &E,
snapshots: &SnapshotMap)
-> RestyleHint
where E: TElement + Clone,
{
debug!("DependencySet::compute_hint({:?})", el);
if let Some(pseudo) = el.implemented_pseudo_element() {
return self.compute_pseudo_hint(el, pseudo, snapshots);
}
self.compute_element_hint(el, snapshots)
}
} }

Просмотреть файл

@ -15,7 +15,7 @@ use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot; use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser}; use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::{Element, MatchAttrGeneric}; use selectors::{Element, MatchAttrGeneric};
use selectors::matching::MatchingContext; use selectors::matching::{MatchingContext, MatchingMode};
use selectors::parser::{AttrSelector, SelectorMethods}; use selectors::parser::{AttrSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
use std::borrow::Cow; use std::borrow::Cow;
@ -51,6 +51,14 @@ pub enum PseudoElement {
ServoInlineAbsolute, ServoInlineAbsolute,
} }
impl ::selectors::parser::PseudoElement for PseudoElement {
type Impl = SelectorImpl;
fn supports_pseudo_class(&self, _: &NonTSPseudoClass) -> bool {
false
}
}
impl ToCss for PseudoElement { impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::PseudoElement::*; use self::PseudoElement::*;
@ -78,18 +86,6 @@ impl ToCss for PseudoElement {
pub const EAGER_PSEUDO_COUNT: usize = 3; pub const EAGER_PSEUDO_COUNT: usize = 3;
impl PseudoElement { impl PseudoElement {
/// The pseudo-element, used for compatibility with Gecko's
/// `PseudoElementSelector`.
pub fn pseudo_element(&self) -> &Self {
self
}
/// The pseudo-element selector's state, used for compatibility with Gecko's
/// `PseudoElementSelector`.
pub fn state(&self) -> ElementState {
ElementState::empty()
}
/// Gets the canonical index of this eagerly-cascaded pseudo-element. /// Gets the canonical index of this eagerly-cascaded pseudo-element.
#[inline] #[inline]
pub fn eager_index(&self) -> usize { pub fn eager_index(&self) -> usize {
@ -264,7 +260,7 @@ impl NonTSPseudoClass {
pub struct SelectorImpl; pub struct SelectorImpl;
impl ::selectors::SelectorImpl for SelectorImpl { impl ::selectors::SelectorImpl for SelectorImpl {
type PseudoElementSelector = PseudoElement; type PseudoElement = PseudoElement;
type NonTSPseudoClass = NonTSPseudoClass; type NonTSPseudoClass = NonTSPseudoClass;
type AttrValue = String; type AttrValue = String;
@ -323,9 +319,7 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
Ok(pseudo_class) Ok(pseudo_class)
} }
fn parse_pseudo_element(&self, fn parse_pseudo_element(&self, name: Cow<str>)
name: Cow<str>,
_input: &mut CssParser)
-> Result<PseudoElement, ()> { -> Result<PseudoElement, ()> {
use self::PseudoElement::*; use self::PseudoElement::*;
let pseudo_element = match_ignore_ascii_case! { &name, let pseudo_element = match_ignore_ascii_case! { &name,
@ -579,8 +573,9 @@ impl MatchAttrGeneric for ServoElementSnapshot {
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E { impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
fn is_link(&self) -> bool { fn is_link(&self) -> bool {
let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut MatchingContext::default(), &mut context,
&mut |_, _| {}) &mut |_, _| {})
} }

Просмотреть файл

@ -26,10 +26,9 @@ use properties::PropertyDeclarationBlock;
use restyle_hints::{RestyleHint, DependencySet}; use restyle_hints::{RestyleHint, DependencySet};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap}; use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
use selectors::Element;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS}; use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext}; use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter}; use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter};
use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector}; use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector};
use selectors::visitor::SelectorVisitor; use selectors::visitor::SelectorVisitor;
@ -479,9 +478,9 @@ impl Stylist {
rule: &Arc<Locked<StyleRule>>, rule: &Arc<Locked<StyleRule>>,
stylesheet: &Stylesheet) stylesheet: &Stylesheet)
{ {
let map = if let Some(ref pseudo_selector) = selector.pseudo_element { let map = if let Some(pseudo) = selector.pseudo_element() {
self.pseudos_map self.pseudos_map
.entry(pseudo_selector.pseudo_element().clone()) .entry(pseudo.clone())
.or_insert_with(PerPseudoElementSelectorMap::new) .or_insert_with(PerPseudoElementSelectorMap::new)
.borrow_for_origin(&stylesheet.origin) .borrow_for_origin(&stylesheet.origin)
} else { } else {
@ -525,9 +524,6 @@ impl Stylist {
#[inline] #[inline]
fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) { fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
if let Some(ref pseudo_selector) = selector.pseudo_element {
self.state_dependencies.insert(pseudo_selector.state());
}
selector.visit(&mut AttributeAndStateDependencyVisitor(self)); selector.visit(&mut AttributeAndStateDependencyVisitor(self));
} }
@ -635,14 +631,13 @@ impl Stylist {
guards: &StylesheetGuards, guards: &StylesheetGuards,
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
pseudo_state: ElementState,
parent: &Arc<ComputedValues>, parent: &Arc<ComputedValues>,
font_metrics: &FontMetricsProvider) font_metrics: &FontMetricsProvider)
-> Option<ComputedStyle> -> Option<ComputedStyle>
where E: TElement, where E: TElement,
{ {
let rule_node = let rule_node =
match self.lazy_pseudo_rules(guards, element, pseudo, pseudo_state) { match self.lazy_pseudo_rules(guards, element, pseudo) {
Some(rule_node) => rule_node, Some(rule_node) => rule_node,
None => return None None => return None
}; };
@ -673,8 +668,7 @@ impl Stylist {
pub fn lazy_pseudo_rules<E>(&self, pub fn lazy_pseudo_rules<E>(&self,
guards: &StylesheetGuards, guards: &StylesheetGuards,
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement)
pseudo_state: ElementState)
-> Option<StrongRuleNode> -> Option<StrongRuleNode>
where E: TElement where E: TElement
{ {
@ -708,14 +702,15 @@ impl Stylist {
}; };
let mut declarations = ApplicableDeclarationList::new(); let mut declarations = ApplicableDeclarationList::new();
let mut matching_context =
MatchingContext::new(MatchingMode::ForStatelessPseudoElement, None);
self.push_applicable_declarations(element, self.push_applicable_declarations(element,
None, Some(pseudo),
None, None,
None, None,
AnimationRules(None, None), AnimationRules(None, None),
Some((pseudo, pseudo_state)),
&mut declarations, &mut declarations,
&mut MatchingContext::default(), &mut matching_context,
&mut set_selector_flags); &mut set_selector_flags);
if declarations.is_empty() { if declarations.is_empty() {
return None return None
@ -839,16 +834,15 @@ impl Stylist {
pub fn push_applicable_declarations<E, V, F>( pub fn push_applicable_declarations<E, V, F>(
&self, &self,
element: &E, element: &E,
parent_bf: Option<&BloomFilter>, pseudo_element: Option<&PseudoElement>,
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>, style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>, smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules, animation_rules: AnimationRules,
pseudo_element: Option<(&PseudoElement, ElementState)>,
applicable_declarations: &mut V, applicable_declarations: &mut V,
context: &mut MatchingContext, context: &mut MatchingContext,
flags_setter: &mut F) flags_setter: &mut F)
where E: TElement, where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>, V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + ::std::fmt::Debug,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
debug_assert!(!self.is_device_dirty); debug_assert!(!self.is_device_dirty);
@ -857,20 +851,31 @@ impl Stylist {
debug_assert!(cfg!(feature = "gecko") || debug_assert!(cfg!(feature = "gecko") ||
style_attribute.is_none() || pseudo_element.is_none(), style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements"); "Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed())); debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed()));
let map = match pseudo_element { let map = match pseudo_element {
Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(), Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(),
None => &self.element_map, None => &self.element_map,
}; };
let is_implemented_pseudo =
element.implemented_pseudo_element().is_some();
// NB: This causes use to rule has pseudo selectors based on the
// properties of the originating element (which is fine, given the
// find_first_from_right usage).
let rule_hash_target = if is_implemented_pseudo {
element.closest_non_native_anonymous_ancestor().unwrap()
} else {
*element
};
debug!("Determining if style is shareable: pseudo: {}", debug!("Determining if style is shareable: pseudo: {}",
pseudo_element.is_some()); pseudo_element.is_some());
// Step 1: Normal user-agent rules. // Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element, map.user_agent.get_all_matching_rules(element,
pseudo_element, &rule_hash_target,
parent_bf,
applicable_declarations, applicable_declarations,
context, context,
flags_setter, flags_setter,
@ -893,19 +898,25 @@ impl Stylist {
debug!("preshints: {:?}", context.relations); debug!("preshints: {:?}", context.relations);
} }
if element.matches_user_and_author_rules() { // NB: the following condition, although it may look somewhat
// inaccurate, would be equivalent to something like:
//
// element.matches_user_and_author_rules() ||
// (is_implemented_pseudo &&
// rule_hash_target.matches_user_and_author_rules())
//
// Which may be more what you would probably expect.
if rule_hash_target.matches_user_and_author_rules() {
// Step 3: User and author normal rules. // Step 3: User and author normal rules.
map.user.get_all_matching_rules(element, map.user.get_all_matching_rules(element,
pseudo_element, &rule_hash_target,
parent_bf,
applicable_declarations, applicable_declarations,
context, context,
flags_setter, flags_setter,
CascadeLevel::UserNormal); CascadeLevel::UserNormal);
debug!("user normal: {:?}", context.relations); debug!("user normal: {:?}", context.relations);
map.author.get_all_matching_rules(element, map.author.get_all_matching_rules(element,
pseudo_element, &rule_hash_target,
parent_bf,
applicable_declarations, applicable_declarations,
context, context,
flags_setter, flags_setter,
@ -960,7 +971,6 @@ impl Stylist {
ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions)); ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
} }
debug!("transition: {:?}", context.relations); debug!("transition: {:?}", context.relations);
debug!("push_applicable_declarations: shareable: {:?}", context.relations); debug!("push_applicable_declarations: shareable: {:?}", context.relations);
} }
@ -993,6 +1003,11 @@ impl Stylist {
where E: TElement, where E: TElement,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
// NB: `MatchingMode` doesn't really matter, given we don't share style
// between pseudos.
let mut matching_context =
MatchingContext::new(MatchingMode::Normal, Some(bloom));
// Note that, by the time we're revalidating, we're guaranteed that the // Note that, by the time we're revalidating, we're guaranteed that the
// candidate and the entry have the same id, classes, and local name. // candidate and the entry have the same id, classes, and local name.
// This means we're guaranteed to get the same rulehash buckets for all // This means we're guaranteed to get the same rulehash buckets for all
@ -1002,8 +1017,7 @@ impl Stylist {
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| { self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| {
results.push(matches_selector(selector, results.push(matches_selector(selector,
element, element,
Some(bloom), &mut matching_context,
&mut MatchingContext::default(),
flags_setter)); flags_setter));
true true
}); });
@ -1182,6 +1196,7 @@ pub fn needs_revalidation(selector: &Selector<SelectorImpl>) -> bool {
/// Map that contains the CSS rules for a specific PseudoElement /// Map that contains the CSS rules for a specific PseudoElement
/// (or lack of PseudoElement). /// (or lack of PseudoElement).
#[cfg_attr(feature = "servo", derive(HeapSizeOf))] #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Debug)]
struct PerPseudoElementSelectorMap { struct PerPseudoElementSelectorMap {
/// Rules from user agent stylesheets /// Rules from user agent stylesheets
user_agent: SelectorMap<Rule>, user_agent: SelectorMap<Rule>,
@ -1283,13 +1298,12 @@ impl SelectorMap<Rule> {
/// Sort the Rules at the end to maintain cascading order. /// Sort the Rules at the end to maintain cascading order.
pub fn get_all_matching_rules<E, V, F>(&self, pub fn get_all_matching_rules<E, V, F>(&self,
element: &E, element: &E,
pseudo_element: Option<(&PseudoElement, ElementState)>, rule_hash_target: &E,
parent_bf: Option<&BloomFilter>,
matching_rules_list: &mut V, matching_rules_list: &mut V,
context: &mut MatchingContext, context: &mut MatchingContext,
flags_setter: &mut F, flags_setter: &mut F,
cascade_level: CascadeLevel) cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>, where E: TElement,
V: VecLike<ApplicableDeclarationBlock>, V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
@ -1299,10 +1313,8 @@ impl SelectorMap<Rule> {
// At the end, we're going to sort the rules that we added, so remember where we began. // At the end, we're going to sort the rules that we added, so remember where we began.
let init_len = matching_rules_list.len(); let init_len = matching_rules_list.len();
if let Some(id) = element.get_id() { if let Some(id) = rule_hash_target.get_id() {
SelectorMap::get_matching_rules_from_hash(element, SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf,
&self.id_hash, &self.id_hash,
&id, &id,
matching_rules_list, matching_rules_list,
@ -1311,10 +1323,8 @@ impl SelectorMap<Rule> {
cascade_level) cascade_level)
} }
element.each_class(|class| { rule_hash_target.each_class(|class| {
SelectorMap::get_matching_rules_from_hash(element, SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf,
&self.class_hash, &self.class_hash,
class, class,
matching_rules_list, matching_rules_list,
@ -1324,18 +1334,14 @@ impl SelectorMap<Rule> {
}); });
SelectorMap::get_matching_rules_from_hash(element, SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf,
&self.local_name_hash, &self.local_name_hash,
element.get_local_name(), rule_hash_target.get_local_name(),
matching_rules_list, matching_rules_list,
context, context,
flags_setter, flags_setter,
cascade_level); cascade_level);
SelectorMap::get_matching_rules(element, SelectorMap::get_matching_rules(element,
pseudo_element,
parent_bf,
&self.other, &self.other,
matching_rules_list, matching_rules_list,
context, context,
@ -1372,15 +1378,13 @@ impl SelectorMap<Rule> {
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>( fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
element: &E, element: &E,
pseudo_element: Option<(&PseudoElement, ElementState)>,
parent_bf: Option<&BloomFilter>,
hash: &FnvHashMap<Str, Vec<Rule>>, hash: &FnvHashMap<Str, Vec<Rule>>,
key: &BorrowedStr, key: &BorrowedStr,
matching_rules: &mut Vector, matching_rules: &mut Vector,
context: &mut MatchingContext, context: &mut MatchingContext,
flags_setter: &mut F, flags_setter: &mut F,
cascade_level: CascadeLevel) cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>, where E: TElement,
Str: Borrow<BorrowedStr> + Eq + Hash, Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash, BorrowedStr: Eq + Hash,
Vector: VecLike<ApplicableDeclarationBlock>, Vector: VecLike<ApplicableDeclarationBlock>,
@ -1388,8 +1392,6 @@ impl SelectorMap<Rule> {
{ {
if let Some(rules) = hash.get(key) { if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element, SelectorMap::get_matching_rules(element,
pseudo_element,
parent_bf,
rules, rules,
matching_rules, matching_rules,
context, context,
@ -1400,42 +1402,18 @@ impl SelectorMap<Rule> {
/// Adds rules in `rules` that match `element` to the `matching_rules` list. /// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V, F>(element: &E, fn get_matching_rules<E, V, F>(element: &E,
pseudo_element: Option<(&PseudoElement, ElementState)>,
parent_bf: Option<&BloomFilter>,
rules: &[Rule], rules: &[Rule],
matching_rules: &mut V, matching_rules: &mut V,
context: &mut MatchingContext, context: &mut MatchingContext,
flags_setter: &mut F, flags_setter: &mut F,
cascade_level: CascadeLevel) cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>, where E: TElement,
V: VecLike<ApplicableDeclarationBlock>, V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
for rule in rules.iter() { for rule in rules {
debug_assert_eq!(rule.selector.pseudo_element.is_some(),
pseudo_element.is_some(),
"Testing pseudo-elements against the wrong map");
if let Some((pseudo, pseudo_state)) = pseudo_element {
let pseudo_selector =
rule.selector.pseudo_element.as_ref().unwrap();
debug_assert_eq!(pseudo_selector.pseudo_element(), pseudo,
"Testing pseudo-element against the wrong entry");
let state = pseudo_selector.state();
// NB: We only allow a subset of the flags here, so using
// contains for them is fine, (and it's necessary, to handle
// multiple state flags properly).
if !state.is_empty() && !pseudo_state.contains(state) {
continue;
}
}
if matches_selector(&rule.selector.inner, if matches_selector(&rule.selector.inner,
element, element,
parent_bf,
context, context,
flags_setter) { flags_setter) {
matching_rules.push( matching_rules.push(
@ -1593,45 +1571,70 @@ impl<T> SelectorMap<T> where T: Clone + Borrow<SelectorInner<SelectorImpl>> {
} }
} }
/// Searches the selector from right to left, beginning to the left of the
/// ::pseudo-element (if any), and ending at the first combinator.
///
/// The first non-None value returned from |f| is returned.
///
/// Effectively, pseudo-elements are ignored, given only state pseudo-classes
/// may appear before them.
fn find_from_right<F, R>(selector: &SelectorInner<SelectorImpl>, mut f: F) -> Option<R>
where F: FnMut(&Component<SelectorImpl>) -> Option<R>,
{
let mut iter = selector.complex.iter();
for ss in &mut iter {
if let Some(r) = f(ss) {
return Some(r)
}
}
if iter.next_sequence() == Some(Combinator::PseudoElement) {
for ss in &mut iter {
if let Some(r) = f(ss) {
return Some(r)
}
}
}
None
}
/// Retrieve the first ID name in the selector, or None otherwise. /// Retrieve the first ID name in the selector, or None otherwise.
pub fn get_id_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> { pub fn get_id_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> {
for ss in selector.complex.iter() { find_from_right(selector, |ss| {
// TODO(pradeep): Implement case-sensitivity based on the // TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode. // document type and quirks mode.
if let Component::ID(ref id) = *ss { if let Component::ID(ref id) = *ss {
return Some(id.clone()); return Some(id.clone());
} }
} None
})
None
} }
/// Retrieve the FIRST class name in the selector, or None otherwise. /// Retrieve the FIRST class name in the selector, or None otherwise.
pub fn get_class_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> { pub fn get_class_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> {
for ss in selector.complex.iter() { find_from_right(selector, |ss| {
// TODO(pradeep): Implement case-sensitivity based on the // TODO(pradeep): Implement case-sensitivity based on the
// document type and quirks mode. // document type and quirks mode.
if let Component::Class(ref class) = *ss { if let Component::Class(ref class) = *ss {
return Some(class.clone()); return Some(class.clone());
} }
} None
})
None
} }
/// Retrieve the name if it is a type selector, or None otherwise. /// Retrieve the name if it is a type selector, or None otherwise.
pub fn get_local_name(selector: &SelectorInner<SelectorImpl>) pub fn get_local_name(selector: &SelectorInner<SelectorImpl>)
-> Option<LocalNameSelector<SelectorImpl>> { -> Option<LocalNameSelector<SelectorImpl>> {
for ss in selector.complex.iter() { find_from_right(selector, |ss| {
if let Component::LocalName(ref n) = *ss { if let Component::LocalName(ref n) = *ss {
return Some(LocalNameSelector { return Some(LocalNameSelector {
name: n.name.clone(), name: n.name.clone(),
lower_name: n.lower_name.clone(), lower_name: n.lower_name.clone(),
}) })
} }
} None
})
None
} }
/// A rule, that wraps a style rule, but represents a single selector of the /// A rule, that wraps a style rule, but represents a single selector of the
@ -1661,7 +1664,7 @@ impl Borrow<SelectorInner<SelectorImpl>> for Rule {
impl Rule { impl Rule {
/// Returns the specificity of the rule. /// Returns the specificity of the rule.
pub fn specificity(&self) -> u32 { pub fn specificity(&self) -> u32 {
self.selector.specificity self.selector.specificity()
} }
fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock { fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock {

Просмотреть файл

@ -1068,7 +1068,6 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard,
d.stylist.lazily_compute_pseudo_element_style(&guards, d.stylist.lazily_compute_pseudo_element_style(&guards,
&element, &element,
&pseudo, &pseudo,
ElementState::empty(),
base, base,
&metrics) &metrics)
.map(|s| s.values().clone()) .map(|s| s.values().clone())

Просмотреть файл

@ -90,8 +90,8 @@ fn test_parse_stylesheet() {
}))), }))),
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![ selectors: SelectorList(vec![
Selector { Selector::new_for_unit_testing(
inner: SelectorInner::from_vec(vec![ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::LocalName(LocalName { Component::LocalName(LocalName {
name: local_name!("input"), name: local_name!("input"),
@ -106,9 +106,8 @@ fn test_parse_stylesheet() {
}), }),
}, "hidden".to_owned(), CaseSensitivity::CaseInsensitive) }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
]), ]),
pseudo_element: None, (0 << 20) + (1 << 10) + (1 << 0)
specificity: (0 << 20) + (1 << 10) + (1 << 0), ),
},
]), ]),
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![ block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::none), (PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
@ -124,28 +123,26 @@ fn test_parse_stylesheet() {
}))), }))),
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![ selectors: SelectorList(vec![
Selector { Selector::new_for_unit_testing(
inner: SelectorInner::from_vec(vec![ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::LocalName(LocalName { Component::LocalName(LocalName {
name: local_name!("html"), name: local_name!("html"),
lower_name: local_name!("html"), lower_name: local_name!("html"),
}), }),
]), ]),
pseudo_element: None, (0 << 20) + (0 << 10) + (1 << 0)
specificity: (0 << 20) + (0 << 10) + (1 << 0), ),
}, Selector::new_for_unit_testing(
Selector { SelectorInner::from_vec(vec![
inner: SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::LocalName(LocalName { Component::LocalName(LocalName {
name: local_name!("body"), name: local_name!("body"),
lower_name: local_name!("body"), lower_name: local_name!("body"),
}), }),
]), ]),
pseudo_element: None, (0 << 20) + (0 << 10) + (1 << 0)
specificity: (0 << 20) + (0 << 10) + (1 << 0), ),
},
]), ]),
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![ block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::block), (PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
@ -158,17 +155,16 @@ fn test_parse_stylesheet() {
}))), }))),
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule { CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![ selectors: SelectorList(vec![
Selector { Selector::new_for_unit_testing(
inner: SelectorInner::from_vec(vec![ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::ID(Atom::from("d1")), Component::ID(Atom::from("d1")),
Component::Combinator(Combinator::Child), Component::Combinator(Combinator::Child),
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")), Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::Class(Atom::from("ok")), Component::Class(Atom::from("ok")),
]), ]),
pseudo_element: None, (1 << 20) + (1 << 10) + (0 << 0)
specificity: (1 << 20) + (1 << 10) + (0 << 0), ),
},
]), ]),
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![ block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
(PropertyDeclaration::BackgroundColor( (PropertyDeclaration::BackgroundColor(

Просмотреть файл

@ -12,8 +12,8 @@ fn size_of_selectors_dummy_types() {
assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>()); assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>()); assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());
assert_eq!(size_of::<dummies::PseudoElementSelector>(), size_of::<real::PseudoElementSelector>()); assert_eq!(size_of::<dummies::PseudoElement>(), size_of::<real::PseudoElement>());
assert_eq!(align_of::<dummies::PseudoElementSelector>(), align_of::<real::PseudoElementSelector>()); assert_eq!(align_of::<dummies::PseudoElement>(), align_of::<real::PseudoElement>());
assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>()); assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>());
assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>()); assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());