layout/
traversal.rs

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
5use bitflags::Flags;
6use layout_api::LayoutDamage;
7use layout_api::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
8use script::layout_dom::ServoThreadSafeLayoutNode;
9use style::context::{SharedStyleContext, StyleContext};
10use style::data::ElementData;
11use style::dom::{NodeInfo, TElement, TNode};
12use style::selector_parser::RestyleDamage;
13use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
14use style::values::computed::Display;
15
16use crate::context::LayoutContext;
17use crate::dom::{DOMLayoutData, NodeExt};
18
19pub struct RecalcStyle<'a> {
20    context: &'a LayoutContext<'a>,
21}
22
23impl<'a> RecalcStyle<'a> {
24    pub(crate) fn new(context: &'a LayoutContext<'a>) -> Self {
25        RecalcStyle { context }
26    }
27
28    pub(crate) fn context(&self) -> &LayoutContext<'a> {
29        self.context
30    }
31}
32
33#[allow(unsafe_code)]
34impl<'dom, E> DomTraversal<E> for RecalcStyle<'_>
35where
36    E: TElement,
37    E::ConcreteNode: 'dom + LayoutNode<'dom>,
38{
39    fn process_preorder<F>(
40        &self,
41        traversal_data: &PerLevelTraversalData,
42        context: &mut StyleContext<E>,
43        node: E::ConcreteNode,
44        note_child: F,
45    ) where
46        F: FnMut(E::ConcreteNode),
47    {
48        if node.is_text_node() {
49            return;
50        }
51
52        let had_style_data = node.style_data().is_some();
53        unsafe {
54            node.initialize_style_and_layout_data::<DOMLayoutData>();
55        }
56
57        let element = node.as_element().unwrap();
58        let mut element_data = element.mutate_data().unwrap();
59
60        if !had_style_data {
61            element_data.damage = RestyleDamage::reconstruct();
62        }
63
64        recalc_style_at(
65            self,
66            traversal_data,
67            context,
68            element,
69            &mut element_data,
70            note_child,
71        );
72
73        unsafe {
74            element.unset_dirty_descendants();
75        }
76    }
77
78    #[inline]
79    fn needs_postorder_traversal() -> bool {
80        false
81    }
82
83    fn process_postorder(&self, _style_context: &mut StyleContext<E>, _node: E::ConcreteNode) {
84        panic!("this should never be called")
85    }
86
87    fn text_node_needs_traversal(node: E::ConcreteNode, parent_data: &ElementData) -> bool {
88        node.layout_data().is_none() || !parent_data.damage.is_empty()
89    }
90
91    fn shared_context(&self) -> &SharedStyleContext<'_> {
92        &self.context.style_context
93    }
94}
95
96#[servo_tracing::instrument(skip_all)]
97pub(crate) fn compute_damage_and_repair_style(
98    context: &SharedStyleContext,
99    node: ServoThreadSafeLayoutNode<'_>,
100    damage_from_environment: RestyleDamage,
101) -> RestyleDamage {
102    compute_damage_and_repair_style_inner(context, node, damage_from_environment)
103}
104
105pub(crate) fn compute_damage_and_repair_style_inner(
106    context: &SharedStyleContext,
107    node: ServoThreadSafeLayoutNode<'_>,
108    damage_from_parent: RestyleDamage,
109) -> RestyleDamage {
110    let mut element_damage;
111    let original_element_damage;
112    let element_data = &node
113        .style_data()
114        .expect("Should not run `compute_damage` before styling.")
115        .element_data;
116
117    {
118        let mut element_data = element_data.borrow_mut();
119        original_element_damage = element_data.damage;
120        element_damage = original_element_damage | damage_from_parent;
121
122        if let Some(ref style) = element_data.styles.primary {
123            if style.get_box().display == Display::None {
124                element_data.damage = element_damage;
125                return element_damage;
126            }
127        }
128    }
129
130    // If we are reconstructing this node, then all of the children should be reconstructed as well.
131    // Otherwise, do not propagate down its box damage.
132    let mut damage_for_children = element_damage;
133    if !element_damage.contains(LayoutDamage::rebuild_box_tree()) {
134        damage_for_children.truncate();
135    }
136
137    let mut damage_from_children = RestyleDamage::empty();
138    for child in node.children() {
139        if child.is_element() {
140            damage_from_children |=
141                compute_damage_and_repair_style_inner(context, child, damage_for_children);
142        }
143    }
144
145    // If one of our children needed to be reconstructed, we need to recollect children
146    // during box tree construction.
147    if damage_from_children.contains(LayoutDamage::recollect_box_tree_children()) {
148        element_damage.insert(LayoutDamage::recollect_box_tree_children());
149    }
150
151    // If this node's box will not be preserved, we need to relayout its box tree.
152    let element_layout_damage = LayoutDamage::from(element_damage);
153    if element_layout_damage.has_box_damage() {
154        element_damage.insert(RestyleDamage::RELAYOUT);
155    }
156
157    // Only propagate up layout phases from children, as other types of damage are
158    // incorporated into `element_damage` above.
159    let damage_for_parent = element_damage | (damage_from_children & RestyleDamage::RELAYOUT);
160
161    // If we are going to potentially reuse this box tree node, then clear any cached
162    // fragment layout.
163    //
164    // TODO: If this node has `recollect_box_tree_children` damage, this is unecessary
165    // unless it's entirely above the dirty root.
166    if element_damage != RestyleDamage::reconstruct() &&
167        damage_for_parent.contains(RestyleDamage::RELAYOUT)
168    {
169        node.clear_fragment_layout_cache();
170    }
171
172    // If the box will be preserved, update the box's style and also in any fragments
173    // that haven't been cleared. Meanwhile, clear the damage to avoid affecting the
174    // next reflow.
175    if !element_layout_damage.has_box_damage() {
176        if !original_element_damage.is_empty() {
177            node.repair_style(context);
178        }
179
180        element_damage = RestyleDamage::empty();
181    }
182
183    if element_damage != original_element_damage {
184        element_data.borrow_mut().damage = element_damage;
185    }
186
187    damage_for_parent
188}