diff options
Diffstat (limited to 'servo/components/style/servo/restyle_damage.rs')
-rw-r--r-- | servo/components/style/servo/restyle_damage.rs | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/servo/components/style/servo/restyle_damage.rs b/servo/components/style/servo/restyle_damage.rs new file mode 100644 index 0000000000..fe17fa6198 --- /dev/null +++ b/servo/components/style/servo/restyle_damage.rs @@ -0,0 +1,268 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! The restyle damage is a hint that tells layout which kind of operations may +//! be needed in presence of incremental style changes. + +use crate::computed_values::display::T as Display; +use crate::matching::{StyleChange, StyleDifference}; +use crate::properties::ComputedValues; +use std::fmt; + +bitflags! { + /// Individual layout actions that may be necessary after restyling. + pub struct ServoRestyleDamage: u8 { + /// Repaint the node itself. + /// + /// Currently unused; need to decide how this propagates. + const REPAINT = 0x01; + + /// The stacking-context-relative position of this node or its + /// descendants has changed. + /// + /// Propagates both up and down the flow tree. + const REPOSITION = 0x02; + + /// Recompute the overflow regions (bounding box of object and all descendants). + /// + /// Propagates down the flow tree because the computation is bottom-up. + const STORE_OVERFLOW = 0x04; + + /// Recompute intrinsic inline_sizes (minimum and preferred). + /// + /// Propagates down the flow tree because the computation is. + /// bottom-up. + const BUBBLE_ISIZES = 0x08; + + /// Recompute actual inline-sizes and block-sizes, only taking + /// out-of-flow children into account. + /// + /// Propagates up the flow tree because the computation is top-down. + const REFLOW_OUT_OF_FLOW = 0x10; + + /// Recompute actual inline_sizes and block_sizes. + /// + /// Propagates up the flow tree because the computation is top-down. + const REFLOW = 0x20; + + /// Re-resolve generated content. + /// + /// Propagates up the flow tree because the computation is inorder. + const RESOLVE_GENERATED_CONTENT = 0x40; + + /// The entire flow needs to be reconstructed. + const RECONSTRUCT_FLOW = 0x80; + } +} + +malloc_size_of_is_0!(ServoRestyleDamage); + +impl ServoRestyleDamage { + /// Compute the `StyleDifference` (including the appropriate restyle damage) + /// for a given style change between `old` and `new`. + pub fn compute_style_difference(old: &ComputedValues, new: &ComputedValues) -> StyleDifference { + let damage = compute_damage(old, new); + let change = if damage.is_empty() { + StyleChange::Unchanged + } else { + // FIXME(emilio): Differentiate between reset and inherited + // properties here, and set `reset_only` appropriately so the + // optimization to skip the cascade in those cases applies. + StyleChange::Changed { reset_only: false } + }; + StyleDifference { damage, change } + } + + /// Returns a bitmask that represents a flow that needs to be rebuilt and + /// reflowed. + /// + /// FIXME(bholley): Do we ever actually need this? Shouldn't + /// RECONSTRUCT_FLOW imply everything else? + pub fn rebuild_and_reflow() -> ServoRestyleDamage { + ServoRestyleDamage::REPAINT | + ServoRestyleDamage::REPOSITION | + ServoRestyleDamage::STORE_OVERFLOW | + ServoRestyleDamage::BUBBLE_ISIZES | + ServoRestyleDamage::REFLOW_OUT_OF_FLOW | + ServoRestyleDamage::REFLOW | + ServoRestyleDamage::RECONSTRUCT_FLOW + } + + /// Returns a bitmask indicating that the frame needs to be reconstructed. + pub fn reconstruct() -> ServoRestyleDamage { + ServoRestyleDamage::RECONSTRUCT_FLOW + } + + /// Supposing a flow has the given `position` property and this damage, + /// returns the damage that we should add to the *parent* of this flow. + pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> ServoRestyleDamage { + if child_is_absolutely_positioned { + self & (ServoRestyleDamage::REPAINT | + ServoRestyleDamage::REPOSITION | + ServoRestyleDamage::STORE_OVERFLOW | + ServoRestyleDamage::REFLOW_OUT_OF_FLOW | + ServoRestyleDamage::RESOLVE_GENERATED_CONTENT) + } else { + self & (ServoRestyleDamage::REPAINT | + ServoRestyleDamage::REPOSITION | + ServoRestyleDamage::STORE_OVERFLOW | + ServoRestyleDamage::REFLOW | + ServoRestyleDamage::REFLOW_OUT_OF_FLOW | + ServoRestyleDamage::RESOLVE_GENERATED_CONTENT) + } + } + + /// Supposing the *parent* of a flow with the given `position` property has + /// this damage, returns the damage that we should add to this flow. + pub fn damage_for_child( + self, + parent_is_absolutely_positioned: bool, + child_is_absolutely_positioned: bool, + ) -> ServoRestyleDamage { + match ( + parent_is_absolutely_positioned, + child_is_absolutely_positioned, + ) { + (false, true) => { + // Absolute children are out-of-flow and therefore insulated from changes. + // + // FIXME(pcwalton): Au contraire, if the containing block dimensions change! + self & (ServoRestyleDamage::REPAINT | ServoRestyleDamage::REPOSITION) + }, + (true, false) => { + // Changing the position of an absolutely-positioned block requires us to reflow + // its kids. + if self.contains(ServoRestyleDamage::REFLOW_OUT_OF_FLOW) { + self | ServoRestyleDamage::REFLOW + } else { + self + } + }, + _ => { + // TODO(pcwalton): Take floatedness into account. + self & (ServoRestyleDamage::REPAINT | + ServoRestyleDamage::REPOSITION | + ServoRestyleDamage::REFLOW) + }, + } + } +} + +impl Default for ServoRestyleDamage { + fn default() -> Self { + Self::empty() + } +} + +impl fmt::Display for ServoRestyleDamage { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let mut first_elem = true; + + let to_iter = [ + (ServoRestyleDamage::REPAINT, "Repaint"), + (ServoRestyleDamage::REPOSITION, "Reposition"), + (ServoRestyleDamage::STORE_OVERFLOW, "StoreOverflow"), + (ServoRestyleDamage::BUBBLE_ISIZES, "BubbleISizes"), + (ServoRestyleDamage::REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow"), + (ServoRestyleDamage::REFLOW, "Reflow"), + ( + ServoRestyleDamage::RESOLVE_GENERATED_CONTENT, + "ResolveGeneratedContent", + ), + (ServoRestyleDamage::RECONSTRUCT_FLOW, "ReconstructFlow"), + ]; + + for &(damage, damage_str) in &to_iter { + if self.contains(damage) { + if !first_elem { + write!(f, " | ")?; + } + write!(f, "{}", damage_str)?; + first_elem = false; + } + } + + if first_elem { + write!(f, "NoDamage")?; + } + + Ok(()) + } +} + +fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDamage { + let mut damage = ServoRestyleDamage::empty(); + + // This should check every CSS property, as enumerated in the fields of + // https://doc.servo.org/style/properties/struct.ComputedValues.html + + // This uses short-circuiting boolean OR for its side effects and ignores the result. + let _ = restyle_damage_rebuild_and_reflow!( + old, + new, + damage, + [ + ServoRestyleDamage::REPAINT, + ServoRestyleDamage::REPOSITION, + ServoRestyleDamage::STORE_OVERFLOW, + ServoRestyleDamage::BUBBLE_ISIZES, + ServoRestyleDamage::REFLOW_OUT_OF_FLOW, + ServoRestyleDamage::REFLOW, + ServoRestyleDamage::RECONSTRUCT_FLOW + ] + ) || (new.get_box().display == Display::Inline && + restyle_damage_rebuild_and_reflow_inline!( + old, + new, + damage, + [ + ServoRestyleDamage::REPAINT, + ServoRestyleDamage::REPOSITION, + ServoRestyleDamage::STORE_OVERFLOW, + ServoRestyleDamage::BUBBLE_ISIZES, + ServoRestyleDamage::REFLOW_OUT_OF_FLOW, + ServoRestyleDamage::REFLOW, + ServoRestyleDamage::RECONSTRUCT_FLOW + ] + )) || + restyle_damage_reflow!( + old, + new, + damage, + [ + ServoRestyleDamage::REPAINT, + ServoRestyleDamage::REPOSITION, + ServoRestyleDamage::STORE_OVERFLOW, + ServoRestyleDamage::BUBBLE_ISIZES, + ServoRestyleDamage::REFLOW_OUT_OF_FLOW, + ServoRestyleDamage::REFLOW + ] + ) || + restyle_damage_reflow_out_of_flow!( + old, + new, + damage, + [ + ServoRestyleDamage::REPAINT, + ServoRestyleDamage::REPOSITION, + ServoRestyleDamage::STORE_OVERFLOW, + ServoRestyleDamage::REFLOW_OUT_OF_FLOW + ] + ) || + restyle_damage_repaint!(old, new, damage, [ServoRestyleDamage::REPAINT]); + + // Paint worklets may depend on custom properties, + // so if they have changed we should repaint. + if !old.custom_properties_equal(new) { + damage.insert(ServoRestyleDamage::REPAINT); + } + + // If the layer requirements of this flow have changed due to the value + // of the transform, then reflow is required to rebuild the layers. + if old.transform_requires_layer() != new.transform_requires_layer() { + damage.insert(ServoRestyleDamage::rebuild_and_reflow()); + } + + damage +} |