mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
style: Cache computed styles objects display: none subtrees
This reuses our existing undisplayed style generation, but in a per-document rather than per-nsComputedDOMStyle object, which means that we can avoid re-resolving styles of elements in display: none subtrees much more often. This brings the test-case in the bug to par with other browsers or better, and is much simpler than the initial approach I tried back in the day. Differential Revision: https://phabricator.services.mozilla.com/D147547
This commit is contained in:
parent
39ac4840ee
commit
d1aeb3921b
3 changed files with 46 additions and 9 deletions
|
@ -129,6 +129,12 @@ impl StylesheetInDocument for GeckoStyleSheet {
|
||||||
pub struct PerDocumentStyleDataImpl {
|
pub struct PerDocumentStyleDataImpl {
|
||||||
/// Rule processor.
|
/// Rule processor.
|
||||||
pub stylist: Stylist,
|
pub stylist: Stylist,
|
||||||
|
|
||||||
|
/// A cache from element to resolved style.
|
||||||
|
pub undisplayed_style_cache: crate::traversal::UndisplayedStyleCache,
|
||||||
|
|
||||||
|
/// The generation for which our cache is valid.
|
||||||
|
pub undisplayed_style_cache_generation: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
|
/// The data itself is an `AtomicRefCell`, which guarantees the proper semantics
|
||||||
|
@ -143,6 +149,8 @@ impl PerDocumentStyleData {
|
||||||
|
|
||||||
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
|
PerDocumentStyleData(AtomicRefCell::new(PerDocumentStyleDataImpl {
|
||||||
stylist: Stylist::new(device, quirks_mode.into()),
|
stylist: Stylist::new(device, quirks_mode.into()),
|
||||||
|
undisplayed_style_cache: Default::default(),
|
||||||
|
undisplayed_style_cache_generation: 0,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,12 +185,6 @@ impl PerDocumentStyleDataImpl {
|
||||||
self.stylist.device().default_computed_values_arc()
|
self.stylist.device().default_computed_values_arc()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether visited styles are enabled.
|
|
||||||
#[inline]
|
|
||||||
pub fn visited_styles_enabled(&self) -> bool {
|
|
||||||
unsafe { bindings::Gecko_VisitedStylesEnabled(self.stylist.device().document()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Measure heap usage.
|
/// Measure heap usage.
|
||||||
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
pub fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) {
|
||||||
self.stylist.add_size_of(ops, sizes);
|
self.stylist.add_size_of(ops, sizes);
|
||||||
|
|
|
@ -411,6 +411,11 @@ impl Device {
|
||||||
self.used_font_metrics.load(Ordering::Relaxed)
|
self.used_font_metrics.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether visited styles are enabled.
|
||||||
|
pub fn visited_styles_enabled(&self) -> bool {
|
||||||
|
unsafe { bindings::Gecko_VisitedStylesEnabled(self.document()) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the device pixel ratio.
|
/// Returns the device pixel ratio.
|
||||||
pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
|
pub fn device_pixel_ratio(&self) -> Scale<f32, CSSPixel, DevicePixel> {
|
||||||
let pc = match self.pres_context() {
|
let pc = match self.pres_context() {
|
||||||
|
|
|
@ -16,6 +16,13 @@ use crate::stylist::RuleInclusion;
|
||||||
use crate::traversal_flags::TraversalFlags;
|
use crate::traversal_flags::TraversalFlags;
|
||||||
use selectors::NthIndexCache;
|
use selectors::NthIndexCache;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// A cache from element reference to known-valid computed style.
|
||||||
|
pub type UndisplayedStyleCache = HashMap<
|
||||||
|
selectors::OpaqueElement,
|
||||||
|
servo_arc::Arc<crate::properties::ComputedValues>,
|
||||||
|
>;
|
||||||
|
|
||||||
/// A per-traversal-level chunk of data. This is sent down by the traversal, and
|
/// A per-traversal-level chunk of data. This is sent down by the traversal, and
|
||||||
/// currently only holds the dom depth for the bloom filter.
|
/// currently only holds the dom depth for the bloom filter.
|
||||||
|
@ -294,6 +301,7 @@ pub fn resolve_style<E>(
|
||||||
element: E,
|
element: E,
|
||||||
rule_inclusion: RuleInclusion,
|
rule_inclusion: RuleInclusion,
|
||||||
pseudo: Option<&PseudoElement>,
|
pseudo: Option<&PseudoElement>,
|
||||||
|
mut undisplayed_style_cache: Option<&mut UndisplayedStyleCache>,
|
||||||
) -> ElementStyles
|
) -> ElementStyles
|
||||||
where
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
|
@ -304,6 +312,11 @@ where
|
||||||
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?"
|
||||||
);
|
);
|
||||||
|
debug_assert!(
|
||||||
|
rule_inclusion == RuleInclusion::All || undisplayed_style_cache.is_none(),
|
||||||
|
"can't use the cache for default styles only"
|
||||||
|
);
|
||||||
|
|
||||||
let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
|
let mut ancestors_requiring_style_resolution = SmallVec::<[E; 16]>::new();
|
||||||
|
|
||||||
// Clear the bloom filter, just in case the caller is reusing TLS.
|
// Clear the bloom filter, just in case the caller is reusing TLS.
|
||||||
|
@ -320,6 +333,12 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(ref mut cache) = undisplayed_style_cache {
|
||||||
|
if let Some(s) = cache.get(¤t.opaque()) {
|
||||||
|
style = Some(s.clone());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
ancestors_requiring_style_resolution.push(current);
|
ancestors_requiring_style_resolution.push(current);
|
||||||
ancestor = current.traversal_parent();
|
ancestor = current.traversal_parent();
|
||||||
}
|
}
|
||||||
|
@ -337,7 +356,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
ancestor = ancestor.unwrap().traversal_parent();
|
ancestor = ancestor.unwrap().traversal_parent();
|
||||||
layout_parent_style = ancestor.map(|a| a.borrow_data().unwrap().styles.primary().clone());
|
layout_parent_style = ancestor.and_then(|a| {
|
||||||
|
a.borrow_data().map(|data| data.styles.primary().clone())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for ancestor in ancestors_requiring_style_resolution.iter().rev() {
|
for ancestor in ancestors_requiring_style_resolution.iter().rev() {
|
||||||
|
@ -360,18 +381,27 @@ where
|
||||||
layout_parent_style = style.clone();
|
layout_parent_style = style.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ref mut cache) = undisplayed_style_cache {
|
||||||
|
cache.insert(ancestor.opaque(), style.clone().unwrap());
|
||||||
|
}
|
||||||
context.thread_local.bloom_filter.push(*ancestor);
|
context.thread_local.bloom_filter.push(*ancestor);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.thread_local.bloom_filter.assert_complete(element);
|
context.thread_local.bloom_filter.assert_complete(element);
|
||||||
StyleResolverForElement::new(
|
let styles: ElementStyles = StyleResolverForElement::new(
|
||||||
element,
|
element,
|
||||||
context,
|
context,
|
||||||
rule_inclusion,
|
rule_inclusion,
|
||||||
PseudoElementResolution::Force,
|
PseudoElementResolution::Force,
|
||||||
)
|
)
|
||||||
.resolve_style(style.as_deref(), layout_parent_style.as_deref())
|
.resolve_style(style.as_deref(), layout_parent_style.as_deref())
|
||||||
.into()
|
.into();
|
||||||
|
|
||||||
|
if let Some(ref mut cache) = undisplayed_style_cache {
|
||||||
|
cache.insert(element.opaque(), styles.primary().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
styles
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the style for a single node.
|
/// Calculates the style for a single node.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue