aboutsummaryrefslogtreecommitdiffstats
path: root/components/style
diff options
context:
space:
mode:
Diffstat (limited to 'components/style')
-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
7 files changed, 202 insertions, 344 deletions
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 {