summaryrefslogtreecommitdiffstats
path: root/servo/components/style/servo/restyle_damage.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/servo/restyle_damage.rs')
-rw-r--r--servo/components/style/servo/restyle_damage.rs268
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
+}