1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/* 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/. */

use bitflags::bitflags;
use style::computed_values::float::T as Float;
use style::selector_parser::RestyleDamage;
use style::servo::restyle_damage::ServoRestyleDamage;

use crate::flow::{Flow, FlowFlags, GetBaseFlow};

/// Used in a flow traversal to indicate whether this re-layout should be incremental or not.
#[derive(Clone, Copy, PartialEq)]
pub enum RelayoutMode {
    Incremental,
    Force,
}

bitflags! {
    pub struct SpecialRestyleDamage: u8 {
        /// If this flag is set, we need to reflow the entire document. This is more or less a
        /// temporary hack to deal with cases that we don't handle incrementally yet.
        const REFLOW_ENTIRE_DOCUMENT = 0x01;
    }
}

impl dyn Flow {
    pub fn compute_layout_damage(&mut self) -> SpecialRestyleDamage {
        let mut special_damage = SpecialRestyleDamage::empty();
        let is_absolutely_positioned = self
            .base()
            .flags
            .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);

        // In addition to damage, we use this phase to compute whether nodes affect CSS counters.
        let mut has_counter_affecting_children = false;

        {
            let self_base = self.mut_base();
            // Take a snapshot of the parent damage before updating it with damage from children.
            let parent_damage = self_base.restyle_damage;

            for kid in self_base.children.iter_mut() {
                let child_is_absolutely_positioned = kid
                    .base()
                    .flags
                    .contains(FlowFlags::IS_ABSOLUTELY_POSITIONED);
                kid.mut_base().restyle_damage.insert(
                    parent_damage
                        .damage_for_child(is_absolutely_positioned, child_is_absolutely_positioned),
                );
                {
                    let kid: &mut dyn Flow = kid;
                    special_damage.insert(kid.compute_layout_damage());
                }
                self_base.restyle_damage.insert(
                    kid.base()
                        .restyle_damage
                        .damage_for_parent(child_is_absolutely_positioned),
                );

                has_counter_affecting_children = has_counter_affecting_children ||
                    kid.base().flags.intersects(
                        FlowFlags::AFFECTS_COUNTERS | FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN,
                    );
            }
        }

        let self_base = self.mut_base();
        if self_base.flags.float_kind() != Float::None &&
            self_base
                .restyle_damage
                .intersects(ServoRestyleDamage::REFLOW)
        {
            special_damage.insert(SpecialRestyleDamage::REFLOW_ENTIRE_DOCUMENT);
        }

        if has_counter_affecting_children {
            self_base
                .flags
                .insert(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
        } else {
            self_base
                .flags
                .remove(FlowFlags::HAS_COUNTER_AFFECTING_CHILDREN)
        }

        special_damage
    }

    pub fn reflow_entire_document(&mut self) {
        let self_base = self.mut_base();
        self_base
            .restyle_damage
            .insert(RestyleDamage::rebuild_and_reflow());
        self_base
            .restyle_damage
            .remove(ServoRestyleDamage::RECONSTRUCT_FLOW);
        for kid in self_base.children.iter_mut() {
            kid.reflow_entire_document();
        }
    }
}