mirror of
https://github.com/servo/servo.git
synced 2025-06-23 16:44:33 +01:00
style: Only store applicable ::before / ::after pseudo styles during the traversal.
This commit is contained in:
parent
5a57a3ef4b
commit
ff700aba75
7 changed files with 136 additions and 28 deletions
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -669,7 +669,7 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe
|
|||
pseudo_type: CSSPseudoElementType)
|
||||
-> ServoStyleContextStrong
|
||||
{
|
||||
use style::style_resolver::StyleResolverForElement;
|
||||
use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||
|
||||
debug_assert!(!snapshots.is_null());
|
||||
let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };
|
||||
|
@ -725,7 +725,8 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe
|
|||
visited_rules: None,
|
||||
};
|
||||
|
||||
StyleResolverForElement::new(element, &mut context, RuleInclusion::All)
|
||||
// Actually `PseudoElementResolution` doesn't matter.
|
||||
StyleResolverForElement::new(element, &mut context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
|
||||
.cascade_style_and_visited_with_default_parents(inputs)
|
||||
.into()
|
||||
}
|
||||
|
@ -2932,8 +2933,9 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
|||
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
|
||||
let data = doc_data.borrow();
|
||||
let rule_inclusion = RuleInclusion::from(rule_inclusion);
|
||||
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
|
||||
match PseudoElement::from_pseudo_type(pseudo_type) {
|
||||
let pseudo = PseudoElement::from_pseudo_type(pseudo_type);
|
||||
let finish = |styles: &ElementStyles, is_probe: bool| -> Option<Arc<ComputedValues>> {
|
||||
match pseudo {
|
||||
Some(ref pseudo) => {
|
||||
get_pseudo_style(
|
||||
&guard,
|
||||
|
@ -2943,22 +2945,27 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
|||
styles,
|
||||
/* inherited_styles = */ None,
|
||||
&*data,
|
||||
/* is_probe = */ false,
|
||||
).expect("We're not probing, so we should always get a style \
|
||||
back")
|
||||
is_probe,
|
||||
)
|
||||
}
|
||||
None => styles.primary().clone(),
|
||||
None => Some(styles.primary().clone()),
|
||||
}
|
||||
};
|
||||
|
||||
let is_before_or_after = pseudo.as_ref().map_or(false, |p| p.is_before_or_after());
|
||||
|
||||
// In the common case we already have the style. Check that before setting
|
||||
// up all the computation machinery. (Don't use it when we're getting
|
||||
// default styles or in a bfcached document (as indicated by
|
||||
// ignore_existing_styles), though.)
|
||||
//
|
||||
// Also, only probe in the ::before or ::after case, since their styles may
|
||||
// not be in the `ElementData`, given they may exist but not be applicable
|
||||
// to generate an actual pseudo-element (like, having a `content: none`).
|
||||
if rule_inclusion == RuleInclusion::All && !ignore_existing_styles {
|
||||
let styles = element.mutate_data().and_then(|d| {
|
||||
if d.has_styles() {
|
||||
Some(finish(&d.styles))
|
||||
finish(&d.styles, is_before_or_after)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -2980,8 +2987,17 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
|||
thread_local: &mut tlc,
|
||||
};
|
||||
|
||||
let styles = resolve_style(&mut context, element, rule_inclusion, ignore_existing_styles);
|
||||
finish(&styles).into()
|
||||
let styles = resolve_style(
|
||||
&mut context,
|
||||
element,
|
||||
rule_inclusion,
|
||||
ignore_existing_styles,
|
||||
pseudo.as_ref()
|
||||
);
|
||||
|
||||
finish(&styles, /* is_probe = */ false)
|
||||
.expect("We're not probing, so we should always get a style back")
|
||||
.into()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue