mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Introduce a new flag and use it to be more permissive about cousin sharing.
MozReview-Commit-ID: BCJg0Ycsy6M
This commit is contained in:
parent
ac37f81c1f
commit
54c52910e4
4 changed files with 45 additions and 23 deletions
|
@ -22,8 +22,17 @@ bitflags! {
|
||||||
flags RestyleFlags: u8 {
|
flags RestyleFlags: u8 {
|
||||||
/// Whether the styles changed for this restyle.
|
/// Whether the styles changed for this restyle.
|
||||||
const WAS_RESTYLED = 1 << 0,
|
const WAS_RESTYLED = 1 << 0,
|
||||||
|
/// Whether the last traversal of this element did not do
|
||||||
|
/// any style computation. This is not true during the initial
|
||||||
|
/// styling pass, nor is it true when we restyle (in which case
|
||||||
|
/// WAS_RESTYLED is set).
|
||||||
|
///
|
||||||
|
/// This bit always corresponds to the last time the element was
|
||||||
|
/// traversed, so each traversal simply updates it with the appropriate
|
||||||
|
/// value.
|
||||||
|
const TRAVERSED_WITHOUT_STYLING = 1 << 1,
|
||||||
/// Whether we reframed/reconstructed any ancestor or self.
|
/// Whether we reframed/reconstructed any ancestor or self.
|
||||||
const ANCESTOR_WAS_RECONSTRUCTED = 1 << 1,
|
const ANCESTOR_WAS_RECONSTRUCTED = 1 << 2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,14 +107,25 @@ impl RestyleData {
|
||||||
/// to do a post-traversal.
|
/// to do a post-traversal.
|
||||||
pub fn set_restyled(&mut self) {
|
pub fn set_restyled(&mut self) {
|
||||||
self.flags.insert(WAS_RESTYLED);
|
self.flags.insert(WAS_RESTYLED);
|
||||||
|
self.flags.remove(TRAVERSED_WITHOUT_STYLING);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark this element as restyled, which is useful to know whether we need
|
/// Returns true if this element was restyled.
|
||||||
/// to do a post-traversal.
|
|
||||||
pub fn is_restyle(&self) -> bool {
|
pub fn is_restyle(&self) -> bool {
|
||||||
self.flags.contains(WAS_RESTYLED)
|
self.flags.contains(WAS_RESTYLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mark that we traversed this element without computing any style for it.
|
||||||
|
pub fn set_traversed_without_styling(&mut self) {
|
||||||
|
self.flags.insert(TRAVERSED_WITHOUT_STYLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the element was traversed without computing any style for
|
||||||
|
/// it.
|
||||||
|
pub fn traversed_without_styling(&self) -> bool {
|
||||||
|
self.flags.contains(TRAVERSED_WITHOUT_STYLING)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether this element has been part of a restyle.
|
/// Returns whether this element has been part of a restyle.
|
||||||
pub fn contains_restyle_data(&self) -> bool {
|
pub fn contains_restyle_data(&self) -> bool {
|
||||||
self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
|
self.is_restyle() || !self.hint.is_empty() || !self.damage.is_empty()
|
||||||
|
|
|
@ -13,18 +13,11 @@ use dom::TElement;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use sharing::{StyleSharingCandidate, StyleSharingTarget};
|
use sharing::{StyleSharingCandidate, StyleSharingTarget};
|
||||||
|
|
||||||
/// Whether, given two elements, they have pointer-equal computed values.
|
/// Whether styles may be shared across the children of the given parent elements.
|
||||||
|
/// This is used to share style across cousins.
|
||||||
///
|
///
|
||||||
/// Both elements need to be styled already.
|
/// Both elements need to be styled already.
|
||||||
///
|
pub fn can_share_style_across_parents<E>(first: Option<E>, second: Option<E>) -> bool
|
||||||
/// This is used to know whether we can share style across cousins (if the two
|
|
||||||
/// parents have the same style).
|
|
||||||
///
|
|
||||||
/// We can only prove that they have the same class list, state, etc if they've
|
|
||||||
/// been restyled at the same time, otherwise the invalidation pass could make
|
|
||||||
/// them keep the same computed values even though now we wouldn't be able to
|
|
||||||
/// prove we match the same selectors.
|
|
||||||
pub fn same_computed_values<E>(first: Option<E>, second: Option<E>) -> bool
|
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
{
|
{
|
||||||
let (first, second) = match (first, second) {
|
let (first, second) = match (first, second) {
|
||||||
|
@ -37,13 +30,18 @@ pub fn same_computed_values<E>(first: Option<E>, second: Option<E>) -> bool
|
||||||
let first_data = first.borrow_data().unwrap();
|
let first_data = first.borrow_data().unwrap();
|
||||||
let second_data = second.borrow_data().unwrap();
|
let second_data = second.borrow_data().unwrap();
|
||||||
|
|
||||||
// FIXME(emilio): This check effectively disables cousin style sharing
|
// If a parent element was already styled and we traversed past it without
|
||||||
// on the initial style.
|
// restyling it, that may be because our clever invalidation logic was able
|
||||||
|
// to prove that the styles of that element would remain unchanged despite
|
||||||
|
// changes to the id or class attributes. However, style sharing relies in
|
||||||
|
// the strong guarantee that all the classes and ids up the respective parent
|
||||||
|
// chains are identical. As such, if we skipped styling for one (or both) of
|
||||||
|
// the parents on this traversal, we can't share styles across cousins.
|
||||||
//
|
//
|
||||||
// This is pretty bad, se the discussion in bug 1381821 for mor details.
|
// This is a somewhat conservative check. We could tighten it by having the
|
||||||
//
|
// invalidation logic explicitly flag elements for which it ellided styling.
|
||||||
// Bug 1387116 tracks fixing this, and various solutions are listed there.
|
if first_data.restyle.traversed_without_styling() ||
|
||||||
if !first_data.restyle.is_restyle() || !second_data.restyle.is_restyle() {
|
second_data.restyle.traversed_without_styling() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -610,13 +610,14 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that we have the same parent, or at least the same pointer
|
// Check that we have the same parent, or at least that the parents
|
||||||
// identity for parent computed style. The latter check allows us to
|
// share styles and permit sharing across their children. The latter
|
||||||
// share style between cousins if the parents shared style.
|
// check allows us to share style between cousins if the parents
|
||||||
|
// shared style.
|
||||||
let parent = target.traversal_parent();
|
let parent = target.traversal_parent();
|
||||||
let candidate_parent = candidate.element.traversal_parent();
|
let candidate_parent = candidate.element.traversal_parent();
|
||||||
if parent != candidate_parent &&
|
if parent != candidate_parent &&
|
||||||
!checks::same_computed_values(parent, candidate_parent) {
|
!checks::can_share_style_across_parents(parent, candidate_parent) {
|
||||||
miss!(Parent)
|
miss!(Parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -528,6 +528,9 @@ where
|
||||||
// evaluate the worklet code. In the case that the size hasn't changed,
|
// evaluate the worklet code. In the case that the size hasn't changed,
|
||||||
// this will result in increased concurrency between script and layout.
|
// this will result in increased concurrency between script and layout.
|
||||||
notify_paint_worklet(context, data);
|
notify_paint_worklet(context, data);
|
||||||
|
} else {
|
||||||
|
debug_assert!(data.has_styles());
|
||||||
|
data.restyle.set_traversed_without_styling();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that matching and cascading is done, clear the bits corresponding to
|
// Now that matching and cascading is done, clear the bits corresponding to
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue