mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Bug 1340334: Allow sibling hints in StoredRestyleHint, and handle them correctly. r=bholley
MozReview-Commit-ID: H6NuKsfjEdj Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
57ff19ec83
commit
32bf8093cc
2 changed files with 53 additions and 92 deletions
|
@ -9,7 +9,7 @@
|
||||||
use dom::TElement;
|
use dom::TElement;
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use properties::longhands::display::computed_value as display;
|
use properties::longhands::display::computed_value as display;
|
||||||
use restyle_hints::{RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
|
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_LATER_SIBLINGS, RESTYLE_SELF, RestyleHint};
|
||||||
use rule_tree::StrongRuleNode;
|
use rule_tree::StrongRuleNode;
|
||||||
use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
|
use selector_parser::{PseudoElement, RestyleDamage, Snapshot};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -126,99 +126,53 @@ impl ElementStyles {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enum to describe the different requirements that a restyle hint may impose
|
/// Restyle hint for storing on ElementData.
|
||||||
/// on its descendants.
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum DescendantRestyleHint {
|
|
||||||
/// This hint does not require any descendants to be restyled.
|
|
||||||
Empty,
|
|
||||||
/// This hint requires direct children to be restyled.
|
|
||||||
Children,
|
|
||||||
/// This hint requires all descendants to be restyled.
|
|
||||||
Descendants,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DescendantRestyleHint {
|
|
||||||
/// Propagates this descendant behavior to a child element.
|
|
||||||
fn propagate(self) -> Self {
|
|
||||||
use self::DescendantRestyleHint::*;
|
|
||||||
if self == Descendants {
|
|
||||||
Descendants
|
|
||||||
} else {
|
|
||||||
Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn union(self, other: Self) -> Self {
|
|
||||||
use self::DescendantRestyleHint::*;
|
|
||||||
if self == Descendants || other == Descendants {
|
|
||||||
Descendants
|
|
||||||
} else if self == Children || other == Children {
|
|
||||||
Children
|
|
||||||
} else {
|
|
||||||
Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Restyle hint for storing on ElementData. We use a separate representation
|
|
||||||
/// to provide more type safety while propagating restyle hints down the tree.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct StoredRestyleHint {
|
|
||||||
/// Whether this element should be restyled during the traversal, and how.
|
|
||||||
///
|
///
|
||||||
/// This hint is stripped down, and only contains hints that are a subset of
|
/// We wrap it in a newtype to force the encapsulation of the complexity of
|
||||||
/// RestyleHint::for_single_element().
|
/// handling the correct invalidations in this file.
|
||||||
pub self_: RestyleHint,
|
#[derive(Clone, Debug)]
|
||||||
/// Whether the descendants of this element need to be restyled.
|
pub struct StoredRestyleHint(RestyleHint);
|
||||||
pub descendants: DescendantRestyleHint,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StoredRestyleHint {
|
impl StoredRestyleHint {
|
||||||
/// Propagates this restyle hint to a child element.
|
/// Propagates this restyle hint to a child element.
|
||||||
pub fn propagate(&self) -> Self {
|
pub fn propagate(&self) -> Self {
|
||||||
StoredRestyleHint {
|
StoredRestyleHint(if self.0.contains(RESTYLE_DESCENDANTS) {
|
||||||
self_: if self.descendants == DescendantRestyleHint::Empty {
|
RESTYLE_SELF | RESTYLE_DESCENDANTS
|
||||||
RestyleHint::empty()
|
|
||||||
} else {
|
} else {
|
||||||
RESTYLE_SELF
|
RestyleHint::empty()
|
||||||
},
|
})
|
||||||
descendants: self.descendants.propagate(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates an empty `StoredRestyleHint`.
|
/// Creates an empty `StoredRestyleHint`.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
StoredRestyleHint {
|
StoredRestyleHint(RestyleHint::empty())
|
||||||
self_: RestyleHint::empty(),
|
|
||||||
descendants: DescendantRestyleHint::Empty,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a restyle hint that forces the whole subtree to be restyled,
|
/// Creates a restyle hint that forces the whole subtree to be restyled,
|
||||||
/// including the element.
|
/// including the element.
|
||||||
pub fn subtree() -> Self {
|
pub fn subtree() -> Self {
|
||||||
StoredRestyleHint {
|
StoredRestyleHint(RESTYLE_SELF | RESTYLE_DESCENDANTS)
|
||||||
self_: RESTYLE_SELF,
|
|
||||||
descendants: DescendantRestyleHint::Descendants,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the hint indicates that our style may be invalidated.
|
/// Returns true if the hint indicates that our style may be invalidated.
|
||||||
pub fn has_self_invalidations(&self) -> bool {
|
pub fn has_self_invalidations(&self) -> bool {
|
||||||
!self.self_.is_empty()
|
self.0.intersects(RestyleHint::for_self())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the hint indicates that our sibling's style may be
|
||||||
|
/// invalidated.
|
||||||
|
pub fn has_sibling_invalidations(&self) -> bool {
|
||||||
|
self.0.intersects(RESTYLE_LATER_SIBLINGS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the restyle hint is empty (nothing requires to be restyled).
|
/// Whether the restyle hint is empty (nothing requires to be restyled).
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
!self.has_self_invalidations() &&
|
self.0.is_empty()
|
||||||
self.descendants == DescendantRestyleHint::Empty
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert another restyle hint, effectively resulting in the union of both.
|
/// Insert another restyle hint, effectively resulting in the union of both.
|
||||||
pub fn insert(&mut self, other: &Self) {
|
pub fn insert(&mut self, other: &Self) {
|
||||||
self.self_ |= other.self_;
|
self.0 |= other.0
|
||||||
self.descendants = self.descendants.union(other.descendants);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,13 +184,7 @@ impl Default for StoredRestyleHint {
|
||||||
|
|
||||||
impl From<RestyleHint> for StoredRestyleHint {
|
impl From<RestyleHint> for StoredRestyleHint {
|
||||||
fn from(hint: RestyleHint) -> Self {
|
fn from(hint: RestyleHint) -> Self {
|
||||||
use restyle_hints::*;
|
StoredRestyleHint(hint)
|
||||||
use self::DescendantRestyleHint::*;
|
|
||||||
debug_assert!(!hint.contains(RESTYLE_LATER_SIBLINGS), "Caller should apply sibling hints");
|
|
||||||
StoredRestyleHint {
|
|
||||||
self_: hint & RestyleHint::for_self(),
|
|
||||||
descendants: if hint.contains(RESTYLE_DESCENDANTS) { Descendants } else { Empty },
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,24 +270,30 @@ pub struct RestyleData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RestyleData {
|
impl RestyleData {
|
||||||
/// Expands the snapshot (if any) into a restyle hint. Returns true if later
|
/// Computes the final restyle hint for this element.
|
||||||
/// siblings must be restyled.
|
///
|
||||||
pub fn expand_snapshot<E: TElement>(&mut self, element: E, stylist: &Stylist) -> bool {
|
/// This expands the snapshot (if any) into a restyle hint, and handles
|
||||||
if self.snapshot.is_none() {
|
/// explicit sibling restyle hints from the stored restyle hint.
|
||||||
return false;
|
///
|
||||||
}
|
/// Returns true if later siblings must be restyled.
|
||||||
|
pub fn compute_final_hint<E: TElement>(&mut self,
|
||||||
|
element: E,
|
||||||
|
stylist: &Stylist)
|
||||||
|
-> bool {
|
||||||
|
let mut hint = self.hint.0;
|
||||||
|
|
||||||
// Compute the hint.
|
if let Some(snapshot) = self.snapshot.as_ref() {
|
||||||
let mut hint = stylist.compute_restyle_hint(&element,
|
hint |= stylist.compute_restyle_hint(&element, snapshot);
|
||||||
self.snapshot.as_ref().unwrap());
|
}
|
||||||
|
|
||||||
// If the hint includes a directive for later siblings, strip it out and
|
// If the hint includes a directive for later siblings, strip it out and
|
||||||
// notify the caller to modify the base hint for future siblings.
|
// notify the caller to modify the base hint for future siblings.
|
||||||
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
|
let later_siblings = hint.contains(RESTYLE_LATER_SIBLINGS);
|
||||||
hint.remove(RESTYLE_LATER_SIBLINGS);
|
hint.remove(RESTYLE_LATER_SIBLINGS);
|
||||||
|
|
||||||
// Insert the hint.
|
// Insert the hint, overriding the previous hint. This effectively takes
|
||||||
self.hint.insert(&hint.into());
|
// care of removing the later siblings restyle hint.
|
||||||
|
self.hint = hint.into();
|
||||||
|
|
||||||
// Destroy the snapshot.
|
// Destroy the snapshot.
|
||||||
self.snapshot.destroy();
|
self.snapshot.destroy();
|
||||||
|
@ -354,6 +308,11 @@ impl RestyleData {
|
||||||
self.snapshot.is_some()
|
self.snapshot.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if this RestyleData might invalidate sibling styles.
|
||||||
|
pub fn has_sibling_invalidations(&self) -> bool {
|
||||||
|
self.hint.has_sibling_invalidations() || self.snapshot.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns damage handled.
|
/// Returns damage handled.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn damage_handled(&self) -> RestyleDamage {
|
pub fn damage_handled(&self) -> RestyleDamage {
|
||||||
|
@ -436,7 +395,8 @@ impl ElementData {
|
||||||
|
|
||||||
debug_assert!(self.restyle.is_some());
|
debug_assert!(self.restyle.is_some());
|
||||||
let restyle_data = self.restyle.as_ref().unwrap();
|
let restyle_data = self.restyle.as_ref().unwrap();
|
||||||
let hint = restyle_data.hint.self_;
|
|
||||||
|
let hint = restyle_data.hint.0;
|
||||||
if hint.contains(RESTYLE_SELF) {
|
if hint.contains(RESTYLE_SELF) {
|
||||||
return RestyleKind::MatchAndCascade;
|
return RestyleKind::MatchAndCascade;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ pub trait DomTraversal<E: TElement> : Sync {
|
||||||
if let Some(mut data) = root.mutate_data() {
|
if let Some(mut data) = root.mutate_data() {
|
||||||
if let Some(r) = data.get_restyle_mut() {
|
if let Some(r) = data.get_restyle_mut() {
|
||||||
debug_assert!(root.next_sibling_element().is_none());
|
debug_assert!(root.next_sibling_element().is_none());
|
||||||
let _later_siblings = r.expand_snapshot(root, stylist);
|
let _later_siblings = r.compute_final_hint(root, stylist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,8 +414,9 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
{
|
{
|
||||||
context.thread_local.begin_element(element, &data);
|
context.thread_local.begin_element(element, &data);
|
||||||
context.thread_local.statistics.elements_traversed += 1;
|
context.thread_local.statistics.elements_traversed += 1;
|
||||||
debug_assert!(data.get_restyle().map_or(true, |r| r.snapshot.is_none()),
|
debug_assert!(data.get_restyle().map_or(true, |r| {
|
||||||
"Snapshots should be expanded by the caller");
|
r.snapshot.is_none() && !r.has_sibling_invalidations()
|
||||||
|
}), "Should've computed the final hint and handled later_siblings already");
|
||||||
|
|
||||||
let compute_self = !data.has_current_styles();
|
let compute_self = !data.has_current_styles();
|
||||||
let mut inherited_style_changed = false;
|
let mut inherited_style_changed = false;
|
||||||
|
@ -594,9 +595,9 @@ fn preprocess_children<E, D>(traversal: &D,
|
||||||
restyle_data.hint.insert(&propagated_hint);
|
restyle_data.hint.insert(&propagated_hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle element snapshots.
|
// Handle element snapshots and sibling restyle hints.
|
||||||
let stylist = &traversal.shared_context().stylist;
|
let stylist = &traversal.shared_context().stylist;
|
||||||
let later_siblings = restyle_data.expand_snapshot(child, stylist);
|
let later_siblings = restyle_data.compute_final_hint(child, stylist);
|
||||||
if later_siblings {
|
if later_siblings {
|
||||||
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
|
propagated_hint.insert(&(RESTYLE_SELF | RESTYLE_DESCENDANTS).into());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue