restyle_damage.rs (7676B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! The restyle damage is a hint that tells layout which kind of operations may 6 //! be needed in presence of incremental style changes. 7 8 use bitflags::Flags; 9 10 use crate::computed_values::isolation::T as Isolation; 11 use crate::computed_values::mix_blend_mode::T as MixBlendMode; 12 use crate::computed_values::transform_style::T as TransformStyle; 13 use crate::dom::TElement; 14 use crate::matching::{StyleChange, StyleDifference}; 15 use crate::properties::{ 16 restyle_damage_rebuild_box, restyle_damage_rebuild_stacking_context, 17 restyle_damage_recalculate_overflow, restyle_damage_repaint, style_structs, ComputedValues, 18 }; 19 use crate::values::computed::basic_shape::ClipPath; 20 use crate::values::computed::Perspective; 21 use crate::values::generics::transform::{GenericRotate, GenericScale, GenericTranslate}; 22 use std::fmt; 23 24 bitflags! { 25 /// Major phases of layout that need to be run due to the damage to a node during restyling. In 26 /// addition to the 4 bytes used for that, the rest of the `u16` is exposed as an extension point 27 /// for users of the crate to add their own custom types of damage that correspond to the 28 /// layout system they are implementing. 29 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 30 pub struct ServoRestyleDamage: u16 { 31 /// Repaint the node itself. 32 /// 33 /// Propagates both up and down the flow tree. 34 const REPAINT = 0b0001; 35 36 /// Rebuilds the stacking contexts. 37 /// 38 /// Propagates both up and down the flow tree. 39 const REBUILD_STACKING_CONTEXT = 0b0011; 40 41 /// Recalculates the scrollable overflow. 42 /// 43 /// Propagates both up and down the flow tree. 44 const RECALCULATE_OVERFLOW = 0b0111; 45 46 /// Any other type of damage, which requires running layout again. 47 /// 48 /// Propagates both up and down the flow tree. 49 const RELAYOUT = 0b1111; 50 } 51 } 52 53 malloc_size_of::malloc_size_of_is_0!(ServoRestyleDamage); 54 55 impl ServoRestyleDamage { 56 /// Compute the `StyleDifference` (including the appropriate restyle damage) 57 /// for a given style change between `old` and `new`. 58 pub fn compute_style_difference<E: TElement>( 59 old: &ComputedValues, 60 new: &ComputedValues, 61 ) -> StyleDifference { 62 let mut damage = compute_damage(old, new); 63 if damage.is_empty() { 64 return StyleDifference { 65 damage, 66 change: StyleChange::Unchanged, 67 }; 68 } 69 70 if damage.contains(ServoRestyleDamage::RELAYOUT) { 71 damage |= E::compute_layout_damage(old, new); 72 } 73 74 // FIXME(emilio): Differentiate between reset and inherited 75 // properties here, and set `reset_only` appropriately so the 76 // optimization to skip the cascade in those cases applies. 77 let change = StyleChange::Changed { reset_only: false }; 78 79 StyleDifference { damage, change } 80 } 81 82 /// Returns a bitmask indicating that the frame needs to be reconstructed. 83 pub fn reconstruct() -> ServoRestyleDamage { 84 // There's no way of knowing what kind of damage system the embedder will use, but part of 85 // this interface is that a fully saturated restyle damage means to rebuild everything. 86 ServoRestyleDamage::from_bits_retain(<ServoRestyleDamage as Flags>::Bits::MAX) 87 } 88 } 89 90 impl Default for ServoRestyleDamage { 91 fn default() -> Self { 92 Self::empty() 93 } 94 } 95 96 impl fmt::Display for ServoRestyleDamage { 97 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 98 let mut first_elem = true; 99 100 let to_iter = [ 101 (ServoRestyleDamage::REPAINT, "Repaint"), 102 ( 103 ServoRestyleDamage::REBUILD_STACKING_CONTEXT, 104 "Rebuild stacking context", 105 ), 106 ( 107 ServoRestyleDamage::RECALCULATE_OVERFLOW, 108 "Recalculate overflow", 109 ), 110 (ServoRestyleDamage::RELAYOUT, "Relayout"), 111 ]; 112 113 for &(damage, damage_str) in &to_iter { 114 if self.contains(damage) { 115 if !first_elem { 116 write!(f, " | ")?; 117 } 118 write!(f, "{}", damage_str)?; 119 first_elem = false; 120 } 121 } 122 123 if first_elem { 124 write!(f, "NoDamage")?; 125 } 126 127 Ok(()) 128 } 129 } 130 131 fn augmented_restyle_damage_rebuild_box(old: &ComputedValues, new: &ComputedValues) -> bool { 132 let old_box = old.get_box(); 133 let new_box = new.get_box(); 134 restyle_damage_rebuild_box(old, new) 135 || old_box.original_display != new_box.original_display 136 || old_box.has_transform_or_perspective() != new_box.has_transform_or_perspective() 137 || old.get_effects().filter.0.is_empty() != new.get_effects().filter.0.is_empty() 138 } 139 140 fn augmented_restyle_damage_rebuild_stacking_context( 141 old: &ComputedValues, 142 new: &ComputedValues, 143 ) -> bool { 144 restyle_damage_rebuild_stacking_context(old, new) 145 || old.guarantees_stacking_context() != new.guarantees_stacking_context() 146 } 147 fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDamage { 148 let mut damage = ServoRestyleDamage::empty(); 149 150 // Damage flags higher up the if-else chain imply damage flags lower down the if-else chain, 151 // so we can skip the diffing process for later flags if an earlier flag is true 152 if augmented_restyle_damage_rebuild_box(old, new) { 153 damage.insert(ServoRestyleDamage::RELAYOUT) 154 } else if restyle_damage_recalculate_overflow(old, new) { 155 damage.insert(ServoRestyleDamage::RECALCULATE_OVERFLOW) 156 } else if augmented_restyle_damage_rebuild_stacking_context(old, new) { 157 damage.insert(ServoRestyleDamage::REBUILD_STACKING_CONTEXT); 158 } else if restyle_damage_repaint(old, new) { 159 damage.insert(ServoRestyleDamage::REPAINT); 160 } 161 // Paint worklets may depend on custom properties, so if they have changed we should repaint. 162 else if !old.custom_properties_equal(new) { 163 damage.insert(ServoRestyleDamage::REPAINT); 164 } 165 166 damage 167 } 168 169 impl ComputedValues { 170 /// Some properties establish a stacking context when they are set to a non-initial value. 171 /// In that case, the damage is only set to `ServoRestyleDamage::REPAINT` because we don't 172 /// need to rebuild stacking contexts when the style changes between different non-initial 173 /// values. This function checks whether any of these properties is set to a value that 174 /// guarantees a stacking context, so that we only do the work when this changes. 175 /// Note that it's still possible to establish a stacking context when this returns false. 176 pub fn guarantees_stacking_context(&self) -> bool { 177 self.get_effects().opacity != 1.0 178 || self.get_effects().mix_blend_mode != MixBlendMode::Normal 179 || self.get_svg().clip_path != ClipPath::None 180 || self.get_box().isolation == Isolation::Isolate 181 } 182 } 183 184 impl style_structs::Box { 185 /// Whether there is a non-default transform or perspective style set 186 pub fn has_transform_or_perspective(&self) -> bool { 187 !self.transform.0.is_empty() 188 || self.scale != GenericScale::None 189 || self.rotate != GenericRotate::None 190 || self.translate != GenericTranslate::None 191 || self.perspective != Perspective::None 192 || self.transform_style == TransformStyle::Preserve3d 193 } 194 }