style: Only store applicable ::before / ::after pseudo styles during the traversal.

This commit is contained in:
Emilio Cobos Álvarez 2017-08-24 11:09:33 +02:00
parent 5a57a3ef4b
commit ff700aba75
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
7 changed files with 136 additions and 28 deletions

View file

@ -702,7 +702,7 @@ pub fn process_resolved_style_request<'a, N>(context: &LayoutContext,
thread_local: &mut tlc,
};
let styles = resolve_style(&mut context, element, RuleInclusion::All, false);
let styles = resolve_style(&mut context, element, RuleInclusion::All, false, pseudo.as_ref());
let style = styles.primary();
let longhand_id = match *property {
PropertyId::Longhand(id) => id,

View file

@ -123,7 +123,7 @@ trait PrivateMatchMethods: TElement {
primary_style: &Arc<ComputedValues>
) -> Option<Arc<ComputedValues>> {
use context::CascadeInputs;
use style_resolver::StyleResolverForElement;
use style_resolver::{PseudoElementResolution, StyleResolverForElement};
use stylist::RuleInclusion;
let rule_node = primary_style.rules();
@ -143,8 +143,9 @@ trait PrivateMatchMethods: TElement {
visited_rules: primary_style.get_visited_style().and_then(|s| s.rules.clone()),
};
// Actually `PseudoElementResolution` doesn't really matter.
let style =
StyleResolverForElement::new(*self, context, RuleInclusion::All)
StyleResolverForElement::new(*self, context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
.cascade_style_and_visited_with_default_parents(inputs);
Some(style)

View file

@ -44,5 +44,17 @@ bitflags! {
/// A flag used to mark styles which are in a display: none subtree, or
/// under one.
const IS_IN_DISPLAY_NONE_SUBTREE = 1 << 5,
/// Whether this style inherits the `display` property.
///
/// This is important because it may affect our optimizations to avoid
/// computing the style of pseudo-elements, given whether the
/// pseudo-element is generated depends on the `display` value.
const INHERITS_DISPLAY = 1 << 6,
/// Whether this style inherits the `content` property.
///
/// Important because of the same reason.
const INHERITS_CONTENT = 1 << 7,
}
}

View file

@ -2691,6 +2691,14 @@ impl<'a> StyleBuilder<'a> {
self.inherited_style_ignoring_first_line.get_${property.style_struct.name_lower}();
% endif
% if property.ident == "content":
self.flags.insert(::properties::computed_value_flags::INHERITS_CONTENT);
% endif
% if property.ident == "display":
self.flags.insert(::properties::computed_value_flags::INHERITS_DISPLAY);
% endif
self.${property.style_struct.ident}.mutate()
.copy_${property.ident}_from(
inherited_struct,

View file

@ -15,12 +15,22 @@ use properties::{AnimationRules, CascadeFlags, ComputedValues};
use properties::{IS_LINK, IS_ROOT_ELEMENT, IS_VISITED_LINK};
use properties::{PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
use properties::{VISITED_DEPENDENT_ONLY, cascade};
use properties::longhands::display::computed_value::T as display;
use rule_tree::StrongRuleNode;
use selector_parser::{PseudoElement, SelectorImpl};
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, VisitedHandlingMode};
use servo_arc::Arc;
use stylist::RuleInclusion;
/// Whether pseudo-elements should be resolved or not.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PseudoElementResolution {
/// Only resolve pseudo-styles if possibly applicable.
IfApplicable,
/// Force pseudo-element resolution.
Force,
}
/// A struct that takes care of resolving the style of a given element.
pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
where
@ -31,6 +41,7 @@ where
element: E,
context: &'a mut StyleContext<'ctx, E>,
rule_inclusion: RuleInclusion,
pseudo_resolution: PseudoElementResolution,
_marker: ::std::marker::PhantomData<&'le E>,
}
@ -66,6 +77,29 @@ where
f(parent_style.map(|x| &**x), layout_parent_style.map(|s| &**s))
}
fn eager_pseudo_is_definitely_not_generated(
pseudo: &PseudoElement,
style: &ComputedValues,
) -> bool {
use properties::computed_value_flags::{INHERITS_CONTENT, INHERITS_DISPLAY};
if !pseudo.is_before_or_after() {
return false;
}
if !style.flags.intersects(INHERITS_DISPLAY) &&
style.get_box().clone_display() == display::none {
return true;
}
if !style.flags.intersects(INHERITS_CONTENT) &&
style.ineffective_content_property() {
return true;
}
false
}
impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
where
'ctx: 'a,
@ -77,11 +111,13 @@ where
element: E,
context: &'a mut StyleContext<'ctx, E>,
rule_inclusion: RuleInclusion,
pseudo_resolution: PseudoElementResolution,
) -> Self {
Self {
element,
context,
rule_inclusion,
pseudo_resolution,
_marker: ::std::marker::PhantomData,
}
}
@ -250,15 +286,21 @@ where
for (i, inputs) in pseudo_array.iter_mut().enumerate() {
if let Some(inputs) = inputs.take() {
let pseudo = PseudoElement::from_eager_index(i);
pseudo_styles.set(
&pseudo,
let style =
self.cascade_style_and_visited(
inputs,
Some(&*primary_style.style),
layout_parent_style_for_pseudo,
Some(&pseudo),
)
)
);
if !matches!(self.pseudo_resolution, PseudoElementResolution::Force) &&
eager_pseudo_is_definitely_not_generated(&pseudo, &style) {
continue;
}
pseudo_styles.set(&pseudo, style);
}
}
}

