Bug 1345950: stylo: Fix slow selector flags. r=bholley

MozReview-Commit-ID: QwuSxulNG0
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-03-21 12:58:28 +01:00
parent f90f7d6824
commit a9b133bcbb
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
9 changed files with 288 additions and 189 deletions

View file

@ -2416,10 +2416,13 @@ impl<'a> ::selectors::Element for Root<Element> {
self.namespace() self.namespace()
} }
fn match_non_ts_pseudo_class(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
_: &mut StyleRelations, _: &mut StyleRelations,
_: &mut ElementSelectorFlags) -> bool { _: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class { match *pseudo_class {
// https://github.com/servo/servo/issues/8718 // https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link | NonTSPseudoClass::Link |

View file

@ -615,10 +615,13 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
self.element.namespace() self.element.namespace()
} }
fn match_non_ts_pseudo_class(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
_: &mut StyleRelations, _: &mut StyleRelations,
_: &mut ElementSelectorFlags) -> bool { _: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class { match *pseudo_class {
// https://github.com/servo/servo/issues/8718 // https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link | NonTSPseudoClass::Link |
@ -1114,10 +1117,13 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
self.element.get_namespace() self.element.get_namespace()
} }
fn match_non_ts_pseudo_class(&self, fn match_non_ts_pseudo_class<F>(&self,
_: &NonTSPseudoClass, _: &NonTSPseudoClass,
_: &mut StyleRelations, _: &mut StyleRelations,
_: &mut ElementSelectorFlags) -> bool { _: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
// NB: This could maybe be implemented // NB: This could maybe be implemented
warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called"); warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
false false

View file

@ -107,8 +107,11 @@ pub fn matches<E>(selector_list: &[Selector<E::Impl>],
{ {
selector_list.iter().any(|selector| { selector_list.iter().any(|selector| {
selector.pseudo_element.is_none() && selector.pseudo_element.is_none() &&
matches_complex_selector(&*selector.complex_selector, element, parent_bf, matches_complex_selector(&*selector.complex_selector,
&mut StyleRelations::empty(), &mut ElementSelectorFlags::empty()) element,
parent_bf,
&mut StyleRelations::empty(),
&mut |_, _| {})
}) })
} }
@ -162,13 +165,14 @@ fn may_match<E>(mut selector: &ComplexSelector<E::Impl>,
} }
/// Determines whether the given element matches the given complex selector. /// Determines whether the given element matches the given complex selector.
pub fn matches_complex_selector<E>(selector: &ComplexSelector<E::Impl>, pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
element: &E, element: &E,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) flags_setter: &mut F)
-> bool -> bool
where E: Element where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{ {
if let Some(filter) = parent_bf { if let Some(filter) = parent_bf {
if !may_match::<E>(selector, filter) { if !may_match::<E>(selector, filter) {
@ -176,7 +180,10 @@ pub fn matches_complex_selector<E>(selector: &ComplexSelector<E::Impl>,
} }
} }
match matches_complex_selector_internal(selector, element, relations, flags) { match matches_complex_selector_internal(selector,
element,
relations,
flags_setter) {
SelectorMatchingResult::Matched => { SelectorMatchingResult::Matched => {
match selector.next { match selector.next {
Some((_, Combinator::NextSibling)) | Some((_, Combinator::NextSibling)) |
@ -240,17 +247,26 @@ enum SelectorMatchingResult {
NotMatchedGlobally, NotMatchedGlobally,
} }
fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>, fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
element: &E, element: &E,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) flags_setter: &mut F)
-> SelectorMatchingResult -> SelectorMatchingResult
where E: Element where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{ {
let matches_all_simple_selectors = selector.compound_selector.iter().all(|simple| { let matches_all_simple_selectors = selector.compound_selector.iter().all(|simple| {
matches_simple_selector(simple, element, relations, flags) matches_simple_selector(simple, element, relations, flags_setter)
}); });
let siblings = selector.next.as_ref().map_or(false, |&(_, combinator)| {
matches!(combinator, Combinator::NextSibling | Combinator::LaterSibling)
});
if siblings {
flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
}
if !matches_all_simple_selectors { if !matches_all_simple_selectors {
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling; return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
} }
@ -258,17 +274,14 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
match selector.next { match selector.next {
None => SelectorMatchingResult::Matched, None => SelectorMatchingResult::Matched,
Some((ref next_selector, combinator)) => { Some((ref next_selector, combinator)) => {
let (siblings, candidate_not_found) = match combinator { let (mut next_element, candidate_not_found) = if siblings {
Combinator::Child => (false, SelectorMatchingResult::NotMatchedGlobally), (element.prev_sibling_element(),
Combinator::Descendant => (false, SelectorMatchingResult::NotMatchedGlobally), SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
Combinator::NextSibling => (true, SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant),
Combinator::LaterSibling => (true, SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant),
};
let mut next_element = if siblings {
element.prev_sibling_element()
} else { } else {
element.parent_element() (element.parent_element(),
SelectorMatchingResult::NotMatchedGlobally)
}; };
loop { loop {
let element = match next_element { let element = match next_element {
None => return candidate_not_found, None => return candidate_not_found,
@ -277,7 +290,7 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
let result = matches_complex_selector_internal(&**next_selector, let result = matches_complex_selector_internal(&**next_selector,
&element, &element,
relations, relations,
flags); flags_setter);
match (result, combinator) { match (result, combinator) {
// Return the status immediately. // Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result, (SelectorMatchingResult::Matched, _) => return result,
@ -316,13 +329,14 @@ fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
/// Determines whether the given element matches the given single selector. /// Determines whether the given element matches the given single selector.
#[inline] #[inline]
fn matches_simple_selector<E>( fn matches_simple_selector<E, F>(
selector: &SimpleSelector<E::Impl>, selector: &SimpleSelector<E::Impl>,
element: &E, element: &E,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) flags_setter: &mut F)
-> bool -> bool
where E: Element where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{ {
macro_rules! relation_if { macro_rules! relation_if {
($ex:expr, $flag:ident) => { ($ex:expr, $flag:ident) => {
@ -399,18 +413,21 @@ fn matches_simple_selector<E>(
false false
} }
SimpleSelector::NonTSPseudoClass(ref pc) => { SimpleSelector::NonTSPseudoClass(ref pc) => {
relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags), relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags_setter),
AFFECTED_BY_STATE) AFFECTED_BY_STATE)
} }
SimpleSelector::FirstChild => { SimpleSelector::FirstChild => {
relation_if!(matches_first_child(element, flags), AFFECTED_BY_CHILD_INDEX) relation_if!(matches_first_child(element, flags_setter),
AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::LastChild => { SimpleSelector::LastChild => {
relation_if!(matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX) relation_if!(matches_last_child(element, flags_setter),
AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::OnlyChild => { SimpleSelector::OnlyChild => {
relation_if!(matches_first_child(element, flags) && relation_if!(matches_first_child(element, flags_setter) &&
matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX) matches_last_child(element, flags_setter),
AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::Root => { SimpleSelector::Root => {
// We never share styles with an element with no parent, so no point // We never share styles with an element with no parent, so no point
@ -418,41 +435,44 @@ fn matches_simple_selector<E>(
element.is_root() element.is_root()
} }
SimpleSelector::Empty => { SimpleSelector::Empty => {
flags.insert(HAS_EMPTY_SELECTOR); flags_setter(element, HAS_EMPTY_SELECTOR);
relation_if!(element.is_empty(), AFFECTED_BY_EMPTY) relation_if!(element.is_empty(), AFFECTED_BY_EMPTY)
} }
SimpleSelector::NthChild(a, b) => { SimpleSelector::NthChild(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, false, false, flags), relation_if!(matches_generic_nth_child(element, a, b, false, false, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::NthLastChild(a, b) => { SimpleSelector::NthLastChild(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, false, true, flags), relation_if!(matches_generic_nth_child(element, a, b, false, true, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::NthOfType(a, b) => { SimpleSelector::NthOfType(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, true, false, flags), relation_if!(matches_generic_nth_child(element, a, b, true, false, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::NthLastOfType(a, b) => { SimpleSelector::NthLastOfType(a, b) => {
relation_if!(matches_generic_nth_child(element, a, b, true, true, flags), relation_if!(matches_generic_nth_child(element, a, b, true, true, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::FirstOfType => { SimpleSelector::FirstOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags), relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::LastOfType => { SimpleSelector::LastOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags), relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::OnlyOfType => { SimpleSelector::OnlyOfType => {
relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags) && relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter) &&
matches_generic_nth_child(element, 0, 1, true, true, flags), matches_generic_nth_child(element, 0, 1, true, true, flags_setter),
AFFECTED_BY_CHILD_INDEX) AFFECTED_BY_CHILD_INDEX)
} }
SimpleSelector::Negation(ref negated) => { SimpleSelector::Negation(ref negated) => {
!negated.iter().all(|s| { !negated.iter().all(|s| {
match matches_complex_selector_internal(s, element, relations, flags) { match matches_complex_selector_internal(s,
element,
relations,
flags_setter) {
SelectorMatchingResult::Matched => true, SelectorMatchingResult::Matched => true,
_ => false, _ => false,
} }
@ -462,16 +482,17 @@ fn matches_simple_selector<E>(
} }
#[inline] #[inline]
fn matches_generic_nth_child<E>(element: &E, fn matches_generic_nth_child<E, F>(element: &E,
a: i32, a: i32,
b: i32, b: i32,
is_of_type: bool, is_of_type: bool,
is_from_end: bool, is_from_end: bool,
flags: &mut ElementSelectorFlags) flags_setter: &mut F)
-> bool -> bool
where E: Element where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{ {
flags.insert(if is_from_end { flags_setter(element, if is_from_end {
HAS_SLOW_SELECTOR HAS_SLOW_SELECTOR
} else { } else {
HAS_SLOW_SELECTOR_LATER_SIBLINGS HAS_SLOW_SELECTOR_LATER_SIBLINGS
@ -514,15 +535,19 @@ fn matches_generic_nth_child<E>(element: &E,
} }
#[inline] #[inline]
fn matches_first_child<E>(element: &E, flags: &mut ElementSelectorFlags) fn matches_first_child<E, F>(element: &E, flags_setter: &mut F) -> bool
-> bool where E: Element { where E: Element,
flags.insert(HAS_EDGE_CHILD_SELECTOR); F: FnMut(&E, ElementSelectorFlags),
{
flags_setter(element, HAS_EDGE_CHILD_SELECTOR);
element.prev_sibling_element().is_none() element.prev_sibling_element().is_none()
} }
#[inline] #[inline]
fn matches_last_child<E>(element: &E, flags: &mut ElementSelectorFlags) fn matches_last_child<E, F>(element: &E, flags_setter: &mut F) -> bool
-> bool where E: Element { where E: Element,
flags.insert(HAS_EDGE_CHILD_SELECTOR); F: FnMut(&E, ElementSelectorFlags),
{
flags_setter(element, HAS_EDGE_CHILD_SELECTOR);
element.next_sibling_element().is_none() element.next_sibling_element().is_none()
} }

View file

@ -139,10 +139,11 @@ pub trait Element: MatchAttr + Sized {
fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName; fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName;
fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl; fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl;
fn match_non_ts_pseudo_class(&self, fn match_non_ts_pseudo_class<F>(&self,
pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass, pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) -> bool; flags_setter: &mut F) -> bool
where F: FnMut(&Self, ElementSelectorFlags);
fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>; fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool; fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;

View file

@ -663,10 +663,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
} }
fn match_non_ts_pseudo_class(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) -> bool { flags_setter: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class { match *pseudo_class {
// https://github.com/servo/servo/issues/8718 // https://github.com/servo/servo/issues/8718
NonTSPseudoClass::AnyLink => unsafe { Gecko_IsLink(self.0) }, NonTSPseudoClass::AnyLink => unsafe { Gecko_IsLink(self.0) },
@ -704,7 +707,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0) Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
}, },
NonTSPseudoClass::MozAny(ref sels) => { NonTSPseudoClass::MozAny(ref sels) => {
sels.iter().any(|s| matches_complex_selector(s, self, None, relations, flags)) sels.iter().any(|s| {
matches_complex_selector(s,
self,
None,
relations,
flags_setter)
})
} }
NonTSPseudoClass::MozSystemMetric(ref s) | NonTSPseudoClass::MozSystemMetric(ref s) |
NonTSPseudoClass::MozLocaleDir(ref s) | NonTSPseudoClass::MozLocaleDir(ref s) |
@ -718,7 +727,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
pseudo_class.to_gecko_pseudoclasstype().unwrap(), pseudo_class.to_gecko_pseudoclasstype().unwrap(),
s.as_ptr(), &mut set_slow_selector); s.as_ptr(), &mut set_slow_selector);
if set_slow_selector { if set_slow_selector {
*flags |= HAS_SLOW_SELECTOR; flags_setter(self, HAS_SLOW_SELECTOR);
} }
matches matches
} }
@ -862,7 +871,7 @@ impl<'le> ElementExt for GeckoElement<'le> {
fn is_link(&self) -> bool { fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut StyleRelations::empty(), &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()) &mut |_, _| {})
} }
#[inline] #[inline]

View file

@ -18,7 +18,7 @@ use dom::{AnimationRules, SendElement, TElement, TNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint}; use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint};
use rule_tree::{CascadeLevel, StrongRuleNode}; use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl}; use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr; use selectors::MatchAttr;
use selectors::bloom::BloomFilter; use selectors::bloom::BloomFilter;
@ -750,12 +750,12 @@ trait PrivateMatchMethods: TElement {
} }
} }
fn compute_rule_node<E: TElement>(context: &StyleContext<E>, fn compute_rule_node<E: TElement>(rule_tree: &RuleTree,
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>) applicable_declarations: &mut Vec<ApplicableDeclarationBlock>)
-> StrongRuleNode -> StrongRuleNode
{ {
let rules = applicable_declarations.drain(..).map(|d| (d.source, d.level)); let rules = applicable_declarations.drain(..).map(|d| (d.source, d.level));
let rule_node = context.shared.stylist.rule_tree.insert_ordered_rules(rules); let rule_node = rule_tree.insert_ordered_rules(rules);
rule_node rule_node
} }
@ -775,20 +775,75 @@ pub trait MatchMethods : TElement {
let stylist = &context.shared.stylist; let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute(); let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None); let animation_rules = self.get_animation_rules(None);
let mut flags = ElementSelectorFlags::empty();
let mut rule_nodes_changed = false; let mut rule_nodes_changed = false;
// TODO(emilio): This is somewhat inefficient, because of a variety of
// reasons:
//
// * It doesn't coalesce flags.
// * It doesn't look at flags already sent in a task for the main
// thread to process.
// * It doesn't take advantage of us knowing that the traversal is
// sequential.
//
// I suspect (need to measure!) that we don't use to set flags on
// a lot of different elements, but we could end up posting the same
// flag over and over with this approach.
//
// If the number of elements is low, perhaps a small cache with the
// flags already sent would be appropriate.
//
// The sequential task business for this is kind of sad :(.
//
// Anyway, let's do the obvious thing for now.
let tasks = &mut context.thread_local.tasks;
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
// Apply the selector flags.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
if element == self {
unsafe { element.set_selector_flags(self_flags); }
} else {
if !element.has_selector_flags(self_flags) {
let task =
SequentialTask::set_selector_flags(element.clone(),
self_flags);
tasks.push(task);
}
}
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
// Avoid the overhead of the SequentialTask if the flags are
// already set.
if !p.has_selector_flags(parent_flags) {
let task = SequentialTask::set_selector_flags(p, parent_flags);
tasks.push(task);
}
}
}
};
// Borrow the stuff we need here so the borrow checker doesn't get mad
// at us later in the closure.
let guards = &context.shared.guards;
let rule_tree = &context.shared.stylist.rule_tree;
let bloom_filter = context.thread_local.bloom_filter.filter();
// Compute the primary rule node. // Compute the primary rule node.
let mut primary_relations = let mut primary_relations =
stylist.push_applicable_declarations(self, stylist.push_applicable_declarations(self,
Some(context.thread_local.bloom_filter.filter()), Some(bloom_filter),
style_attribute, style_attribute,
animation_rules, animation_rules,
None, None,
&context.shared.guards, guards,
&mut applicable_declarations, &mut applicable_declarations,
&mut flags); &mut set_selector_flags);
let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
let primary_rule_node =
compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
if !data.has_styles() { if !data.has_styles() {
data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node))); data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
rule_nodes_changed = true; rule_nodes_changed = true;
@ -808,15 +863,16 @@ pub trait MatchMethods : TElement {
AnimationRules(None, None) AnimationRules(None, None)
}; };
stylist.push_applicable_declarations(self, stylist.push_applicable_declarations(self,
Some(context.thread_local.bloom_filter.filter()), Some(bloom_filter),
None, pseudo_animation_rules, None, pseudo_animation_rules,
Some(&pseudo), Some(&pseudo),
&context.shared.guards, &guards,
&mut applicable_declarations, &mut applicable_declarations,
&mut flags); &mut set_selector_flags);
if !applicable_declarations.is_empty() { if !applicable_declarations.is_empty() {
let new_rules = compute_rule_node(context, &mut applicable_declarations); let new_rules =
compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
match per_pseudo.entry(pseudo) { match per_pseudo.entry(pseudo) {
Entry::Occupied(mut e) => { Entry::Occupied(mut e) => {
if e.get().rules != new_rules { if e.get().rules != new_rules {
@ -848,22 +904,6 @@ pub trait MatchMethods : TElement {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS; primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
} }
// Apply the selector flags.
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { self.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = self.parent_element() {
// Avoid the overhead of the SequentialTask if the flags are already set.
if !p.has_selector_flags(parent_flags) {
let task = SequentialTask::set_selector_flags(p, parent_flags);
context.thread_local.tasks.push(task);
}
}
}
MatchResults { MatchResults {
primary_relations: Some(primary_relations), primary_relations: Some(primary_relations),
rule_nodes_changed: rule_nodes_changed, rule_nodes_changed: rule_nodes_changed,

View file

@ -269,17 +269,25 @@ impl<'a, E> MatchAttr for ElementWrapper<'a, E>
impl<'a, E> Element for ElementWrapper<'a, E> impl<'a, E> Element for ElementWrapper<'a, E>
where E: TElement, where E: TElement,
{ {
fn match_non_ts_pseudo_class(&self, fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass, pseudo_class: &NonTSPseudoClass,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags) -> bool { _: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class); let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class);
if flag == ElementState::empty() { if flag.is_empty() {
self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags) return self.element.match_non_ts_pseudo_class(pseudo_class,
} else { relations,
match self.snapshot.and_then(|s| s.state()) { &mut |_, _| {})
Some(snapshot_state) => snapshot_state.contains(flag), }
_ => self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags) match self.snapshot.and_then(|s| s.state()) {
Some(snapshot_state) => snapshot_state.contains(flag),
None => {
self.element.match_non_ts_pseudo_class(pseudo_class,
relations,
&mut |_, _| {})
} }
} }
} }
@ -581,11 +589,11 @@ impl DependencySet {
let matched_then = let matched_then =
matches_complex_selector(&dep.selector, snapshot, None, matches_complex_selector(&dep.selector, snapshot, None,
&mut StyleRelations::empty(), &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()); &mut |_, _| {});
let matches_now = let matches_now =
matches_complex_selector(&dep.selector, element, None, matches_complex_selector(&dep.selector, element, None,
&mut StyleRelations::empty(), &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()); &mut |_, _| {});
if matched_then != matches_now { if matched_then != matches_now {
hint.insert(dep.hint); hint.insert(dep.hint);
} }

View file

@ -14,7 +14,7 @@ use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser}; use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable}; use selector_parser::{attr_equals_selector_is_shareable, attr_exists_selector_is_shareable};
use selectors::{Element, MatchAttrGeneric}; use selectors::{Element, MatchAttrGeneric};
use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::matching::StyleRelations;
use selectors::parser::{AttrSelector, SelectorMethods}; use selectors::parser::{AttrSelector, SelectorMethods};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
@ -459,7 +459,7 @@ impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
fn is_link(&self) -> bool { fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink, self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
&mut StyleRelations::empty(), &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()) &mut |_, _| {})
} }
#[inline] #[inline]

View file

@ -398,7 +398,31 @@ impl Stylist {
let mut declarations = vec![]; let mut declarations = vec![];
let mut flags = ElementSelectorFlags::empty(); // Apply the selector flags. We should be in sequential mode
// already, so we can directly apply the parent flags.
let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
if cfg!(feature = "servo") {
// Servo calls this function from the worker, but only for internal
// pseudos, so we should never generate selector flags here.
unreachable!("internal pseudo generated slow selector flags?");
}
// Gecko calls this from sequential mode, so we can directly apply
// the flags.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { element.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
unsafe { p.set_selector_flags(parent_flags); }
}
}
};
self.push_applicable_declarations(element, self.push_applicable_declarations(element,
None, None,
None, None,
@ -406,7 +430,7 @@ impl Stylist {
Some(pseudo), Some(pseudo),
guards, guards,
&mut declarations, &mut declarations,
&mut flags); &mut set_selector_flags);
let rule_node = let rule_node =
self.rule_tree.insert_ordered_rules( self.rule_tree.insert_ordered_rules(
@ -426,28 +450,6 @@ impl Stylist {
&StdoutErrorReporter, &StdoutErrorReporter,
CascadeFlags::empty()); CascadeFlags::empty());
// Apply the selector flags. We should be in sequential mode already,
// so we can directly apply the parent flags.
if cfg!(feature = "servo") {
// Servo calls this function from the worker, but only for internal
// pseudos, so we should never generate selector flags here.
debug_assert!(flags.is_empty());
} else {
// Gecko calls this from sequential mode, so we can directly apply
// the flags.
debug_assert!(thread_state::get() == thread_state::LAYOUT);
let self_flags = flags.for_self();
if !self_flags.is_empty() {
unsafe { element.set_selector_flags(self_flags); }
}
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
unsafe { p.set_selector_flags(parent_flags); }
}
}
}
Some(ComputedStyle::new(rule_node, Arc::new(computed))) Some(ComputedStyle::new(rule_node, Arc::new(computed)))
} }
@ -537,7 +539,7 @@ impl Stylist {
/// ///
/// The returned `StyleRelations` indicate hints about which kind of rules /// The returned `StyleRelations` indicate hints about which kind of rules
/// have matched. /// have matched.
pub fn push_applicable_declarations<E, V>( pub fn push_applicable_declarations<E, V, F>(
&self, &self,
element: &E, element: &E,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
@ -546,11 +548,13 @@ impl Stylist {
pseudo_element: Option<&PseudoElement>, pseudo_element: Option<&PseudoElement>,
guards: &StylesheetGuards, guards: &StylesheetGuards,
applicable_declarations: &mut V, applicable_declarations: &mut V,
flags: &mut ElementSelectorFlags) -> StyleRelations flags_setter: &mut F)
-> StyleRelations
where E: TElement + where E: TElement +
fmt::Debug + fmt::Debug +
PresentationalHintsSynthetizer, PresentationalHintsSynthetizer,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{ {
debug_assert!(!self.is_device_dirty); debug_assert!(!self.is_device_dirty);
debug_assert!(style_attribute.is_none() || pseudo_element.is_none(), debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
@ -573,7 +577,7 @@ impl Stylist {
guards.ua_or_user, guards.ua_or_user,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
flags, flags_setter,
CascadeLevel::UANormal); CascadeLevel::UANormal);
debug!("UA normal: {:?}", relations); debug!("UA normal: {:?}", relations);
@ -598,7 +602,7 @@ impl Stylist {
guards.ua_or_user, guards.ua_or_user,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
flags, flags_setter,
CascadeLevel::UserNormal); CascadeLevel::UserNormal);
debug!("user normal: {:?}", relations); debug!("user normal: {:?}", relations);
map.author.get_all_matching_rules(element, map.author.get_all_matching_rules(element,
@ -606,7 +610,7 @@ impl Stylist {
guards.author, guards.author,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
flags, flags_setter,
CascadeLevel::AuthorNormal); CascadeLevel::AuthorNormal);
debug!("author normal: {:?}", relations); debug!("author normal: {:?}", relations);
@ -641,7 +645,7 @@ impl Stylist {
guards.author, guards.author,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
flags, flags_setter,
CascadeLevel::AuthorImportant); CascadeLevel::AuthorImportant);
debug!("author important: {:?}", relations); debug!("author important: {:?}", relations);
@ -665,7 +669,7 @@ impl Stylist {
guards.ua_or_user, guards.ua_or_user,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
flags, flags_setter,
CascadeLevel::UserImportant); CascadeLevel::UserImportant);
debug!("user important: {:?}", relations); debug!("user important: {:?}", relations);
@ -679,7 +683,7 @@ impl Stylist {
guards.ua_or_user, guards.ua_or_user,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
flags, flags_setter,
CascadeLevel::UAImportant); CascadeLevel::UAImportant);
debug!("UA important: {:?}", relations); debug!("UA important: {:?}", relations);
@ -735,11 +739,11 @@ impl Stylist {
let element_matches = let element_matches =
matches_complex_selector(&selector.complex_selector, element, matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(), None, &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()); &mut |_, _| {});
let candidate_matches = let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate, matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(), None, &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()); &mut |_, _| {});
if element_matches != candidate_matches { if element_matches != candidate_matches {
return false; return false;
@ -774,12 +778,12 @@ impl Stylist {
let element_matches = let element_matches =
matches_complex_selector(&selector.complex_selector, element, matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(), None, &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()); &mut |_, _| {});
let candidate_matches = let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate, matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(), None, &mut StyleRelations::empty(),
&mut ElementSelectorFlags::empty()); &mut |_, _| {});
if element_matches != candidate_matches { if element_matches != candidate_matches {
debug!("match_same_sibling_affecting_rules: Failure due to {:?}", debug!("match_same_sibling_affecting_rules: Failure due to {:?}",
@ -912,16 +916,17 @@ impl SelectorMap {
/// ///
/// Extract matching rules as per element's ID, classes, tag name, etc.. /// Extract matching rules as per element's ID, classes, tag name, etc..
/// Sort the Rules at the end to maintain cascading order. /// Sort the Rules at the end to maintain cascading order.
pub fn get_all_matching_rules<E, V>(&self, pub fn get_all_matching_rules<E, V, F>(&self,
element: &E, element: &E,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
matching_rules_list: &mut V, matching_rules_list: &mut V,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags, flags_setter: &mut F,
cascade_level: CascadeLevel) cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>, where E: Element<Impl=SelectorImpl>,
V: VecLike<ApplicableDeclarationBlock> V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{ {
if self.empty { if self.empty {
return return
@ -937,7 +942,7 @@ impl SelectorMap {
guard, guard,
matching_rules_list, matching_rules_list,
relations, relations,
flags, flags_setter,
cascade_level) cascade_level)
} }
@ -949,7 +954,7 @@ impl SelectorMap {
guard, guard,
matching_rules_list, matching_rules_list,
relations, relations,
flags, flags_setter,
cascade_level); cascade_level);
}); });
@ -965,7 +970,7 @@ impl SelectorMap {
guard, guard,
matching_rules_list, matching_rules_list,
relations, relations,
flags, flags_setter,
cascade_level); cascade_level);
SelectorMap::get_matching_rules(element, SelectorMap::get_matching_rules(element,
@ -974,7 +979,7 @@ impl SelectorMap {
guard, guard,
matching_rules_list, matching_rules_list,
relations, relations,
flags, flags_setter,
cascade_level); cascade_level);
// Sort only the rules we just added. // Sort only the rules we just added.
@ -1027,7 +1032,7 @@ impl SelectorMap {
matching_rules_list matching_rules_list
} }
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector>( fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
element: &E, element: &E,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
hash: &FnvHashMap<Str, Vec<Rule>>, hash: &FnvHashMap<Str, Vec<Rule>>,
@ -1035,12 +1040,13 @@ impl SelectorMap {
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
matching_rules: &mut Vector, matching_rules: &mut Vector,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags, flags_setter: &mut F,
cascade_level: CascadeLevel) cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>, where E: Element<Impl=SelectorImpl>,
Str: Borrow<BorrowedStr> + Eq + Hash, Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash, BorrowedStr: Eq + Hash,
Vector: VecLike<ApplicableDeclarationBlock> Vector: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{ {
if let Some(rules) = hash.get(key) { if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element, SelectorMap::get_matching_rules(element,
@ -1049,22 +1055,23 @@ impl SelectorMap {
guard, guard,
matching_rules, matching_rules,
relations, relations,
flags, flags_setter,
cascade_level) cascade_level)
} }
} }
/// Adds rules in `rules` that match `element` to the `matching_rules` list. /// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V>(element: &E, fn get_matching_rules<E, V, F>(element: &E,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
rules: &[Rule], rules: &[Rule],
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
matching_rules: &mut V, matching_rules: &mut V,
relations: &mut StyleRelations, relations: &mut StyleRelations,
flags: &mut ElementSelectorFlags, flags_setter: &mut F,
cascade_level: CascadeLevel) cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>, where E: Element<Impl=SelectorImpl>,
V: VecLike<ApplicableDeclarationBlock> V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{ {
for rule in rules.iter() { for rule in rules.iter() {
let style_rule = rule.style_rule.read_with(guard); let style_rule = rule.style_rule.read_with(guard);
@ -1076,7 +1083,7 @@ impl SelectorMap {
}; };
if any_declaration_for_importance && if any_declaration_for_importance &&
matches_complex_selector(&*rule.selector, element, parent_bf, matches_complex_selector(&*rule.selector, element, parent_bf,
relations, flags) { relations, flags_setter) {
matching_rules.push( matching_rules.push(
rule.to_applicable_declaration_block(cascade_level)); rule.to_applicable_declaration_block(cascade_level));
} }