aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-05-17 05:40:14 -0500
committerGitHub <noreply@github.com>2017-05-17 05:40:14 -0500
commitd8b7013fcddff79a9c879077e1a564d83201359c (patch)
tree88a16d0317249297479192f769217f69a8b39895
parenta7b77306f985b79d3b8b4d9b2cb51fa38f45bd39 (diff)
parent522f8489d6fca474e6aad1d341ee84a99c130738 (diff)
downloadservo-d8b7013fcddff79a9c879077e1a564d83201359c.tar.gz
servo-d8b7013fcddff79a9c879077e1a564d83201359c.zip
Auto merge of #16900 - emilio:pseudos, r=bholley
Bug 1364850: Move PseudoElement to be just another combinator in selectors. r=bholley On top of https://github.com/servo/servo/pull/16890. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16900) <!-- Reviewable:end -->
-rw-r--r--components/script/dom/element.rs20
-rw-r--r--components/script/dom/node.rs10
-rw-r--r--components/script/layout_wrapper.rs16
-rw-r--r--components/script_layout_interface/wrapper_traits.rs2
-rw-r--r--components/selectors/gecko_like_types.rs3
-rw-r--r--components/selectors/matching.rs152
-rw-r--r--components/selectors/parser.rs400
-rw-r--r--components/selectors/size_of_tests.rs13
-rw-r--r--components/selectors/tree.rs13
-rw-r--r--components/style/gecko/pseudo_element.rs14
-rw-r--r--components/style/gecko/selector_parser.rs92
-rw-r--r--components/style/gecko/wrapper.rs24
-rw-r--r--components/style/matching.rs50
-rw-r--r--components/style/restyle_hints.rs158
-rw-r--r--components/style/servo/selector_parser.rs31
-rw-r--r--components/style/stylist.rs177
-rw-r--r--ports/geckolib/glue.rs1
-rw-r--r--tests/unit/style/stylesheets.rs36
-rw-r--r--tests/unit/stylo/size_of.rs4
19 files changed, 675 insertions, 541 deletions
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index 011b87aabf1..6b844dd83a2 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -86,7 +86,7 @@ use net_traits::request::CorsSettings;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType;
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::parser::{AttrSelector, NamespaceConstraint};
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::restyle_hints::RESTYLE_SELF;
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::sink::Push;
use style::stylearc::Arc;
@@ -2058,7 +2058,8 @@ impl ElementMethods for Element {
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
Err(()) => Err(Error::Syntax),
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>();
for element in root.inclusive_ancestors() {
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));
}
}
@@ -2386,6 +2387,15 @@ impl<'a> ::selectors::Element for Root<Element> {
self.upcast::<Node>().GetParentElement()
}
+ fn match_pseudo_element(&self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext)
+ -> bool
+ {
+ false
+ }
+
+
fn first_child_element(&self) -> Option<Root<Element>> {
self.node.child_elements().next()
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 8c8831801ef..1ad28974f3d 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -70,7 +70,7 @@ use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddr
use script_layout_interface::message::Msg;
use script_traits::DocumentActivity;
use script_traits::UntrustedNodeAddress;
-use selectors::matching::matches_selector_list;
+use selectors::matching::{matches_selector_list, MatchingContext, MatchingMode};
use selectors::parser::SelectorList;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
@@ -346,11 +346,14 @@ impl<'a> Iterator for QuerySelectorIterator {
fn next(&mut self) -> Option<Root<Node>> {
let selectors = &self.selectors.0;
+
// TODO(cgaebel): Is it worth it to build a bloom filter here
// (instead of passing `None`)? Probably.
+ let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
+
self.iterator.by_ref().filter_map(|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));
}
}
@@ -717,8 +720,9 @@ impl Node {
Err(()) => Err(Error::Syntax),
// Step 3.
Ok(selectors) => {
+ let mut ctx = MatchingContext::new(MatchingMode::Normal, None);
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)
}))
}
}
diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs
index 7802d446114..4cf1789beef 100644
--- a/components/script/layout_wrapper.rs
+++ b/components/script/layout_wrapper.rs
@@ -652,6 +652,14 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
self.element.namespace()
}
+ fn match_pseudo_element(&self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext)
+ -> bool
+ {
+ false
+ }
+
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
_: &mut MatchingContext,
@@ -1150,6 +1158,14 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
self.element.get_namespace()
}
+ fn match_pseudo_element(&self,
+ _pseudo: &PseudoElement,
+ _context: &mut MatchingContext)
+ -> bool
+ {
+ false
+ }
+
fn match_non_ts_pseudo_class<F>(&self,
_: &NonTSPseudoClass,
_: &mut MatchingContext,
diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs
index 4f2870aace0..2ba72231269 100644
--- a/components/script_layout_interface/wrapper_traits.rs
+++ b/components/script_layout_interface/wrapper_traits.rs
@@ -20,7 +20,6 @@ use style::context::SharedStyleContext;
use style::data::ElementData;
use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode};
use style::dom::OpaqueNode;
-use style::element_state::ElementState;
use style::font_metrics::ServoMetricsProvider;
use style::properties::{CascadeFlags, ServoComputedValues};
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
@@ -435,7 +434,6 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
&context.guards,
unsafe { &self.unsafe_get() },
&style_pseudo,
- ElementState::empty(),
data.styles().primary.values(),
&ServoMetricsProvider);
data.styles_mut().cached_pseudos
diff --git a/components/selectors/gecko_like_types.rs b/components/selectors/gecko_like_types.rs
index bbb78823c50..9ffffaf2aed 100644
--- a/components/selectors/gecko_like_types.rs
+++ b/components/selectors/gecko_like_types.rs
@@ -19,9 +19,6 @@ pub enum PseudoElement {
B,
}
-#[derive(Eq, PartialEq, Clone, Debug)]
-pub struct PseudoElementSelector(PseudoElement, u64);
-
#[derive(Eq, PartialEq, Clone, Debug, Default)]
pub struct Atom(usize);
diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs
index c4e19f47fd3..9c60e6b78d7 100644
--- a/components/selectors/matching.rs
+++ b/components/selectors/matching.rs
@@ -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
/// used across many selectors for an element, so it's not appropriate for
/// transient data that applies to only a single selector.
-#[derive(Default)]
-pub struct MatchingContext {
+pub struct MatchingContext<'a> {
/// Output that records certains relations between elements noticed during
/// matching (and also extended after matching).
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>],
element: &E,
- parent_bf: Option<&BloomFilter>)
+ context: &mut MatchingContext)
-> bool
where E: Element
{
selector_list.iter().any(|selector| {
- selector.pseudo_element.is_none() &&
matches_selector(&selector.inner,
element,
- parent_bf,
- &mut MatchingContext::default(),
+ context,
&mut |_, _| {})
})
}
@@ -115,27 +154,6 @@ fn may_match<E>(sel: &SelectorInner<E::Impl>,
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,
///
/// NotMatchedAndRestartFromClosestLaterSibling
@@ -186,16 +204,65 @@ enum SelectorMatchingResult {
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.
-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,
context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
- where E: Element,
- F: FnMut(&E, ElementSelectorFlags),
+ where E: Element,
+ 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,
context,
flags_setter) {
@@ -229,12 +296,19 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
match combinator {
None => SelectorMatchingResult::Matched,
Some(c) => {
- let (mut next_element, candidate_not_found) = if siblings {
- (element.prev_sibling_element(),
- SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
- } else {
- (element.parent_element(),
- SelectorMatchingResult::NotMatchedGlobally)
+ let (mut next_element, candidate_not_found) = match c {
+ Combinator::NextSibling | Combinator::LaterSibling => {
+ (element.prev_sibling_element(),
+ SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
+ }
+ Combinator::Child | Combinator::Descendant => {
+ (element.parent_element(),
+ SelectorMatchingResult::NotMatchedGlobally)
+ }
+ Combinator::PseudoElement => {
+ (element.pseudo_element_originating_element(),
+ SelectorMatchingResult::NotMatchedGlobally)
+ }
};
loop {
@@ -253,6 +327,7 @@ fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Im
// Upgrade the failure status to
// NotMatchedAndRestartFromClosestDescendant.
+ (_, Combinator::PseudoElement) |
(_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
// Return the status directly.
@@ -306,6 +381,9 @@ fn matches_simple_selector<E, F>(
match *selector {
Component::Combinator(_) => unreachable!(),
+ Component::PseudoElement(ref pseudo) => {
+ element.match_pseudo_element(pseudo, context)
+ }
Component::LocalName(LocalName { ref name, ref lower_name }) => {
let name = if element.is_html_element_in_html_document() { lower_name } else { name };
element.get_local_name() == name.borrow()
diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs
index 34ed4418b3d..01f1eafc07c 100644
--- a/components/selectors/parser.rs
+++ b/components/selectors/parser.rs
@@ -16,6 +16,22 @@ use std::slice;
use tree::SELECTOR_WHITESPACE;
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 {
(
[ $( $InSelector: tt )* ]
@@ -60,7 +76,7 @@ macro_rules! with_all_bounds {
type NonTSPseudoClass: $($CommonBounds)* + Sized + ToCss + SelectorMethods<Impl = Self>;
/// pseudo-elements
- type PseudoElementSelector: $($CommonBounds)* + Sized + ToCss;
+ type PseudoElement: $($CommonBounds)* + PseudoElement<Impl = Self>;
}
}
}
@@ -97,8 +113,8 @@ pub trait Parser {
Err(())
}
- fn parse_pseudo_element(&self, _name: Cow<str>, _input: &mut CssParser)
- -> Result<<Self::Impl as SelectorImpl>::PseudoElementSelector, ()> {
+ fn parse_pseudo_element(&self, _name: Cow<str>)
+ -> Result<<Self::Impl as SelectorImpl>::PseudoElement, ()> {
Err(())
}
@@ -178,18 +194,57 @@ impl<Impl: SelectorImpl> SelectorInner<Impl> {
#[derive(PartialEq, Eq, Clone)]
pub struct Selector<Impl: SelectorImpl> {
pub inner: SelectorInner<Impl>,
- pub pseudo_element: Option<Impl::PseudoElementSelector>,
- pub specificity: u32,
+ specificity_and_flags: 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> {
+ 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.
///
/// Used for "pre-computed" pseudo-elements in components/style/stylist.rs
pub fn is_universal(&self) -> bool {
self.inner.complex.iter_raw().all(|c| matches!(*c,
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> {
type Item = &'a Component<Impl>;
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() {
None => None,
Some(&Component::Combinator(c)) => {
@@ -384,12 +440,12 @@ impl<'a, Impl: 'a + SelectorImpl> AncestorIter<'a, Impl> {
result
}
- /// Skips a sequence of simple selectors and all subsequent sequences until an
- /// ancestor combinator is reached.
+ /// Skips a sequence of simple selectors and all subsequent sequences until
+ /// a non-pseudo-element ancestor combinator is reached.
fn skip_until_ancestor(&mut self) {
loop {
- while let Some(_) = self.0.next() {}
- if self.0.next_sequence().map_or(true, |x| x.is_ancestor()) {
+ while self.0.next().is_some() {}
+ if self.0.next_sequence().map_or(true, |x| matches!(x, Combinator::Child | Combinator::Descendant)) {
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.
if let Some(combinator) = self.0.next_sequence() {
- if !combinator.is_ancestor() {
+ if !matches!(combinator, Combinator::Child | Combinator::Descendant) {
self.skip_until_ancestor();
}
}
@@ -422,12 +478,24 @@ pub enum Combinator {
Descendant, // space
NextSibling, // +
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 {
/// Returns true if this combinator is a child or descendant combinator.
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.
@@ -491,6 +559,7 @@ pub enum Component<Impl: SelectorImpl> {
LastOfType,
OnlyOfType,
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 {
f.write_str("Selector(")?;
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> {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
- self.inner.complex.to_css(dest)?;
- if let Some(ref pseudo) = self.pseudo_element {
- pseudo.to_css(dest)?;
- }
- Ok(())
+ self.inner.complex.to_css(dest)
}
}
@@ -646,6 +711,7 @@ impl ToCss for Combinator {
Combinator::Descendant => dest.write_str(" "),
Combinator::NextSibling => 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) => {
c.to_css(dest)
}
+ PseudoElement(ref p) => {
+ p.to_css(dest)
+ }
ID(ref s) => {
dest.write_char('#')?;
display_to_css_identifier(s, dest)
@@ -841,25 +910,23 @@ impl From<Specificity> for u32 {
}
}
-fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>,
- pseudo_element: Option<&Impl::PseudoElementSelector>)
- -> u32
- where Impl: SelectorImpl {
- let mut specificity = complex_selector_specificity(complex_selector);
- if pseudo_element.is_some() {
- specificity.element_selectors += 1;
- }
- specificity.into()
+fn specificity<Impl>(complex_selector: &ComplexSelector<Impl>) -> u32
+ where Impl: SelectorImpl
+{
+ complex_selector_specificity(complex_selector).into()
}
fn complex_selector_specificity<Impl>(selector: &ComplexSelector<Impl>)
-> Specificity
- where Impl: SelectorImpl {
+ where Impl: SelectorImpl
+{
fn simple_selector_specificity<Impl>(simple_selector: &Component<Impl>,
specificity: &mut Specificity)
- where Impl: SelectorImpl {
+ where Impl: SelectorImpl
+ {
match *simple_selector {
Component::Combinator(..) => unreachable!(),
+ Component::PseudoElement(..) |
Component::LocalName(..) => {
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>, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl
{
- let (complex, pseudo_element) =
- parse_complex_selector_and_pseudo_element(parser, input)?;
+ let (complex, has_pseudo_element) = parse_complex_selector(parser, input)?;
+ let mut specificity = specificity(&complex);
+ if has_pseudo_element {
+ specificity |= HAS_PSEUDO_BIT;
+ }
Ok(Selector {
- specificity: specificity(&complex, pseudo_element.as_ref()),
+ specificity_and_flags: specificity,
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.
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,
input: &mut CssParser)
- -> Result<(ComplexSelector<Impl>, Option<Impl::PseudoElementSelector>), ()>
+ -> Result<(ComplexSelector<Impl>, bool), ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl
{
let mut sequence = ParseVec::new();
- let mut pseudo_element;
+ let mut parsed_pseudo_element;
'outer_loop: loop {
// Parse a sequence of simple selectors.
- pseudo_element = parse_compound_selector(parser, input, &mut sequence,
- /* inside_negation = */ false)?;
- if pseudo_element.is_some() {
+ parsed_pseudo_element =
+ parse_compound_selector(parser, input, &mut sequence,
+ /* inside_negation = */ false)?;
+ if parsed_pseudo_element {
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()));
- Ok((complex, pseudo_element))
+ Ok((complex, parsed_pseudo_element))
}
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, ()>
where P: Parser<Impl=Impl>
{
- let (complex, pseudo_element) =
- parse_complex_selector_and_pseudo_element(parser, input)?;
- if pseudo_element.is_some() {
+ let (complex, has_pseudo_element) =
+ parse_complex_selector(parser, input)?;
+ if has_pseudo_element {
return Err(())
}
Ok(complex)
@@ -1062,7 +1137,7 @@ fn parse_type_selector<P, Impl>(parser: &P, input: &mut CssParser, sequence: &mu
#[derive(Debug)]
enum SimpleSelectorParseResult<Impl: SelectorImpl> {
SimpleSelector(Component<Impl>),
- PseudoElement(Impl::PseudoElementSelector),
+ PseudoElement(Impl::PseudoElement),
}
#[derive(Debug)]
@@ -1295,13 +1370,15 @@ fn single_simple_selector<Impl: SelectorImpl>(v: &[Component<Impl>]) -> bool {
/// : [ type_selector | universal ] [ 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>(
parser: &P,
input: &mut CssParser,
mut sequence: &mut ParseVec<Impl>,
inside_negation: bool)
- -> Result<Option<Impl::PseudoElementSelector>, ()>
+ -> Result<bool, ()>
where P: Parser<Impl=Impl>, Impl: SelectorImpl
{
// Consume any leading whitespace.
@@ -1328,7 +1405,7 @@ fn parse_compound_selector<P, Impl>(
empty = false;
}
- let mut pseudo_element = None;
+ let mut pseudo = false;
loop {
match parse_one_simple_selector(parser, input, inside_negation)? {
None => break,
@@ -1337,7 +1414,41 @@ fn parse_compound_selector<P, Impl>(
empty = false
}
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;
break
}
@@ -1347,7 +1458,7 @@ fn parse_compound_selector<P, Impl>(
// An empty selector is invalid.
Err(())
} 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("first-line") ||
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)))
} else {
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) => {
match input.next_including_whitespace() {
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)))
}
_ => Err(())
@@ -1478,6 +1589,7 @@ fn parse_simple_pseudo_class<P, Impl>(parser: &P, name: Cow<str>) -> Result<Comp
#[cfg(test)]
pub mod tests {
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
+ use parser;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
@@ -1486,6 +1598,7 @@ pub mod tests {
#[derive(PartialEq, Clone, Debug, Eq)]
pub enum PseudoClass {
Hover,
+ Active,
Lang(String),
}
@@ -1495,10 +1608,23 @@ pub mod tests {
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 {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match *self {
PseudoClass::Hover => dest.write_str(":hover"),
+ PseudoClass::Active => dest.write_str(":active"),
PseudoClass::Lang(ref lang) => {
dest.write_str(":lang(")?;
serialize_identifier(lang, dest)?;
@@ -1543,7 +1669,7 @@ pub mod tests {
type BorrowedLocalName = DummyAtom;
type BorrowedNamespaceUrl = DummyAtom;
type NonTSPseudoClass = PseudoClass;
- type PseudoElementSelector = PseudoElement;
+ type PseudoElement = PseudoElement;
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
@@ -1580,6 +1706,7 @@ pub mod tests {
-> Result<PseudoClass, ()> {
match_ignore_ascii_case! { &name,
"hover" => Ok(PseudoClass::Hover),
+ "active" => Ok(PseudoClass::Active),
_ => Err(())
}
}
@@ -1593,8 +1720,7 @@ pub mod tests {
}
}
- fn parse_pseudo_element(&self, name: Cow<str>, _input: &mut CssParser)
- -> Result<PseudoElement, ()> {
+ fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
match_ignore_ascii_case! { &name,
"before" => Ok(PseudoElement::Before),
"after" => Ok(PseudoElement::After),
@@ -1647,11 +1773,9 @@ pub mod tests {
inner: SelectorInner::from_vec(vec!(
Component::LocalName(LocalName {
name: DummyAtom::from("EeÉ"),
- lower_name: DummyAtom::from("eeÉ")
- }),
- )),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ lower_name: DummyAtom::from("eeÉ") })),
+ ),
+ specificity_and_flags: specificity(0, 0, 1),
}))));
assert_eq!(parse("|e"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
@@ -1661,8 +1785,7 @@ pub mod tests {
lower_name: DummyAtom::from("e")
}),
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ specificity_and_flags: specificity(0, 0, 1),
}))));
// https://github.com/servo/servo/issues/16020
assert_eq!(parse("*|e"), Ok(SelectorList(vec!(Selector {
@@ -1673,44 +1796,38 @@ pub mod tests {
lower_name: DummyAtom::from("e")
}),
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ specificity_and_flags: specificity(0, 0, 1),
}))));
assert_eq!(parse("*"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
Component::ExplicitUniversalType,
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse("|*"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
Component::ExplicitNoNamespace,
Component::ExplicitUniversalType,
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse("*|*"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
Component::ExplicitAnyNamespace,
Component::ExplicitUniversalType,
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse(".foo:lang(en-US)"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec![
Component::Class(DummyAtom::from("foo")),
Component::NonTSPseudoClass(PseudoClass::Lang("en-US".to_owned()))
]),
- pseudo_element: None,
- specificity: specificity(0, 2, 0),
+ specificity_and_flags: specificity(0, 2, 0),
}))));
assert_eq!(parse("#bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::ID(DummyAtom::from("bar")))),
- pseudo_element: None,
- specificity: specificity(1, 0, 0),
+ specificity_and_flags: specificity(1, 0, 0),
}))));
assert_eq!(parse("e.foo#bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::LocalName(LocalName {
@@ -1718,8 +1835,7 @@ pub mod tests {
lower_name: DummyAtom::from("e") }),
Component::Class(DummyAtom::from("foo")),
Component::ID(DummyAtom::from("bar")))),
- pseudo_element: None,
- specificity: specificity(1, 1, 1),
+ specificity_and_flags: specificity(1, 1, 1),
}))));
assert_eq!(parse("e.foo #bar"), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
@@ -1731,8 +1847,7 @@ pub mod tests {
Component::Combinator(Combinator::Descendant),
Component::ID(DummyAtom::from("bar")),
)),
- pseudo_element: None,
- specificity: specificity(1, 1, 1),
+ specificity_and_flags: specificity(1, 1, 1),
}))));
// Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652
@@ -1746,8 +1861,7 @@ pub mod tests {
prefix: None,
url: "".into(),
}) }))),
- pseudo_element: None,
- specificity: specificity(0, 1, 0),
+ specificity_and_flags: specificity(0, 1, 0),
}))));
assert_eq!(parse_ns("svg|circle", &parser), Err(()));
parser.ns_prefixes.insert(DummyAtom("svg".into()), DummyAtom(SVG.into()));
@@ -1760,8 +1874,7 @@ pub mod tests {
lower_name: DummyAtom::from("circle"),
})
]),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ specificity_and_flags: specificity(0, 0, 1),
}])));
assert_eq!(parse_ns("svg|*", &parser), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec(
@@ -1769,8 +1882,7 @@ pub mod tests {
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
Component::ExplicitUniversalType,
]),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}])));
// Default namespace does not apply to attribute selectors
// https://github.com/mozilla/servo/pull/1652
@@ -1790,8 +1902,7 @@ pub mod tests {
}),
}),
]),
- pseudo_element: None,
- specificity: specificity(0, 1, 0),
+ specificity_and_flags: specificity(0, 1, 0),
}))));
// Default namespace does apply to type selectors
assert_eq!(parse_ns("e", &parser), Ok(SelectorList(vec!(Selector {
@@ -1802,8 +1913,7 @@ pub mod tests {
name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e") }),
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ specificity_and_flags: specificity(0, 0, 1),
}))));
assert_eq!(parse_ns("*", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(
@@ -1811,8 +1921,7 @@ pub mod tests {
Component::DefaultNamespace(MATHML.into()),
Component::ExplicitUniversalType,
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse_ns("*|*", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(
@@ -1820,8 +1929,7 @@ pub mod tests {
Component::ExplicitAnyNamespace,
Component::ExplicitUniversalType,
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
// Default namespace applies to universal and type selectors inside :not and :matches,
// but not otherwise.
@@ -1832,8 +1940,7 @@ pub mod tests {
Component::Class(DummyAtom::from("cl"))
].into_boxed_slice()),
)),
- pseudo_element: None,
- specificity: specificity(0, 1, 0),
+ specificity_and_flags: specificity(0, 1, 0),
}))));
assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
@@ -1843,8 +1950,7 @@ pub mod tests {
Component::ExplicitUniversalType,
].into_boxed_slice()),
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse_ns(":not(e)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(
@@ -1857,8 +1963,7 @@ pub mod tests {
}),
].into_boxed_slice())
)),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ specificity_and_flags: specificity(0, 0, 1),
}))));
assert_eq!(parse("[attr |= \"foo\"]"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec(
@@ -1872,15 +1977,42 @@ pub mod tests {
}),
}, DummyAtom::from("foo"))
]),
- pseudo_element: None,
- specificity: specificity(0, 1, 0),
+ specificity_and_flags: specificity(0, 1, 0),
}])));
// https://github.com/mozilla/servo/issues/1723
assert_eq!(parse("::before"), Ok(SelectorList(vec!(Selector {
- inner: SelectorInner::from_vec(vec![]),
- pseudo_element: Some(PseudoElement::Before),
- specificity: specificity(0, 0, 1),
+ inner: SelectorInner::from_vec(
+ vec![
+ 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
assert_eq!(parse(":: before"), Err(()));
assert_eq!(parse("div ::after"), Ok(SelectorList(vec!(Selector {
@@ -1890,9 +2022,10 @@ pub mod tests {
name: DummyAtom::from("div"),
lower_name: DummyAtom::from("div") }),
Component::Combinator(Combinator::Descendant),
+ Component::Combinator(Combinator::PseudoElement),
+ Component::PseudoElement(PseudoElement::After),
]),
- pseudo_element: Some(PseudoElement::After),
- specificity: specificity(0, 0, 2),
+ specificity_and_flags: specificity(0, 0, 2) | HAS_PSEUDO_BIT,
}))));
assert_eq!(parse("#d1 > .ok"), Ok(SelectorList(vec![Selector {
inner: SelectorInner::from_vec(
@@ -1901,8 +2034,7 @@ pub mod tests {
Component::Combinator(Combinator::Child),
Component::Class(DummyAtom::from("ok")),
]),
- pseudo_element: None,
- specificity: (1 << 20) + (1 << 10) + (0 << 0),
+ specificity_and_flags: (1 << 20) + (1 << 10) + (0 << 0),
}])));
parser.default_ns = None;
assert_eq!(parse(":not(#provel.old)"), Err(()));
@@ -1914,8 +2046,7 @@ pub mod tests {
Component::ID(DummyAtom::from("provel")),
].into_boxed_slice()
))),
- pseudo_element: None,
- specificity: specificity(1, 0, 0),
+ specificity_and_flags: specificity(1, 0, 0),
}))));
assert_eq!(parse_ns(":not(svg|circle)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation(
@@ -1927,8 +2058,7 @@ pub mod tests {
}),
].into_boxed_slice()
))),
- pseudo_element: None,
- specificity: specificity(0, 0, 1),
+ specificity_and_flags: specificity(0, 0, 1),
}))));
// https://github.com/servo/servo/issues/16017
assert_eq!(parse_ns(":not(*)", &parser), Ok(SelectorList(vec!(Selector {
@@ -1937,8 +2067,7 @@ pub mod tests {
Component::ExplicitUniversalType,
].into_boxed_slice()
))),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse_ns(":not(|*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation(
@@ -1947,8 +2076,7 @@ pub mod tests {
Component::ExplicitUniversalType,
].into_boxed_slice()
))),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse_ns(":not(*|*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation(
@@ -1957,8 +2085,7 @@ pub mod tests {
Component::ExplicitUniversalType,
].into_boxed_slice()
))),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: specificity(0, 0, 0),
}))));
assert_eq!(parse_ns(":not(svg|*)", &parser), Ok(SelectorList(vec!(Selector {
inner: SelectorInner::from_vec(vec!(Component::Negation(
@@ -1967,11 +2094,40 @@ pub mod tests {
Component::ExplicitUniversalType,
].into_boxed_slice()
))),
- pseudo_element: None,
- specificity: specificity(0, 0, 0),
+ specificity_and_flags: 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 {
seen: Vec<String>,
}
@@ -1992,5 +2148,9 @@ pub mod tests {
let mut test_visitor = TestVisitor { seen: vec![], };
parse(":not(:hover) ~ label").unwrap().0[0].visit(&mut test_visitor);
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()));
}
}
diff --git a/components/selectors/size_of_tests.rs b/components/selectors/size_of_tests.rs
index 1d06278abd5..c03a0c80db5 100644
--- a/components/selectors/size_of_tests.rs
+++ b/components/selectors/size_of_tests.rs
@@ -3,14 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use cssparser::ToCss;
+use gecko_like_types;
use gecko_like_types::*;
+use parser;
use parser::*;
use precomputed_hash::PrecomputedHash;
use std::fmt;
use visitor::SelectorVisitor;
-size_of_test!(size_of_selector, Selector<Impl>, 72);
-size_of_test!(size_of_pseudo_element, PseudoElementSelector, 16);
+size_of_test!(size_of_selector, Selector<Impl>, 48);
+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_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_pseudo_class, PseudoClass, 24);
+impl parser::PseudoElement for gecko_like_types::PseudoElement {
+ type Impl = Impl;
+}
// Boilerplate
@@ -31,7 +36,7 @@ impl SelectorImpl for Impl {
type BorrowedLocalName = Atom;
type BorrowedNamespaceUrl = Atom;
type NonTSPseudoClass = PseudoClass;
- type PseudoElementSelector = PseudoElementSelector;
+ type PseudoElement = gecko_like_types::PseudoElement;
}
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!() }
}
-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!() }
}
diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs
index 6514a786673..e5a4539e3cc 100644
--- a/components/selectors/tree.rs
+++ b/components/selectors/tree.rs
@@ -123,6 +123,14 @@ impl<T> MatchAttr for T where T: MatchAttrGeneric, T::Impl: SelectorImpl<AttrVal
pub trait Element: MatchAttr + Sized {
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
fn first_child_element(&self) -> Option<Self>;
@@ -145,6 +153,11 @@ pub trait Element: MatchAttr + Sized {
flags_setter: &mut F) -> bool
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 has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs
index f01c182802e..b15e17e6f38 100644
--- a/components/style/gecko/pseudo_element.rs
+++ b/components/style/gecko/pseudo_element.rs
@@ -10,12 +10,24 @@
use cssparser::ToCss;
use gecko_bindings::structs::{self, CSSPseudoElementType};
-use selector_parser::PseudoElementCascadeType;
+use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
use std::fmt;
use string_cache::Atom;
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 {
/// Returns the kind of cascade type that a given pseudo is going to use.
///
diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs
index 22abaf63b67..787ecd5b2cb 100644
--- a/components/style/gecko/selector_parser.rs
+++ b/components/style/gecko/selector_parser.rs
@@ -5,7 +5,6 @@
//! Gecko-specific bits for selector-parsing.
use cssparser::{Parser, ToCss};
-use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
@@ -132,7 +131,7 @@ impl NonTSPseudoClass {
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos
///
/// 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 |
NonTSPseudoClass::Active |
NonTSPseudoClass::Focus)
@@ -195,58 +194,6 @@ impl NonTSPseudoClass {
#[derive(Clone, Debug, PartialEq, Eq)]
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 {
type AttrValue = Atom;
type Identifier = Atom;
@@ -257,7 +204,7 @@ impl ::selectors::SelectorImpl for SelectorImpl {
type BorrowedNamespaceUrl = WeakNamespace;
type BorrowedLocalName = WeakAtom;
- type PseudoElementSelector = PseudoElementSelector;
+ type PseudoElement = PseudoElement;
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, ()> {
- let pseudo =
- match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
- 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 parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
+ PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
+ .ok_or(())
}
fn default_namespace(&self) -> Option<Namespace> {
diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs
index c171404d3eb..cef8962df3c 100644
--- a/components/style/gecko/wrapper.rs
+++ b/components/style/gecko/wrapper.rs
@@ -64,7 +64,7 @@ use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::ElementExt;
use selectors::Element;
-use selectors::matching::{ElementSelectorFlags, MatchingContext};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use shared_lock::Locked;
use sink::Push;
@@ -1055,6 +1055,11 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
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> {
let mut child = self.as_node().first_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> {
let ptr = unsafe {
bindings::Gecko_AtomAttrValue(self.0,
@@ -1378,8 +1397,9 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
impl<'le> ElementExt for GeckoElement<'le> {
#[inline]
fn is_link(&self) -> bool {
+ let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
- &mut MatchingContext::default(),
+ &mut context,
&mut |_, _| {})
}
diff --git a/components/style/matching.rs b/components/style/matching.rs
index 24cb5c2abe7..4765e89004c 100644
--- a/components/style/matching.rs
+++ b/components/style/matching.rs
@@ -15,7 +15,6 @@ use cascade_info::CascadeInfo;
use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use dom::{AnimationRules, SendElement, TElement, TNode};
-use element_state::ElementState;
use font_metrics::FontMetricsProvider;
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
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 selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
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 shared_lock::StylesheetGuards;
use sink::ForgetfulSink;
@@ -895,10 +894,10 @@ pub trait MatchMethods : TElement {
sharing: StyleSharingBehavior)
{
// 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,
data,
- &mut primary_matching_context);
+ &mut relations);
// Cascade properties and compute primary values.
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 !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.
@@ -932,7 +931,7 @@ pub trait MatchMethods : TElement {
.style_sharing_candidate_cache
.insert_if_possible(self,
data.styles().primary.values(),
- primary_matching_context.relations,
+ relations,
revalidation_match_results);
}
}
@@ -952,7 +951,7 @@ pub trait MatchMethods : TElement {
fn match_primary(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
- matching_context: &mut MatchingContext)
+ relations: &mut StyleRelations)
-> bool
{
let implemented_pseudo = self.implemented_pseudo_element();
@@ -1004,35 +1003,27 @@ pub trait MatchMethods : TElement {
let animation_rules = self.get_animation_rules();
let bloom = context.thread_local.bloom_filter.filter();
+
let map = &mut context.thread_local.selector_flags;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
self.apply_selector_flags(map, element, flags);
};
- let selector_matching_target = match implemented_pseudo {
- Some(..) => {
- 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,
- };
+ let mut matching_context =
+ MatchingContext::new(MatchingMode::Normal, Some(bloom));
// Compute the primary rule node.
- stylist.push_applicable_declarations(&selector_matching_target,
- Some(bloom),
+ stylist.push_applicable_declarations(self,
+ implemented_pseudo.as_ref(),
style_attribute,
smil_override,
animation_rules,
- pseudo_and_state,
&mut applicable_declarations,
- matching_context,
+ &mut matching_context,
&mut set_selector_flags);
+ *relations = matching_context.relations;
+
let primary_rule_node =
compute_rule_node::<Self>(&stylist.rule_tree,
&mut applicable_declarations,
@@ -1041,8 +1032,8 @@ pub trait MatchMethods : TElement {
return data.set_primary_rules(primary_rule_node);
}
- /// Runs selector matching to (re)compute eager pseudo-element rule nodes for this
- /// element.
+ /// Runs selector matching to (re)compute eager pseudo-element rule nodes
+ /// for this element.
///
/// Returns whether any of the pseudo rule nodes changed (including, but not
/// limited to, cases where we match different pseudos altogether).
@@ -1070,6 +1061,10 @@ pub trait MatchMethods : TElement {
let rule_tree = &stylist.rule_tree;
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.
let mut matches_different_pseudos = 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
// traversing them.
stylist.push_applicable_declarations(self,
- Some(bloom_filter),
+ Some(&pseudo),
None,
None,
AnimationRules(None, None),
- Some((&pseudo, ElementState::empty())),
&mut applicable_declarations,
- &mut MatchingContext::default(),
+ &mut matching_context,
&mut set_selector_flags);
if !applicable_declarations.is_empty() {
diff --git a/components/style/restyle_hints.rs b/components/style/restyle_hints.rs
index d0b9cd665f8..23a3cc9f2c3 100644
--- a/components/style/restyle_hints.rs
+++ b/components/style/restyle_hints.rs
@@ -9,14 +9,13 @@
use Atom;
use dom::TElement;
use element_state::*;
-use fnv::FnvHashMap;
#[cfg(feature = "gecko")]
use gecko_bindings::structs::nsRestyleHint;
#[cfg(feature = "servo")]
use heapsize::HeapSizeOf;
use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap};
use selectors::{Element, MatchAttr};
-use selectors::matching::{ElementSelectorFlags, MatchingContext};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
use selectors::matching::matches_selector;
use selectors::parser::{AttrSelector, Combinator, Component, Selector};
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> {
self.element.parent_element()
.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)
}
}
+
+ 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 {
@@ -507,6 +519,9 @@ fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
match combinator {
None => RESTYLE_SELF,
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::Descendant => RESTYLE_DESCENDANTS,
Combinator::NextSibling => RESTYLE_LATER_SIBLINGS,
@@ -634,13 +649,6 @@ impl SelectorVisitor for SensitivitiesVisitor {
#[derive(Debug)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
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.
dependencies: SelectorMap<Dependency>,
}
@@ -668,34 +676,9 @@ impl DependencySet {
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 !visitor.sensitivities.is_empty() {
- let mut 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 hint = combinator_to_restyle_hint(combinator);
let dep_selector = if sequence_start == 0 {
// Reuse the bloom hashes if this is the base selector.
@@ -724,82 +707,22 @@ impl DependencySet {
pub fn new() -> Self {
DependencySet {
dependencies: SelectorMap::new(),
- pseudo_dependencies: FnvHashMap::default(),
}
}
/// Return the total number of dependencies that this set contains.
pub fn len(&self) -> usize {
- self.dependencies.len() +
- self.pseudo_dependencies.values().fold(0, |acc, val| acc + val.len())
+ self.dependencies.len()
}
/// Clear this dependency set.
pub fn clear(&mut self) {
self.dependencies = SelectorMap::new();
- self.pseudo_dependencies.clear()
}
- fn compute_pseudo_hint<E>(
- &self,
- pseudo: &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>(
+ /// 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)
@@ -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
- .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);
if !dep.sensitivities.sensitive_to(attrs_changed,
state_changes) {
@@ -856,12 +789,12 @@ impl DependencySet {
// been set during original matching for any element that might
// change its matching behavior here.
let matched_then =
- matches_selector(&dep.selector, &snapshot_el, None,
- &mut MatchingContext::default(),
+ matches_selector(&dep.selector, &snapshot_el,
+ &mut matching_context,
&mut |_, _| {});
let matches_now =
- matches_selector(&dep.selector, el, None,
- &mut MatchingContext::default(),
+ matches_selector(&dep.selector, el,
+ &mut matching_context,
&mut |_, _| {});
if matched_then != matches_now {
hint.insert(dep.hint);
@@ -875,21 +808,4 @@ impl DependencySet {
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)
- }
}
diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs
index 6f9ce19f40e..6fd498ee9aa 100644
--- a/components/style/servo/selector_parser.rs
+++ b/components/style/servo/selector_parser.rs
@@ -15,7 +15,7 @@ use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::{Element, MatchAttrGeneric};
-use selectors::matching::MatchingContext;
+use selectors::matching::{MatchingContext, MatchingMode};
use selectors::parser::{AttrSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
@@ -51,6 +51,14 @@ pub enum PseudoElement {
ServoInlineAbsolute,
}
+impl ::selectors::parser::PseudoElement for PseudoElement {
+ type Impl = SelectorImpl;
+
+ fn supports_pseudo_class(&self, _: &NonTSPseudoClass) -> bool {
+ false
+ }
+}
+
impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
use self::PseudoElement::*;
@@ -78,18 +86,6 @@ impl ToCss for PseudoElement {
pub const EAGER_PSEUDO_COUNT: usize = 3;
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.
#[inline]
pub fn eager_index(&self) -> usize {
@@ -264,7 +260,7 @@ impl NonTSPseudoClass {
pub struct SelectorImpl;
impl ::selectors::SelectorImpl for SelectorImpl {
- type PseudoElementSelector = PseudoElement;
+ type PseudoElement = PseudoElement;
type NonTSPseudoClass = NonTSPseudoClass;
type AttrValue = String;
@@ -323,9 +319,7 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
Ok(pseudo_class)
}
- fn parse_pseudo_element(&self,
- name: Cow<str>,
- _input: &mut CssParser)
+ fn parse_pseudo_element(&self, name: Cow<str>)
-> Result<PseudoElement, ()> {
use self::PseudoElement::*;
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 {
fn is_link(&self) -> bool {
+ let mut context = MatchingContext::new(MatchingMode::Normal, None);
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
- &mut MatchingContext::default(),
+ &mut context,
&mut |_, _| {})
}
diff --git a/components/style/stylist.rs b/components/style/stylist.rs
index acef07d054e..644552050b2 100644
--- a/components/style/stylist.rs
+++ b/components/style/stylist.rs
@@ -26,10 +26,9 @@ use properties::PropertyDeclarationBlock;
use restyle_hints::{RestyleHint, DependencySet};
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
-use selectors::Element;
use selectors::bloom::BloomFilter;
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::{SelectorMethods, LocalName as LocalNameSelector};
use selectors::visitor::SelectorVisitor;
@@ -479,9 +478,9 @@ impl Stylist {
rule: &Arc<Locked<StyleRule>>,
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
- .entry(pseudo_selector.pseudo_element().clone())
+ .entry(pseudo.clone())
.or_insert_with(PerPseudoElementSelectorMap::new)
.borrow_for_origin(&stylesheet.origin)
} else {
@@ -525,9 +524,6 @@ impl Stylist {
#[inline]
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));
}
@@ -635,14 +631,13 @@ impl Stylist {
guards: &StylesheetGuards,
element: &E,
pseudo: &PseudoElement,
- pseudo_state: ElementState,
parent: &Arc<ComputedValues>,
font_metrics: &FontMetricsProvider)
-> Option<ComputedStyle>
where E: TElement,
{
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,
None => return None
};
@@ -673,8 +668,7 @@ impl Stylist {
pub fn lazy_pseudo_rules<E>(&self,
guards: &StylesheetGuards,
element: &E,
- pseudo: &PseudoElement,
- pseudo_state: ElementState)
+ pseudo: &PseudoElement)
-> Option<StrongRuleNode>
where E: TElement
{
@@ -708,14 +702,15 @@ impl Stylist {
};
let mut declarations = ApplicableDeclarationList::new();
+ let mut matching_context =
+ MatchingContext::new(MatchingMode::ForStatelessPseudoElement, None);
self.push_applicable_declarations(element,
- None,
+ Some(pseudo),
None,
None,
AnimationRules(None, None),
- Some((pseudo, pseudo_state)),
&mut declarations,
- &mut MatchingContext::default(),
+ &mut matching_context,
&mut set_selector_flags);
if declarations.is_empty() {
return None
@@ -839,16 +834,15 @@ impl Stylist {
pub fn push_applicable_declarations<E, V, F>(
&self,
element: &E,
- parent_bf: Option<&BloomFilter>,
+ pseudo_element: Option<&PseudoElement>,
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules,
- pseudo_element: Option<(&PseudoElement, ElementState)>,
applicable_declarations: &mut V,
context: &mut MatchingContext,
flags_setter: &mut F)
where E: TElement,
- V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
+ V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + ::std::fmt::Debug,
F: FnMut(&E, ElementSelectorFlags),
{
debug_assert!(!self.is_device_dirty);
@@ -857,20 +851,31 @@ impl Stylist {
debug_assert!(cfg!(feature = "gecko") ||
style_attribute.is_none() || pseudo_element.is_none(),
"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 {
- Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(),
+ Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(),
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: {}",
pseudo_element.is_some());
// Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element,
- pseudo_element,
- parent_bf,
+ &rule_hash_target,
applicable_declarations,
context,
flags_setter,
@@ -893,19 +898,25 @@ impl Stylist {
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.
map.user.get_all_matching_rules(element,
- pseudo_element,
- parent_bf,
+ &rule_hash_target,
applicable_declarations,
context,
flags_setter,
CascadeLevel::UserNormal);
debug!("user normal: {:?}", context.relations);
map.author.get_all_matching_rules(element,
- pseudo_element,
- parent_bf,
+ &rule_hash_target,
applicable_declarations,
context,
flags_setter,
@@ -960,7 +971,6 @@ impl Stylist {
ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
}
debug!("transition: {:?}", context.relations);
-
debug!("push_applicable_declarations: shareable: {:?}", context.relations);
}
@@ -993,6 +1003,11 @@ impl Stylist {
where E: TElement,
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
// 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
@@ -1002,8 +1017,7 @@ impl Stylist {
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| {
results.push(matches_selector(selector,
element,
- Some(bloom),
- &mut MatchingContext::default(),
+ &mut matching_context,
flags_setter));
true
});
@@ -1182,6 +1196,7 @@ pub fn needs_revalidation(selector: &Selector<SelectorImpl>) -> bool {
/// Map that contains the CSS rules for a specific PseudoElement
/// (or lack of PseudoElement).
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
+#[derive(Debug)]
struct PerPseudoElementSelectorMap {
/// Rules from user agent stylesheets
user_agent: SelectorMap<Rule>,
@@ -1283,13 +1298,12 @@ impl SelectorMap<Rule> {
/// Sort the Rules at the end to maintain cascading order.
pub fn get_all_matching_rules<E, V, F>(&self,
element: &E,
- pseudo_element: Option<(&PseudoElement, ElementState)>,
- parent_bf: Option<&BloomFilter>,
+ rule_hash_target: &E,
matching_rules_list: &mut V,
context: &mut MatchingContext,
flags_setter: &mut F,
cascade_level: CascadeLevel)
- where E: Element<Impl=SelectorImpl>,
+ where E: TElement,
V: VecLike<ApplicableDeclarationBlock>,
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.
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,
- pseudo_element,
- parent_bf,
&self.id_hash,
&id,
matching_rules_list,
@@ -1311,10 +1323,8 @@ impl SelectorMap<Rule> {
cascade_level)
}
- element.each_class(|class| {
+ rule_hash_target.each_class(|class| {
SelectorMap::get_matching_rules_from_hash(element,
- pseudo_element,
- parent_bf,
&self.class_hash,
class,
matching_rules_list,
@@ -1324,18 +1334,14 @@ impl SelectorMap<Rule> {
});
SelectorMap::get_matching_rules_from_hash(element,
- pseudo_element,
- parent_bf,
&self.local_name_hash,
- element.get_local_name(),
+ rule_hash_target.get_local_name(),
matching_rules_list,
context,
flags_setter,
cascade_level);
SelectorMap::get_matching_rules(element,
- pseudo_element,
- parent_bf,
&self.other,
matching_rules_list,
context,
@@ -1372,15 +1378,13 @@ impl SelectorMap<Rule> {
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
element: &E,
- pseudo_element: Option<(&PseudoElement, ElementState)>,
- parent_bf: Option<&BloomFilter>,
hash: &FnvHashMap<Str, Vec<Rule>>,
key: &BorrowedStr,
matching_rules: &mut Vector,
context: &mut MatchingContext,
flags_setter: &mut F,
cascade_level: CascadeLevel)
- where E: Element<Impl=SelectorImpl>,
+ where E: TElement,
Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash,
Vector: VecLike<ApplicableDeclarationBlock>,
@@ -1388,8 +1392,6 @@ impl SelectorMap<Rule> {
{
if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element,
- pseudo_element,
- parent_bf,
rules,
matching_rules,
context,
@@ -1400,42 +1402,18 @@ impl SelectorMap<Rule> {
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V, F>(element: &E,
- pseudo_element: Option<(&PseudoElement, ElementState)>,
- parent_bf: Option<&BloomFilter>,
rules: &[Rule],
matching_rules: &mut V,
context: &mut MatchingContext,
flags_setter: &mut F,
cascade_level: CascadeLevel)
- where E: Element<Impl=SelectorImpl>,
+ where E: TElement,
V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{
- for rule in rules.iter() {
- 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;
- }
- }
-
+ for rule in rules {
if matches_selector(&rule.selector.inner,
element,
- parent_bf,
context,
flags_setter) {
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.
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
// document type and quirks mode.
if let Component::ID(ref id) = *ss {
return Some(id.clone());
}
- }
-
- None
+ None
+ })
}
/// Retrieve the FIRST class name in the selector, or None otherwise.
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
// document type and quirks mode.
if let Component::Class(ref class) = *ss {
return Some(class.clone());
}
- }
-
- None
+ None
+ })
}
/// Retrieve the name if it is a type selector, or None otherwise.
pub fn get_local_name(selector: &SelectorInner<SelectorImpl>)
-> Option<LocalNameSelector<SelectorImpl>> {
- for ss in selector.complex.iter() {
+ find_from_right(selector, |ss| {
if let Component::LocalName(ref n) = *ss {
return Some(LocalNameSelector {
name: n.name.clone(),
lower_name: n.lower_name.clone(),
})
}
- }
-
- None
+ None
+ })
}
/// 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 {
/// Returns the specificity of the rule.
pub fn specificity(&self) -> u32 {
- self.selector.specificity
+ self.selector.specificity()
}
fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock {
diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs
index f7aca89a524..dfd563a3fb1 100644
--- a/ports/geckolib/glue.rs
+++ b/ports/geckolib/glue.rs
@@ -1068,7 +1068,6 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard,
d.stylist.lazily_compute_pseudo_element_style(&guards,
&element,
&pseudo,
- ElementState::empty(),
base,
&metrics)
.map(|s| s.values().clone())
diff --git a/tests/unit/style/stylesheets.rs b/tests/unit/style/stylesheets.rs
index 81d8a6fffd4..6d2a2d72bee 100644
--- a/tests/unit/style/stylesheets.rs
+++ b/tests/unit/style/stylesheets.rs
@@ -90,8 +90,8 @@ fn test_parse_stylesheet() {
}))),
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![
- Selector {
- inner: SelectorInner::from_vec(vec![
+ Selector::new_for_unit_testing(
+ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::LocalName(LocalName {
name: local_name!("input"),
@@ -106,9 +106,8 @@ fn test_parse_stylesheet() {
}),
}, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
]),
- pseudo_element: None,
- specificity: (0 << 20) + (1 << 10) + (1 << 0),
- },
+ (0 << 20) + (1 << 10) + (1 << 0)
+ ),
]),
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
@@ -124,28 +123,26 @@ fn test_parse_stylesheet() {
}))),
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![
- Selector {
- inner: SelectorInner::from_vec(vec![
+ Selector::new_for_unit_testing(
+ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::LocalName(LocalName {
name: local_name!("html"),
lower_name: local_name!("html"),
}),
]),
- pseudo_element: None,
- specificity: (0 << 20) + (0 << 10) + (1 << 0),
- },
- Selector {
- inner: SelectorInner::from_vec(vec![
+ (0 << 20) + (0 << 10) + (1 << 0)
+ ),
+ Selector::new_for_unit_testing(
+ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::LocalName(LocalName {
name: local_name!("body"),
lower_name: local_name!("body"),
}),
]),
- pseudo_element: None,
- specificity: (0 << 20) + (0 << 10) + (1 << 0),
- },
+ (0 << 20) + (0 << 10) + (1 << 0)
+ ),
]),
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
(PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
@@ -158,17 +155,16 @@ fn test_parse_stylesheet() {
}))),
CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
selectors: SelectorList(vec![
- Selector {
- inner: SelectorInner::from_vec(vec![
+ Selector::new_for_unit_testing(
+ SelectorInner::from_vec(vec![
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::ID(Atom::from("d1")),
Component::Combinator(Combinator::Child),
Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
Component::Class(Atom::from("ok")),
]),
- pseudo_element: None,
- specificity: (1 << 20) + (1 << 10) + (0 << 0),
- },
+ (1 << 20) + (1 << 10) + (0 << 0)
+ ),
]),
block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
(PropertyDeclaration::BackgroundColor(
diff --git a/tests/unit/stylo/size_of.rs b/tests/unit/stylo/size_of.rs
index 4ae541c8de0..f8770a8842e 100644
--- a/tests/unit/stylo/size_of.rs
+++ b/tests/unit/stylo/size_of.rs
@@ -12,8 +12,8 @@ fn size_of_selectors_dummy_types() {
assert_eq!(size_of::<dummies::PseudoClass>(), size_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!(align_of::<dummies::PseudoElementSelector>(), align_of::<real::PseudoElementSelector>());
+ assert_eq!(size_of::<dummies::PseudoElement>(), size_of::<real::PseudoElement>());
+ assert_eq!(align_of::<dummies::PseudoElement>(), align_of::<real::PseudoElement>());
assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>());
assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());