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,
|
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 style = styles.primary();
|
||||||
let longhand_id = match *property {
|
let longhand_id = match *property {
|
||||||
PropertyId::Longhand(id) => id,
|
PropertyId::Longhand(id) => id,
|
||||||
|
|
|
@ -123,7 +123,7 @@ trait PrivateMatchMethods: TElement {
|
||||||
primary_style: &Arc<ComputedValues>
|
primary_style: &Arc<ComputedValues>
|
||||||
) -> Option<Arc<ComputedValues>> {
|
) -> Option<Arc<ComputedValues>> {
|
||||||
use context::CascadeInputs;
|
use context::CascadeInputs;
|
||||||
use style_resolver::StyleResolverForElement;
|
use style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||||
use stylist::RuleInclusion;
|
use stylist::RuleInclusion;
|
||||||
|
|
||||||
let rule_node = primary_style.rules();
|
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()),
|
visited_rules: primary_style.get_visited_style().and_then(|s| s.rules.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Actually `PseudoElementResolution` doesn't really matter.
|
||||||
let style =
|
let style =
|
||||||
StyleResolverForElement::new(*self, context, RuleInclusion::All)
|
StyleResolverForElement::new(*self, context, RuleInclusion::All, PseudoElementResolution::IfApplicable)
|
||||||
.cascade_style_and_visited_with_default_parents(inputs);
|
.cascade_style_and_visited_with_default_parents(inputs);
|
||||||
|
|
||||||
Some(style)
|
Some(style)
|
||||||
|
|
|
@ -44,5 +44,17 @@ bitflags! {
|
||||||
/// A flag used to mark styles which are in a display: none subtree, or
|
/// A flag used to mark styles which are in a display: none subtree, or
|
||||||
/// under one.
|
/// under one.
|
||||||
const IS_IN_DISPLAY_NONE_SUBTREE = 1 << 5,
|
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}();
|
self.inherited_style_ignoring_first_line.get_${property.style_struct.name_lower}();
|
||||||
% endif
|
% 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()
|
self.${property.style_struct.ident}.mutate()
|
||||||
.copy_${property.ident}_from(
|
.copy_${property.ident}_from(
|
||||||
inherited_struct,
|
inherited_struct,
|
||||||
|
|
|
@ -15,12 +15,22 @@ use properties::{AnimationRules, CascadeFlags, ComputedValues};
|
||||||
use properties::{IS_LINK, IS_ROOT_ELEMENT, IS_VISITED_LINK};
|
use properties::{IS_LINK, IS_ROOT_ELEMENT, IS_VISITED_LINK};
|
||||||
use properties::{PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
|
use properties::{PROHIBIT_DISPLAY_CONTENTS, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP};
|
||||||
use properties::{VISITED_DEPENDENT_ONLY, cascade};
|
use properties::{VISITED_DEPENDENT_ONLY, cascade};
|
||||||
|
use properties::longhands::display::computed_value::T as display;
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_parser::{PseudoElement, SelectorImpl};
|
use selector_parser::{PseudoElement, SelectorImpl};
|
||||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, VisitedHandlingMode};
|
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, VisitedHandlingMode};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use stylist::RuleInclusion;
|
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.
|
/// A struct that takes care of resolving the style of a given element.
|
||||||
pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
|
pub struct StyleResolverForElement<'a, 'ctx, 'le, E>
|
||||||
where
|
where
|
||||||
|
@ -31,6 +41,7 @@ where
|
||||||
element: E,
|
element: E,
|
||||||
context: &'a mut StyleContext<'ctx, E>,
|
context: &'a mut StyleContext<'ctx, E>,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
|
pseudo_resolution: PseudoElementResolution,
|
||||||
_marker: ::std::marker::PhantomData<&'le E>,
|
_marker: ::std::marker::PhantomData<&'le E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +77,29 @@ where
|
||||||
f(parent_style.map(|x| &**x), layout_parent_style.map(|s| &**s))
|
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>
|
impl<'a, 'ctx, 'le, E> StyleResolverForElement<'a, 'ctx, 'le, E>
|
||||||
where
|
where
|
||||||
'ctx: 'a,
|
'ctx: 'a,
|
||||||
|
@ -77,11 +111,13 @@ where
|
||||||
element: E,
|
element: E,
|
||||||
context: &'a mut StyleContext<'ctx, E>,
|
context: &'a mut StyleContext<'ctx, E>,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
|
pseudo_resolution: PseudoElementResolution,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
element,
|
element,
|
||||||
context,
|
context,
|
||||||
rule_inclusion,
|
rule_inclusion,
|
||||||
|
pseudo_resolution,
|
||||||
_marker: ::std::marker::PhantomData,
|
_marker: ::std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,15 +286,21 @@ where
|
||||||
for (i, inputs) in pseudo_array.iter_mut().enumerate() {
|
for (i, inputs) in pseudo_array.iter_mut().enumerate() {
|
||||||
if let Some(inputs) = inputs.take() {
|
if let Some(inputs) = inputs.take() {
|
||||||
let pseudo = PseudoElement::from_eager_index(i);
|
let pseudo = PseudoElement::from_eager_index(i);
|
||||||
pseudo_styles.set(
|
|
||||||
&pseudo,
|
let style =
|
||||||
self.cascade_style_and_visited(
|
self.cascade_style_and_visited(
|
||||||
inputs,
|
inputs,
|
||||||
Some(&*primary_style.style),
|
Some(&*primary_style.style),
|
||||||
layout_parent_style_for_pseudo,
|
layout_parent_style_for_pseudo,
|
||||||
Some(&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 dom::{NodeInfo, OpaqueNode, TElement, TNode};
|
||||||
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
|
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
|
||||||
use matching::{ChildCascadeRequirement, MatchMethods};
|
use matching::{ChildCascadeRequirement, MatchMethods};
|
||||||
|
use selector_parser::PseudoElement;
|
||||||
use sharing::StyleSharingTarget;
|
use sharing::StyleSharingTarget;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use style_resolver::StyleResolverForElement;
|
use style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||||
#[cfg(feature = "servo")] use style_traits::ToCss;
|
#[cfg(feature = "servo")] use style_traits::ToCss;
|
||||||
use stylist::RuleInclusion;
|
use stylist::RuleInclusion;
|
||||||
use traversal_flags::{TraversalFlags, self};
|
use traversal_flags::{TraversalFlags, self};
|
||||||
|
@ -387,6 +388,7 @@ pub fn resolve_style<E>(
|
||||||
element: E,
|
element: E,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
ignore_existing_style: bool,
|
ignore_existing_style: bool,
|
||||||
|
pseudo: Option<&PseudoElement>,
|
||||||
) -> ElementStyles
|
) -> ElementStyles
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
|
@ -395,6 +397,7 @@ where
|
||||||
|
|
||||||
debug_assert!(rule_inclusion == RuleInclusion::DefaultOnly ||
|
debug_assert!(rule_inclusion == RuleInclusion::DefaultOnly ||
|
||||||
ignore_existing_style ||
|
ignore_existing_style ||
|
||||||
|
pseudo.map_or(false, |p| p.is_before_or_after()) ||
|
||||||
element.borrow_data().map_or(true, |d| !d.has_styles()),
|
element.borrow_data().map_or(true, |d| !d.has_styles()),
|
||||||
"Why are we here?");
|
"Why are we here?");
|
||||||
let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
|
let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
|
||||||
|
@ -438,8 +441,10 @@ where
|
||||||
for ancestor in ancestors_requiring_style_resolution.iter().rev() {
|
for ancestor in ancestors_requiring_style_resolution.iter().rev() {
|
||||||
context.thread_local.bloom_filter.assert_complete(*ancestor);
|
context.thread_local.bloom_filter.assert_complete(*ancestor);
|
||||||
|
|
||||||
|
// Actually `PseudoElementResolution` doesn't really matter here.
|
||||||
|
// (but it does matter below!).
|
||||||
let primary_style =
|
let primary_style =
|
||||||
StyleResolverForElement::new(*ancestor, context, rule_inclusion)
|
StyleResolverForElement::new(*ancestor, context, rule_inclusion, PseudoElementResolution::IfApplicable)
|
||||||
.resolve_primary_style(
|
.resolve_primary_style(
|
||||||
style.as_ref().map(|s| &**s),
|
style.as_ref().map(|s| &**s),
|
||||||
layout_parent_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);
|
context.thread_local.bloom_filter.assert_complete(element);
|
||||||
StyleResolverForElement::new(element, context, rule_inclusion)
|
StyleResolverForElement::new(element, context, rule_inclusion, PseudoElementResolution::Force)
|
||||||
.resolve_style(
|
.resolve_style(
|
||||||
style.as_ref().map(|s| &**s),
|
style.as_ref().map(|s| &**s),
|
||||||
layout_parent_style.as_ref().map(|s| &**s)
|
layout_parent_style.as_ref().map(|s| &**s)
|
||||||
|
@ -692,9 +697,17 @@ where
|
||||||
CannotShare => {
|
CannotShare => {
|
||||||
context.thread_local.statistics.elements_matched += 1;
|
context.thread_local.statistics.elements_matched += 1;
|
||||||
// Perform the matching and cascading.
|
// Perform the matching and cascading.
|
||||||
let new_styles =
|
let new_styles = {
|
||||||
StyleResolverForElement::new(element, context, RuleInclusion::All)
|
let mut resolver =
|
||||||
.resolve_style_with_default_parents();
|
StyleResolverForElement::new(
|
||||||
|
element,
|
||||||
|
context,
|
||||||
|
RuleInclusion::All,
|
||||||
|
PseudoElementResolution::IfApplicable
|
||||||
|
);
|
||||||
|
|
||||||
|
resolver.resolve_style_with_default_parents()
|
||||||
|
};
|
||||||
|
|
||||||
context.thread_local
|
context.thread_local
|
||||||
.style_sharing_candidate_cache
|
.style_sharing_candidate_cache
|
||||||
|
@ -715,15 +728,31 @@ where
|
||||||
ElementCascadeInputs::new_from_element_data(data);
|
ElementCascadeInputs::new_from_element_data(data);
|
||||||
important_rules_changed =
|
important_rules_changed =
|
||||||
element.replace_rules(flags, context, &mut cascade_inputs);
|
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 => {
|
CascadeOnly => {
|
||||||
// Skipping full matching, load cascade inputs from previous values.
|
// Skipping full matching, load cascade inputs from previous values.
|
||||||
let cascade_inputs =
|
let cascade_inputs =
|
||||||
ElementCascadeInputs::new_from_element_data(data);
|
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)
|
pseudo_type: CSSPseudoElementType)
|
||||||
-> ServoStyleContextStrong
|
-> ServoStyleContextStrong
|
||||||
{
|
{
|
||||||
use style::style_resolver::StyleResolverForElement;
|
use style::style_resolver::{PseudoElementResolution, StyleResolverForElement};
|
||||||
|
|
||||||
debug_assert!(!snapshots.is_null());
|
debug_assert!(!snapshots.is_null());
|
||||||
let computed_values = unsafe { ArcBorrow::from_ref(computed_values) };
|
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,
|
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)
|
.cascade_style_and_visited_with_default_parents(inputs)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -2932,8 +2933,9 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
|
let doc_data = PerDocumentStyleData::from_ffi(raw_data);
|
||||||
let data = doc_data.borrow();
|
let data = doc_data.borrow();
|
||||||
let rule_inclusion = RuleInclusion::from(rule_inclusion);
|
let rule_inclusion = RuleInclusion::from(rule_inclusion);
|
||||||
let finish = |styles: &ElementStyles| -> Arc<ComputedValues> {
|
let pseudo = PseudoElement::from_pseudo_type(pseudo_type);
|
||||||
match PseudoElement::from_pseudo_type(pseudo_type) {
|
let finish = |styles: &ElementStyles, is_probe: bool| -> Option<Arc<ComputedValues>> {
|
||||||
|
match pseudo {
|
||||||
Some(ref pseudo) => {
|
Some(ref pseudo) => {
|
||||||
get_pseudo_style(
|
get_pseudo_style(
|
||||||
&guard,
|
&guard,
|
||||||
|
@ -2943,22 +2945,27 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
styles,
|
styles,
|
||||||
/* inherited_styles = */ None,
|
/* inherited_styles = */ None,
|
||||||
&*data,
|
&*data,
|
||||||
/* is_probe = */ false,
|
is_probe,
|
||||||
).expect("We're not probing, so we should always get a style \
|
)
|
||||||
back")
|
|
||||||
}
|
}
|
||||||
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
|
// 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
|
// up all the computation machinery. (Don't use it when we're getting
|
||||||
// default styles or in a bfcached document (as indicated by
|
// default styles or in a bfcached document (as indicated by
|
||||||
// ignore_existing_styles), though.)
|
// 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 {
|
if rule_inclusion == RuleInclusion::All && !ignore_existing_styles {
|
||||||
let styles = element.mutate_data().and_then(|d| {
|
let styles = element.mutate_data().and_then(|d| {
|
||||||
if d.has_styles() {
|
if d.has_styles() {
|
||||||
Some(finish(&d.styles))
|
finish(&d.styles, is_before_or_after)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -2980,8 +2987,17 @@ pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
|
||||||
thread_local: &mut tlc,
|
thread_local: &mut tlc,
|
||||||
};
|
};
|
||||||
|
|
||||||
let styles = resolve_style(&mut context, element, rule_inclusion, ignore_existing_styles);
|
let styles = resolve_style(
|
||||||
finish(&styles).into()
|
&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]
|
#[no_mangle]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue