mirror of
https://github.com/servo/servo.git
synced 2025-06-25 09:34:32 +01:00
style: Use RestyleDamage to determine whether we must continue cascading style changes to children.
This commit is contained in:
parent
715d18d377
commit
8b7a414b1c
5 changed files with 221 additions and 109 deletions
|
@ -160,7 +160,9 @@ pub fn recalc_style_for_animations(context: &LayoutContext,
|
||||||
animation,
|
animation,
|
||||||
&mut fragment.style,
|
&mut fragment.style,
|
||||||
&ServoMetricsProvider);
|
&ServoMetricsProvider);
|
||||||
damage |= RestyleDamage::compute(&old_style, &fragment.style);
|
let difference =
|
||||||
|
RestyleDamage::compute_style_difference(&old_style, &fragment.style);
|
||||||
|
damage |= difference.damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,7 @@ use gecko_bindings::bindings;
|
||||||
use gecko_bindings::structs;
|
use gecko_bindings::structs;
|
||||||
use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
|
use gecko_bindings::structs::{nsChangeHint, nsStyleContext};
|
||||||
use gecko_bindings::sugar::ownership::FFIArcHelpers;
|
use gecko_bindings::sugar::ownership::FFIArcHelpers;
|
||||||
|
use matching::{StyleChange, StyleDifference};
|
||||||
use properties::ComputedValues;
|
use properties::ComputedValues;
|
||||||
use std::ops::{BitAnd, BitOr, BitOrAssign, Not};
|
use std::ops::{BitAnd, BitOr, BitOrAssign, Not};
|
||||||
use stylearc::Arc;
|
use stylearc::Arc;
|
||||||
|
@ -38,15 +39,17 @@ impl GeckoRestyleDamage {
|
||||||
self.0 == nsChangeHint(0)
|
self.0 == nsChangeHint(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes a change hint given an old style (in the form of a
|
/// Computes the `StyleDifference` (including the appropriate change hint)
|
||||||
/// `nsStyleContext`, and a new style (in the form of `ComputedValues`).
|
/// given an old style (in the form of a `nsStyleContext`, and a new style
|
||||||
|
/// (in the form of `ComputedValues`).
|
||||||
///
|
///
|
||||||
/// Note that we could in theory just get two `ComputedValues` here and diff
|
/// Note that we could in theory just get two `ComputedValues` here and diff
|
||||||
/// them, but Gecko has an interesting optimization when they mark accessed
|
/// them, but Gecko has an interesting optimization when they mark accessed
|
||||||
/// structs, so they effectively only diff structs that have ever been
|
/// structs, so they effectively only diff structs that have ever been
|
||||||
/// accessed from layout.
|
/// accessed from layout.
|
||||||
pub fn compute(source: &nsStyleContext,
|
pub fn compute_style_difference(source: &nsStyleContext,
|
||||||
new_style: &Arc<ComputedValues>) -> Self {
|
new_style: &Arc<ComputedValues>)
|
||||||
|
-> StyleDifference {
|
||||||
// TODO(emilio): Const-ify this?
|
// TODO(emilio): Const-ify this?
|
||||||
let context = source as *const nsStyleContext as *mut nsStyleContext;
|
let context = source as *const nsStyleContext as *mut nsStyleContext;
|
||||||
let mut any_style_changed: bool = false;
|
let mut any_style_changed: bool = false;
|
||||||
|
@ -55,7 +58,8 @@ impl GeckoRestyleDamage {
|
||||||
new_style.as_borrowed_opt().unwrap(),
|
new_style.as_borrowed_opt().unwrap(),
|
||||||
&mut any_style_changed)
|
&mut any_style_changed)
|
||||||
};
|
};
|
||||||
GeckoRestyleDamage(hint)
|
let change = if any_style_changed { StyleChange::Changed } else { StyleChange::Unchanged };
|
||||||
|
StyleDifference::new(GeckoRestyleDamage(hint), change)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this restyle damage contains all the damage of |other|.
|
/// Returns true if this restyle damage contains all the damage of |other|.
|
||||||
|
|
|
@ -51,6 +51,34 @@ fn relations_are_shareable(relations: &StyleRelations) -> bool {
|
||||||
AFFECTED_BY_PRESENTATIONAL_HINTS)
|
AFFECTED_BY_PRESENTATIONAL_HINTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the result of comparing an element's old and new style.
|
||||||
|
pub struct StyleDifference {
|
||||||
|
/// The resulting damage.
|
||||||
|
pub damage: RestyleDamage,
|
||||||
|
|
||||||
|
/// Whether any styles changed.
|
||||||
|
pub change: StyleChange,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StyleDifference {
|
||||||
|
/// Creates a new `StyleDifference`.
|
||||||
|
pub fn new(damage: RestyleDamage, change: StyleChange) -> Self {
|
||||||
|
StyleDifference {
|
||||||
|
change: change,
|
||||||
|
damage: damage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents whether or not the style of an element has changed.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum StyleChange {
|
||||||
|
/// The style hasn't changed.
|
||||||
|
Unchanged,
|
||||||
|
/// The style has changed.
|
||||||
|
Changed,
|
||||||
|
}
|
||||||
|
|
||||||
/// Information regarding a style sharing candidate.
|
/// Information regarding a style sharing candidate.
|
||||||
///
|
///
|
||||||
/// Note that this information is stored in TLS and cleared after the traversal,
|
/// Note that this information is stored in TLS and cleared after the traversal,
|
||||||
|
@ -375,8 +403,37 @@ pub enum StyleSharingResult {
|
||||||
/// We didn't find anybody to share the style with.
|
/// We didn't find anybody to share the style with.
|
||||||
CannotShare,
|
CannotShare,
|
||||||
/// The node's style can be shared. The integer specifies the index in the
|
/// The node's style can be shared. The integer specifies the index in the
|
||||||
/// LRU cache that was hit and the damage that was done.
|
/// LRU cache that was hit and the damage that was done. The
|
||||||
StyleWasShared(usize),
|
/// `ChildCascadeRequirement` indicates whether style changes due to using
|
||||||
|
/// the shared style mean we need to recascade to children.
|
||||||
|
StyleWasShared(usize, ChildCascadeRequirement),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether or not newly computed values for an element need to be cascade
|
||||||
|
/// to children.
|
||||||
|
pub enum ChildCascadeRequirement {
|
||||||
|
/// Old and new computed values were the same, or we otherwise know that
|
||||||
|
/// we won't bother recomputing style for children, so we can skip cascading
|
||||||
|
/// the new values into child elements.
|
||||||
|
CanSkipCascade,
|
||||||
|
/// Old and new computed values were different, so we must cascade the
|
||||||
|
/// new values to children.
|
||||||
|
///
|
||||||
|
/// FIXME(heycam) Although this is "must" cascade, in the future we should
|
||||||
|
/// track whether child elements rely specifically on inheriting particular
|
||||||
|
/// property values. When we do that, we can treat `MustCascade` as "must
|
||||||
|
/// cascade unless we know that changes to these properties can be
|
||||||
|
/// ignored".
|
||||||
|
MustCascade,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StyleChange> for ChildCascadeRequirement {
|
||||||
|
fn from(change: StyleChange) -> ChildCascadeRequirement {
|
||||||
|
match change {
|
||||||
|
StyleChange::Unchanged => ChildCascadeRequirement::CanSkipCascade,
|
||||||
|
StyleChange::Changed => ChildCascadeRequirement::MustCascade,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result status for match primary rules.
|
/// The result status for match primary rules.
|
||||||
|
@ -570,7 +627,8 @@ trait PrivateMatchMethods: TElement {
|
||||||
fn cascade_primary(&self,
|
fn cascade_primary(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
important_rules_changed: bool) {
|
important_rules_changed: bool)
|
||||||
|
-> ChildCascadeRequirement {
|
||||||
// Collect some values.
|
// Collect some values.
|
||||||
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
let (mut styles, restyle) = data.styles_and_restyle_mut();
|
||||||
let mut primary_style = &mut styles.primary;
|
let mut primary_style = &mut styles.primary;
|
||||||
|
@ -589,16 +647,19 @@ trait PrivateMatchMethods: TElement {
|
||||||
important_rules_changed);
|
important_rules_changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(old) = old_values {
|
let child_cascade_requirement =
|
||||||
self.accumulate_damage(&context.shared,
|
self.accumulate_damage(&context.shared,
|
||||||
restyle.unwrap(),
|
restyle,
|
||||||
&old,
|
old_values.as_ref().map(|v| v.as_ref()),
|
||||||
&new_values,
|
&new_values,
|
||||||
None);
|
None);
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new computed values.
|
// Set the new computed values.
|
||||||
primary_style.values = Some(new_values);
|
primary_style.values = Some(new_values);
|
||||||
|
|
||||||
|
// Return whether the damage indicates we must cascade new inherited
|
||||||
|
// values into children.
|
||||||
|
child_cascade_requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cascade_eager_pseudo(&self,
|
fn cascade_eager_pseudo(&self,
|
||||||
|
@ -613,29 +674,11 @@ trait PrivateMatchMethods: TElement {
|
||||||
let new_values =
|
let new_values =
|
||||||
self.cascade_internal(context, &styles.primary, Some(pseudo_style));
|
self.cascade_internal(context, &styles.primary, Some(pseudo_style));
|
||||||
|
|
||||||
if let Some(old) = old_values {
|
|
||||||
// ::before and ::after are element-backed in Gecko, so they do the
|
|
||||||
// damage calculation for themselves, when there's an actual pseudo.
|
|
||||||
//
|
|
||||||
// NOTE(emilio): An alternative to this check is to just remove the
|
|
||||||
// pseudo-style entry here if new_values is display: none or has
|
|
||||||
// an ineffective content property...
|
|
||||||
//
|
|
||||||
// I think it's nice to handle it here instead for symmetry with how
|
|
||||||
// we handle display: none elements, but the other approach may be
|
|
||||||
// ok too?
|
|
||||||
let is_unexisting_before_or_after =
|
|
||||||
pseudo.is_before_or_after() &&
|
|
||||||
self.existing_style_for_restyle_damage(&old, Some(pseudo)).is_none();
|
|
||||||
|
|
||||||
if cfg!(feature = "servo") || is_unexisting_before_or_after {
|
|
||||||
self.accumulate_damage(&context.shared,
|
self.accumulate_damage(&context.shared,
|
||||||
restyle.unwrap(),
|
restyle,
|
||||||
&old,
|
old_values.as_ref().map(|v| &**v),
|
||||||
&new_values,
|
&new_values,
|
||||||
Some(pseudo));
|
Some(pseudo));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pseudo_style.values = Some(new_values)
|
pseudo_style.values = Some(new_values)
|
||||||
}
|
}
|
||||||
|
@ -794,54 +837,89 @@ trait PrivateMatchMethods: TElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes and applies restyle damage.
|
||||||
|
fn accumulate_damage(&self,
|
||||||
|
shared_context: &SharedStyleContext,
|
||||||
|
restyle: Option<&mut RestyleData>,
|
||||||
|
old_values: Option<&ComputedValues>,
|
||||||
|
new_values: &Arc<ComputedValues>,
|
||||||
|
pseudo: Option<&PseudoElement>)
|
||||||
|
-> ChildCascadeRequirement {
|
||||||
|
let restyle = match restyle {
|
||||||
|
Some(r) => r,
|
||||||
|
None => return ChildCascadeRequirement::MustCascade,
|
||||||
|
};
|
||||||
|
|
||||||
|
let old_values = match old_values {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return ChildCascadeRequirement::MustCascade,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ::before and ::after are element-backed in Gecko, so they do the
|
||||||
|
// damage calculation for themselves, when there's an actual pseudo.
|
||||||
|
let is_existing_before_or_after =
|
||||||
|
cfg!(feature = "gecko") &&
|
||||||
|
pseudo.map_or(false, |p| p.is_before_or_after()) &&
|
||||||
|
self.existing_style_for_restyle_damage(old_values, pseudo)
|
||||||
|
.is_some();
|
||||||
|
|
||||||
|
if is_existing_before_or_after {
|
||||||
|
return ChildCascadeRequirement::CanSkipCascade;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.accumulate_damage_for(shared_context,
|
||||||
|
restyle,
|
||||||
|
old_values,
|
||||||
|
new_values,
|
||||||
|
pseudo)
|
||||||
|
}
|
||||||
|
|
||||||
/// Computes and applies non-redundant damage.
|
/// Computes and applies non-redundant damage.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
fn accumulate_damage(&self,
|
fn accumulate_damage_for(&self,
|
||||||
shared_context: &SharedStyleContext,
|
shared_context: &SharedStyleContext,
|
||||||
restyle: &mut RestyleData,
|
restyle: &mut RestyleData,
|
||||||
old_values: &ComputedValues,
|
old_values: &ComputedValues,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>) {
|
pseudo: Option<&PseudoElement>)
|
||||||
|
-> ChildCascadeRequirement {
|
||||||
// Don't accumulate damage if we're in a restyle for reconstruction.
|
// Don't accumulate damage if we're in a restyle for reconstruction.
|
||||||
if shared_context.traversal_flags.for_reconstruct() {
|
if shared_context.traversal_flags.for_reconstruct() {
|
||||||
return;
|
return ChildCascadeRequirement::MustCascade;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If an ancestor is already getting reconstructed by Gecko's top-down
|
// If an ancestor is already getting reconstructed by Gecko's top-down
|
||||||
// frame constructor, no need to apply damage.
|
// frame constructor, no need to apply damage. Similarly if we already
|
||||||
if restyle.damage_handled.contains(RestyleDamage::reconstruct()) {
|
// have an explicitly stored ReconstructFrame hint.
|
||||||
restyle.damage = RestyleDamage::empty();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add restyle damage, but only the bits that aren't redundant with
|
|
||||||
// respect to damage applied on our ancestors.
|
|
||||||
//
|
//
|
||||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301258#c12
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1301258#c12
|
||||||
// for followup work to make the optimization here more optimal by
|
// for followup work to make the optimization here more optimal by considering
|
||||||
// considering each bit individually.
|
// each bit individually.
|
||||||
if !restyle.damage.contains(RestyleDamage::reconstruct()) {
|
let skip_applying_damage =
|
||||||
let new_damage = self.compute_restyle_damage(&old_values,
|
restyle.damage_handled.contains(RestyleDamage::reconstruct()) ||
|
||||||
|
restyle.damage.contains(RestyleDamage::reconstruct());
|
||||||
|
|
||||||
|
let difference = self.compute_style_difference(&old_values,
|
||||||
&new_values,
|
&new_values,
|
||||||
pseudo);
|
pseudo);
|
||||||
if !restyle.damage_handled.contains(new_damage) {
|
if !skip_applying_damage {
|
||||||
restyle.damage |= new_damage;
|
restyle.damage |= difference.damage;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
difference.change.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes and applies restyle damage unless we've already maxed it out.
|
/// Computes and applies restyle damage unless we've already maxed it out.
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
fn accumulate_damage(&self,
|
fn accumulate_damage_for(&self,
|
||||||
_shared_context: &SharedStyleContext,
|
_shared_context: &SharedStyleContext,
|
||||||
restyle: &mut RestyleData,
|
restyle: &mut RestyleData,
|
||||||
old_values: &ComputedValues,
|
old_values: &ComputedValues,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>) {
|
pseudo: Option<&PseudoElement>)
|
||||||
if restyle.damage != RestyleDamage::rebuild_and_reflow() {
|
-> ChildCascadeRequirement {
|
||||||
restyle.damage |=
|
let difference = self.compute_style_difference(&old_values, &new_values, pseudo);
|
||||||
self.compute_restyle_damage(&old_values, &new_values, pseudo);
|
restyle.damage |= difference.damage;
|
||||||
}
|
difference.change.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "servo")]
|
#[cfg(feature = "servo")]
|
||||||
|
@ -929,14 +1007,19 @@ pub trait MatchMethods : TElement {
|
||||||
fn match_and_cascade(&self,
|
fn match_and_cascade(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
data: &mut ElementData,
|
data: &mut ElementData,
|
||||||
sharing: StyleSharingBehavior)
|
sharing: StyleSharingBehavior) -> ChildCascadeRequirement
|
||||||
{
|
{
|
||||||
// Perform selector matching for the primary style.
|
// Perform selector matching for the primary style.
|
||||||
let mut relations = StyleRelations::empty();
|
let mut relations = StyleRelations::empty();
|
||||||
let result = self.match_primary(context, data, &mut relations);
|
let result = self.match_primary(context, data, &mut relations);
|
||||||
|
|
||||||
// Cascade properties and compute primary values.
|
// Cascade properties and compute primary values.
|
||||||
self.cascade_primary(context, data, result.important_rules_overriding_animation_changed);
|
let child_cascade_requirement =
|
||||||
|
self.cascade_primary(
|
||||||
|
context,
|
||||||
|
data,
|
||||||
|
result.important_rules_overriding_animation_changed
|
||||||
|
);
|
||||||
|
|
||||||
// Match and cascade eager pseudo-elements.
|
// Match and cascade eager pseudo-elements.
|
||||||
if !data.styles().is_display_none() {
|
if !data.styles().is_display_none() {
|
||||||
|
@ -969,6 +1052,8 @@ pub trait MatchMethods : TElement {
|
||||||
relations,
|
relations,
|
||||||
revalidation_match_results);
|
revalidation_match_results);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child_cascade_requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the cascade, without matching.
|
/// Performs the cascade, without matching.
|
||||||
|
@ -976,9 +1061,12 @@ pub trait MatchMethods : TElement {
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
mut data: &mut ElementData,
|
mut data: &mut ElementData,
|
||||||
important_rules_changed: bool)
|
important_rules_changed: bool)
|
||||||
|
-> ChildCascadeRequirement
|
||||||
{
|
{
|
||||||
|
let child_cascade_requirement =
|
||||||
self.cascade_primary(context, &mut data, important_rules_changed);
|
self.cascade_primary(context, &mut data, important_rules_changed);
|
||||||
self.cascade_pseudos(context, &mut data);
|
self.cascade_pseudos(context, &mut data);
|
||||||
|
child_cascade_requirement
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs selector matching to (re)compute the primary rule node for this element.
|
/// Runs selector matching to (re)compute the primary rule node for this element.
|
||||||
|
@ -1354,11 +1442,12 @@ pub trait MatchMethods : TElement {
|
||||||
debug_assert_eq!(data.has_styles(), data.has_restyle());
|
debug_assert_eq!(data.has_styles(), data.has_restyle());
|
||||||
let old_values = data.get_styles_mut()
|
let old_values = data.get_styles_mut()
|
||||||
.and_then(|s| s.primary.values.take());
|
.and_then(|s| s.primary.values.take());
|
||||||
if let Some(old) = old_values {
|
let child_cascade_requirement =
|
||||||
self.accumulate_damage(&context.shared,
|
self.accumulate_damage(&context.shared,
|
||||||
data.restyle_mut(), &old,
|
data.get_restyle_mut(),
|
||||||
shared_style.values(), None);
|
old_values.as_ref().map(|v| v.as_ref()),
|
||||||
}
|
shared_style.values(),
|
||||||
|
None);
|
||||||
|
|
||||||
// We never put elements with pseudo style into the style
|
// We never put elements with pseudo style into the style
|
||||||
// sharing cache, so we can just mint an ElementStyles
|
// sharing cache, so we can just mint an ElementStyles
|
||||||
|
@ -1368,7 +1457,7 @@ pub trait MatchMethods : TElement {
|
||||||
let styles = ElementStyles::new(shared_style);
|
let styles = ElementStyles::new(shared_style);
|
||||||
data.set_styles(styles);
|
data.set_styles(styles);
|
||||||
|
|
||||||
return StyleSharingResult::StyleWasShared(i)
|
return StyleSharingResult::StyleWasShared(i, child_cascade_requirement)
|
||||||
}
|
}
|
||||||
Err(miss) => {
|
Err(miss) => {
|
||||||
debug!("Cache miss: {:?}", miss);
|
debug!("Cache miss: {:?}", miss);
|
||||||
|
@ -1452,14 +1541,14 @@ pub trait MatchMethods : TElement {
|
||||||
/// Given the old and new style of this element, and whether it's a
|
/// Given the old and new style of this element, and whether it's a
|
||||||
/// pseudo-element, compute the restyle damage used to determine which
|
/// pseudo-element, compute the restyle damage used to determine which
|
||||||
/// kind of layout or painting operations we'll need.
|
/// kind of layout or painting operations we'll need.
|
||||||
fn compute_restyle_damage(&self,
|
fn compute_style_difference(&self,
|
||||||
old_values: &ComputedValues,
|
old_values: &ComputedValues,
|
||||||
new_values: &Arc<ComputedValues>,
|
new_values: &Arc<ComputedValues>,
|
||||||
pseudo: Option<&PseudoElement>)
|
pseudo: Option<&PseudoElement>)
|
||||||
-> RestyleDamage
|
-> StyleDifference
|
||||||
{
|
{
|
||||||
if let Some(source) = self.existing_style_for_restyle_damage(old_values, pseudo) {
|
if let Some(source) = self.existing_style_for_restyle_damage(old_values, pseudo) {
|
||||||
return RestyleDamage::compute(source, new_values);
|
return RestyleDamage::compute_style_difference(source, new_values)
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_style_is_display_none =
|
let new_style_is_display_none =
|
||||||
|
@ -1474,7 +1563,7 @@ pub trait MatchMethods : TElement {
|
||||||
// pseudo-elements.
|
// pseudo-elements.
|
||||||
if new_style_is_display_none && old_style_is_display_none {
|
if new_style_is_display_none && old_style_is_display_none {
|
||||||
// The style remains display:none. No need for damage.
|
// The style remains display:none. No need for damage.
|
||||||
return RestyleDamage::empty()
|
return StyleDifference::new(RestyleDamage::empty(), StyleChange::Unchanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pseudo.map_or(false, |p| p.is_before_or_after()) {
|
if pseudo.map_or(false, |p| p.is_before_or_after()) {
|
||||||
|
@ -1484,18 +1573,19 @@ pub trait MatchMethods : TElement {
|
||||||
new_values.ineffective_content_property()) {
|
new_values.ineffective_content_property()) {
|
||||||
// The pseudo-element will remain undisplayed, so just avoid
|
// The pseudo-element will remain undisplayed, so just avoid
|
||||||
// triggering any change.
|
// triggering any change.
|
||||||
return RestyleDamage::empty()
|
return StyleDifference::new(RestyleDamage::empty(), StyleChange::Unchanged)
|
||||||
}
|
}
|
||||||
return RestyleDamage::reconstruct()
|
return StyleDifference::new(RestyleDamage::reconstruct(), StyleChange::Changed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something else. Be conservative for now.
|
// Something else. Be conservative for now.
|
||||||
warn!("Reframing due to lack of old style source: {:?}, pseudo: {:?}",
|
warn!("Reframing due to lack of old style source: {:?}, pseudo: {:?}",
|
||||||
self, pseudo);
|
self, pseudo);
|
||||||
RestyleDamage::reconstruct()
|
// Something else. Be conservative for now.
|
||||||
|
StyleDifference::new(RestyleDamage::reconstruct(), StyleChange::Changed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cascade the eager pseudo-elements of this element.
|
/// Performs the cascade for the element's eager pseudos.
|
||||||
fn cascade_pseudos(&self,
|
fn cascade_pseudos(&self,
|
||||||
context: &mut StyleContext<Self>,
|
context: &mut StyleContext<Self>,
|
||||||
mut data: &mut ElementData)
|
mut data: &mut ElementData)
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
use computed_values::display;
|
use computed_values::display;
|
||||||
use heapsize::HeapSizeOf;
|
use heapsize::HeapSizeOf;
|
||||||
|
use matching::{StyleChange, StyleDifference};
|
||||||
use properties::ServoComputedValues;
|
use properties::ServoComputedValues;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
@ -57,12 +58,14 @@ impl HeapSizeOf for ServoRestyleDamage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServoRestyleDamage {
|
impl ServoRestyleDamage {
|
||||||
/// Compute the appropriate restyle damage for a given style change between
|
/// Compute the `StyleDifference` (including the appropriate restyle damage)
|
||||||
/// `old` and `new`.
|
/// for a given style change between `old` and `new`.
|
||||||
pub fn compute(old: &ServoComputedValues,
|
pub fn compute_style_difference(old: &ServoComputedValues,
|
||||||
new: &ServoComputedValues)
|
new: &ServoComputedValues)
|
||||||
-> ServoRestyleDamage {
|
-> StyleDifference {
|
||||||
compute_damage(old, new)
|
let damage = compute_damage(old, new);
|
||||||
|
let change = if damage.is_empty() { StyleChange::Unchanged } else { StyleChange::Changed };
|
||||||
|
StyleDifference::new(damage, change)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a bitmask that represents a flow that needs to be rebuilt and
|
/// Returns a bitmask that represents a flow that needs to be rebuilt and
|
||||||
|
|
|
@ -8,7 +8,7 @@ use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||||
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext};
|
||||||
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
use data::{ElementData, ElementStyles, StoredRestyleHint};
|
||||||
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
|
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
|
||||||
use matching::{MatchMethods, StyleSharingBehavior};
|
use matching::{ChildCascadeRequirement, MatchMethods, StyleSharingBehavior};
|
||||||
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
use restyle_hints::{RESTYLE_DESCENDANTS, RESTYLE_SELF};
|
||||||
use selector_parser::RestyleDamage;
|
use selector_parser::RestyleDamage;
|
||||||
#[cfg(feature = "servo")] use servo_config::opts;
|
#[cfg(feature = "servo")] use servo_config::opts;
|
||||||
|
@ -611,7 +611,12 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
|
|
||||||
// Compute style for this element if necessary.
|
// Compute style for this element if necessary.
|
||||||
if compute_self {
|
if compute_self {
|
||||||
compute_style(traversal, traversal_data, context, element, &mut data);
|
match compute_style(traversal, traversal_data, context, element, &mut data) {
|
||||||
|
ChildCascadeRequirement::MustCascade => {
|
||||||
|
inherited_style_changed = true;
|
||||||
|
}
|
||||||
|
ChildCascadeRequirement::CanSkipCascade => {}
|
||||||
|
};
|
||||||
|
|
||||||
// If we're restyling this element to display:none, throw away all style
|
// If we're restyling this element to display:none, throw away all style
|
||||||
// data in the subtree, notify the caller to early-return.
|
// data in the subtree, notify the caller to early-return.
|
||||||
|
@ -620,9 +625,6 @@ pub fn recalc_style_at<E, D>(traversal: &D,
|
||||||
element);
|
element);
|
||||||
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
clear_descendant_data(element, &|e| unsafe { D::clear_element_data(&e) });
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(bholley): Compute this accurately from the call to CalcStyleDifference.
|
|
||||||
inherited_style_changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now that matching and cascading is done, clear the bits corresponding to
|
// Now that matching and cascading is done, clear the bits corresponding to
|
||||||
|
@ -711,7 +713,7 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
traversal_data: &PerLevelTraversalData,
|
traversal_data: &PerLevelTraversalData,
|
||||||
context: &mut StyleContext<E>,
|
context: &mut StyleContext<E>,
|
||||||
element: E,
|
element: E,
|
||||||
mut data: &mut AtomicRefMut<ElementData>)
|
mut data: &mut AtomicRefMut<ElementData>) -> ChildCascadeRequirement
|
||||||
where E: TElement,
|
where E: TElement,
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
|
@ -727,10 +729,10 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
let sharing_result = unsafe {
|
let sharing_result = unsafe {
|
||||||
element.share_style_if_possible(context, &mut data)
|
element.share_style_if_possible(context, &mut data)
|
||||||
};
|
};
|
||||||
if let StyleWasShared(index) = sharing_result {
|
if let StyleWasShared(index, had_damage) = sharing_result {
|
||||||
context.thread_local.statistics.styles_shared += 1;
|
context.thread_local.statistics.styles_shared += 1;
|
||||||
context.thread_local.style_sharing_candidate_cache.touch(index);
|
context.thread_local.style_sharing_candidate_cache.touch(index);
|
||||||
return;
|
return had_damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,17 +746,28 @@ fn compute_style<E, D>(_traversal: &D,
|
||||||
context.thread_local.statistics.elements_matched += 1;
|
context.thread_local.statistics.elements_matched += 1;
|
||||||
|
|
||||||
// Perform the matching and cascading.
|
// Perform the matching and cascading.
|
||||||
element.match_and_cascade(context, &mut data, StyleSharingBehavior::Allow);
|
element.match_and_cascade(
|
||||||
|
context,
|
||||||
|
&mut data,
|
||||||
|
StyleSharingBehavior::Allow
|
||||||
|
)
|
||||||
}
|
}
|
||||||
CascadeWithReplacements(hint) => {
|
CascadeWithReplacements(flags) => {
|
||||||
let rules_changed = element.replace_rules(hint, context, &mut data);
|
let rules_changed = element.replace_rules(flags, context, &mut data);
|
||||||
element.cascade_primary_and_pseudos(context, &mut data,
|
element.cascade_primary_and_pseudos(
|
||||||
rules_changed.important_rules_changed());
|
context,
|
||||||
|
&mut data,
|
||||||
|
rules_changed.important_rules_changed()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
CascadeOnly => {
|
CascadeOnly => {
|
||||||
element.cascade_primary_and_pseudos(context, &mut data, false);
|
element.cascade_primary_and_pseudos(
|
||||||
|
context,
|
||||||
|
&mut data,
|
||||||
|
/* important_rules_changed = */ false
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preprocess_children<E, D>(traversal: &D,
|
fn preprocess_children<E, D>(traversal: &D,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue