1use 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 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 damage_from_children.contains(LayoutDamage::recollect_box_tree_children()) {
148 element_damage.insert(LayoutDamage::recollect_box_tree_children());
149 }
150
151 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 let damage_for_parent = element_damage | (damage_from_children & RestyleDamage::RELAYOUT);
160
161 if element_damage != RestyleDamage::reconstruct() &&
167 damage_for_parent.contains(RestyleDamage::RELAYOUT)
168 {
169 node.clear_fragment_layout_cache();
170 }
171
172 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}