View file

@ -9,9 +9,10 @@ use data::{ElementData, ElementStyles};
use dom::{NodeInfo, OpaqueNode, TElement, TNode};
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
use matching::{ChildCascadeRequirement, MatchMethods};
use selector_parser::PseudoElement;
use sharing::StyleSharingTarget;
use smallvec::SmallVec;
use style_resolver::StyleResolverForElement;
use style_resolver::{PseudoElementResolution, StyleResolverForElement};
#[cfg(feature = "servo")] use style_traits::ToCss;
use stylist::RuleInclusion;
use traversal_flags::{TraversalFlags, self};
@ -387,6 +388,7 @@ pub fn resolve_style<E>(
element: E,
rule_inclusion: RuleInclusion,
ignore_existing_style: bool,
pseudo: Option<&PseudoElement>,
) -> ElementStyles
where
E: TElement,
@ -395,6 +397,7 @@ where
debug_assert!(rule_inclusion == RuleInclusion::DefaultOnly ||
ignore_existing_style ||
pseudo.map_or(false, |p| p.is_before_or_after()) ||
element.borrow_data().map_or(true, |d| !d.has_styles()),
"Why are we here?");
let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
@ -438,8 +441,10 @@ where
for ancestor in ancestors_requiring_style_resolution.iter().rev() {
context.thread_local.bloom_filter.assert_complete(*ancestor);
// Actually `PseudoElementResolution` doesn't really matter here.
// (but it does matter below!).
let primary_style =
StyleResolverForElement::new(*ancestor, context, rule_inclusion)
StyleResolverForElement::new(*ancestor, context, rule_inclusion, PseudoElementResolution::IfApplicable)
.resolve_primary_style(
style.as_ref().map(|s| &**s),
layout_parent_style.as_ref().map(|s| &**s)
@ -456,7 +461,7 @@ where
}
context.thread_local.bloom_filter.assert_complete(element);
StyleResolverForElement::new(element, context, rule_inclusion)
StyleResolverForElement::new(element, context, rule_inclusion, PseudoElementResolution::Force)
.resolve_style(
style.as_ref().map(|s| &**s),
layout_parent_style.as_ref().map(|s| &**s)
@ -692,9 +697,17 @@ where
CannotShare => {
context.thread_local.statistics.elements_matched += 1;
// Perform the matching and cascading.
let new_styles =
StyleResolverForElement::new(element, context, RuleInclusion::All)
.resolve_style_with_default_parents();
let new_styles = {
let mut resolver =
StyleResolverForElement::new(
element,
context,
RuleInclusion::All,
PseudoElementResolution::IfApplicable
);
resolver.resolve_style_with_default_parents()
};
context.thread_local
.style_sharing_candidate_cache
@ -715,15 +728,31 @@ where
ElementCascadeInputs::new_from_element_data(data);
important_rules_changed =
element.replace_rules(flags, context, &mut cascade_inputs);
StyleResolverForElement::new(element, context, RuleInclusion::All)
.cascade_styles_with_default_parents(cascade_inputs)
let mut resolver =
StyleResolverForElement::new(
element,
context,
RuleInclusion::All,
PseudoElementResolution::IfApplicable
);
resolver.cascade_styles_with_default_parents(cascade_inputs)
}
CascadeOnly => {
// Skipping full matching, load cascade inputs from previous values.
let cascade_inputs =
ElementCascadeInputs::new_from_element_data(data);
StyleResolverForElement::new(element, context, RuleInclusion::All)
.cascade_styles_with_default_parents(cascade_inputs)
let mut resolver =
StyleResolverForElement::new(
element,
context,
RuleInclusion::All,
PseudoElementResolution::IfApplicable
);
resolver.cascade_styles_with_default_parents(cascade_inputs)
}
};