mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Bug 1364850: Move PseudoElement to be just another combinator in selectors. r=bholley
MozReview-Commit-ID: 8OoOIodkKJ5 Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
8375319928
commit
522f8489d6
19 changed files with 675 additions and 541 deletions
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue