tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }