1use apace_traits::node_id::NodeId;
12use std::cell::Cell;
13use std::ops::Range;
14use stylo_takia::CopyStyleGetters;
15use thin_vec::ThinVec;
16
17use crate::layout::damage::ALL_DAMAGE;
18use crate::tree::NodeTree;
19use crate::{BaseDocument, Node};
20use style::properties::generated::longhands::position::computed_value::T as Position;
21use style::selector_parser::RestyleDamage;
22use style::values::computed::Float;
23use style::values::specified::box_::DisplayInside;
24use takia::{Point, Rect};
25
26pub fn hoisted_child_position(tree: &NodeTree, sc_root_id: NodeId, child_id: NodeId) -> Point<f32> {
35 let child = &tree[child_id];
36 let mut position = Point::ZERO;
37 let mut current = child.containing_block();
38
39 if child.takia_position() == takia::Position::Fixed
44 && let Some(cb) = current.and_then(|id| tree.get(id))
45 && cb.containing_block().is_none()
46 && !cb.flags.is_top_layer()
47 {
48 let scroll_offset = *cb.scroll_offset();
49 let viewport_scroll = tree.viewport_scroll();
50 position.x += scroll_offset.x as f32 + viewport_scroll.x as f32;
51 position.y += scroll_offset.y as f32 + viewport_scroll.y as f32;
52 }
53
54 while let Some(id) = current {
55 if id == sc_root_id {
56 break;
57 }
58 let Some(node) = tree.get(id) else {
59 break;
60 };
61 let location = node.final_layout().location;
62 let scroll_offset = *node.scroll_offset();
63 position.x += location.x - scroll_offset.x as f32;
64 position.y += location.y - scroll_offset.y as f32;
65 current = node.containing_block();
66 }
67 position
68}
69
70#[derive(Debug, Clone)]
72pub struct HoistedPaintChild {
73 pub node_id: NodeId,
74 pub z_index: i32,
75 position: Cell<(u64, Point<f32>)>,
78}
79
80impl HoistedPaintChild {
81 fn new(node_id: NodeId, z_index: i32) -> Self {
82 Self {
83 node_id,
84 z_index,
85 position: Cell::new((0, Point::ZERO)),
86 }
87 }
88
89 pub fn position(&self, tree: &NodeTree, sc_root_id: NodeId) -> Point<f32> {
93 let generation = tree.geometry_generation();
94 let (stamp, cached) = self.position.get();
95 if stamp == generation {
96 return cached;
97 }
98 let position = hoisted_child_position(tree, sc_root_id, self.node_id);
99 self.position.set((generation, position));
100 position
101 }
102
103 pub fn position_is_cached(&self, tree: &NodeTree) -> bool {
105 self.position.get().0 == tree.geometry_generation()
106 }
107}
108
109#[derive(Debug)]
110pub struct StackingContext {
111 pub children: ThinVec<HoistedPaintChild>,
112 pub negative_z_count: u32,
114 hoisted_content_bbox: Cell<(u64, Rect<f32>)>,
117}
118
119impl StackingContext {
120 pub(crate) fn retire_child(&mut self, node_id: NodeId) {
123 self.children.retain(|child| child.node_id != node_id);
124 self.negative_z_count = self.children.iter().take_while(|c| c.z_index < 0).count() as u32;
125 self.hoisted_content_bbox.set((0, Rect::ZERO));
126 }
127
128 fn from_children(children: ThinVec<HoistedPaintChild>) -> Self {
130 let mut sc = Self {
131 children,
132 negative_z_count: 0,
133 hoisted_content_bbox: Cell::new((0, Rect::ZERO)),
134 };
135 sc.sort();
136 sc
137 }
138
139 pub fn hoisted_content_bbox(
145 &self,
146 tree: &NodeTree,
147 sc_root_id: NodeId,
148 scale: f64,
149 ) -> Rect<f32> {
150 let generation = tree.geometry_generation();
151 let (stamp, cached) = self.hoisted_content_bbox.get();
152 if stamp == generation {
153 return cached;
154 }
155
156 let child_rect = |child: &HoistedPaintChild| -> Option<Rect<f32>> {
157 let node = tree.get(child.node_id)?;
158 let position = child.position(tree, sc_root_id);
159 let location = node.final_layout().location;
160 let mut overflow = *node.scrollable_overflow();
161 if let Some(transform) = node.transform().as_deref() {
162 overflow = transform.transform_rect_bbox(overflow);
163 }
164 let left = position.x + location.x;
165 let top = position.y + location.y;
166 Some(Rect {
167 left: left + (overflow.x0 / scale) as f32,
168 top: top + (overflow.y0 / scale) as f32,
169 right: left + (overflow.x1 / scale) as f32,
170 bottom: top + (overflow.y1 / scale) as f32,
171 })
172 };
173
174 let mut rects = self.children.iter().filter_map(child_rect);
177 let mut area = rects.next().unwrap_or(Rect::ZERO);
178 for pos in rects {
179 area.left = area.left.min(pos.left);
180 area.top = area.top.min(pos.top);
181 area.right = area.right.max(pos.right);
182 area.bottom = area.bottom.max(pos.bottom);
183 }
184 self.hoisted_content_bbox.set((generation, area));
185 area
186 }
187
188 pub fn sort(&mut self) {
189 self.children.sort_by_key(|c| c.z_index);
190 self.negative_z_count = self.children.iter().take_while(|c| c.z_index < 0).count() as u32;
191 }
192
193 pub fn neg_z_range(&self) -> Range<usize> {
194 0..(self.negative_z_count as usize)
195 }
196
197 pub fn pos_z_range(&self) -> Range<usize> {
198 (self.negative_z_count as usize)..self.children.len()
199 }
200
201 pub fn neg_z_hoisted_children(
202 &self,
203 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
204 self.children[self.neg_z_range()].iter()
205 }
206
207 pub fn pos_z_hoisted_children(
208 &self,
209 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
210 self.children[self.pos_z_range()].iter()
211 }
212}
213
214impl BaseDocument {
215 pub(crate) fn build_paint_tree(&mut self, root_id: NodeId) {
226 self.build_paint_tree_impl(root_id, None);
227 }
228
229 fn build_paint_tree_impl(
232 &mut self,
233 node_id: NodeId,
234 stacking_context: Option<&mut ThinVec<HoistedPaintChild>>,
235 ) {
236 {
237 let node = &self.nodes[node_id];
238 let role_unchanged = node.stacking_context.is_some() == stacking_context.is_none();
250 let clean = role_unchanged
251 && !node
252 .damage()
253 .unwrap_or(ALL_DAMAGE)
254 .contains(RestyleDamage::REBUILD_STACKING_CONTEXT)
255 && !node.is_anonymous();
256 if clean {
257 if let Some(stacking_context) = stacking_context {
258 stacking_context.extend(node.sc_contribution_cache.borrow().iter().cloned());
259 }
260 return;
261 }
262 }
263
264 let Some(display) = self.nodes[node_id].display_style() else {
265 return;
266 };
267
268 let mut entries = {
273 let node = &mut self.nodes[node_id];
274 let mut entries = match stacking_context {
275 None => node
276 .stacking_context
277 .take()
278 .map(|sc| sc.children)
279 .unwrap_or_default(),
280 Some(_) => std::mem::take(&mut *node.sc_contribution_cache.borrow_mut()),
281 };
282 entries.clear();
283 entries
284 };
285
286 let children = self.nodes[node_id].layout_children.borrow_mut().take();
287 if let Some(children) = children {
288 let is_flex_or_grid =
289 matches!(display.inside(), DisplayInside::Flex | DisplayInside::Grid);
290
291 let owned_here = |doc: &Self, child_id: NodeId| -> bool {
297 doc.nodes[child_id]
298 .oof_containing_block
299 .get()
300 .is_none_or(|cb| cb == node_id)
301 };
302 let mut hoisted_past_parent: Vec<(usize, NodeId)> = self.nodes[node_id]
308 .hoisted_children
309 .borrow()
310 .iter()
311 .copied()
312 .filter(|&id| {
313 self.nodes
314 .get(id)
315 .is_some_and(|n| n.layout_parent.get() != Some(node_id))
316 })
317 .map(|id| {
318 let anchor = self
319 .layout_child_containing(node_id, id)
320 .and_then(|anchor| children.iter().position(|&c| c == anchor))
321 .unwrap_or(children.len());
322 (anchor, id)
323 })
324 .collect();
325 hoisted_past_parent.sort_by_key(|&(anchor, _)| anchor);
326 let mut next_hoisted = 0;
327
328 let mut paint_children = self.nodes[node_id]
329 .paint_children
330 .borrow_mut()
331 .take()
332 .unwrap_or_default();
333 paint_children.clear();
334 paint_children.reserve(children.len() + hoisted_past_parent.len());
335
336 for (index, &child_id) in children.iter().enumerate() {
337 if owned_here(self, child_id) {
338 let is_sc_root = self.nodes[child_id].is_stacking_context_root(is_flex_or_grid);
339 self.build_paint_tree_impl(
340 child_id,
341 match is_sc_root {
342 true => None,
343 false => Some(&mut entries),
344 },
345 );
346
347 let z_index = self.nodes[child_id].z_index();
351 if is_sc_root && z_index != 0 {
352 entries.push(HoistedPaintChild::new(child_id, z_index))
353 } else {
354 paint_children.push(child_id);
355 }
356 }
357
358 while let Some(&(anchor, hoisted_id)) = hoisted_past_parent.get(next_hoisted)
359 && anchor == index
360 {
361 self.attach_hoisted_past_parent(hoisted_id, &mut paint_children, &mut entries);
362 next_hoisted += 1;
363 }
364 }
365 for &(_, hoisted_id) in &hoisted_past_parent[next_hoisted..] {
366 self.attach_hoisted_past_parent(hoisted_id, &mut paint_children, &mut entries);
367 }
368
369 paint_children
370 .sort_by_cached_key(|id| node_to_paint_order(&self.nodes[*id], is_flex_or_grid));
371
372 let node = &self.nodes[node_id];
373 *node.paint_children.borrow_mut() = Some(paint_children);
374 *node.layout_children.borrow_mut() = Some(children);
375 }
376
377 let node = &mut self.nodes[node_id];
378 if let Some(stacking_context) = stacking_context {
379 node.stacking_context = None;
380 stacking_context.extend(entries.iter().cloned());
381 *node.sc_contribution_cache.borrow_mut() = entries;
382 } else {
383 node.sc_contribution_cache.borrow_mut().clear();
384 node.stacking_context = Some(Box::new(StackingContext::from_children(entries)));
385 }
386 }
387
388 fn layout_child_containing(&self, node_id: NodeId, id: NodeId) -> Option<NodeId> {
391 let mut current = id;
392 loop {
393 let parent = self.nodes.get(current)?.layout_parent.get()?;
394 if parent == node_id {
395 return Some(current);
396 }
397 current = parent;
398 }
399 }
400
401 fn attach_hoisted_past_parent(
407 &mut self,
408 child_id: NodeId,
409 paint_children: &mut ThinVec<NodeId>,
410 entries: &mut ThinVec<HoistedPaintChild>,
411 ) {
412 self.build_paint_tree_impl(
413 child_id,
414 match self.nodes[child_id].is_stacking_context_root(false) {
415 true => None,
416 false => Some(entries),
417 },
418 );
419
420 let z_index = self.nodes[child_id].z_index();
421 if z_index != 0 {
422 entries.push(HoistedPaintChild::new(child_id, z_index));
423 } else {
424 paint_children.push(child_id);
425 }
426 }
427}
428
429#[inline(always)]
430fn position_to_order(pos: Position) -> i32 {
431 match pos {
432 Position::Static => 0,
433 Position::Relative | Position::Sticky | Position::Absolute | Position::Fixed => 2,
437 }
438}
439#[inline(always)]
440fn float_to_order(pos: Float) -> i32 {
441 match pos {
442 Float::None => 0,
443 _ => 1,
444 }
445}
446
447#[inline(always)]
452fn node_to_paint_order(node: &Node, is_flex_or_grid: bool) -> (i32, i32) {
453 let Some(style) = node.primary_styles() else {
454 return (0, 0);
455 };
456 let position = style.clone_position();
457 if is_flex_or_grid {
458 match position {
459 Position::Static => (0, style.clone_order()),
460 Position::Relative | Position::Sticky => (2, style.clone_order()),
461 Position::Absolute | Position::Fixed => (2, 0),
464 }
465 } else {
466 (
467 position_to_order(position) + float_to_order(style.clone_float()),
468 0,
469 )
470 }
471}