1use apace_traits::node_id::NodeId;
12use std::cell::Cell;
13use std::ops::Range;
14use thin_vec::ThinVec;
15
16use crate::layout::damage::ALL_DAMAGE;
17use crate::tree::NodeTree;
18use crate::{BaseDocument, Node};
19use style::properties::generated::longhands::position::computed_value::T as Position;
20use style::selector_parser::RestyleDamage;
21use style::values::computed::Float;
22use style::values::specified::box_::DisplayInside;
23use takia::{Point, Rect};
24
25pub fn hoisted_child_position(tree: &NodeTree, sc_root_id: NodeId, child_id: NodeId) -> Point<f32> {
34 let child = &tree[child_id];
35 let mut position = Point::ZERO;
36 let mut current = child.containing_block();
37
38 if child.takia_position() == takia::Position::Fixed
43 && let Some(cb) = current.and_then(|id| tree.get(id))
44 && cb.containing_block().is_none()
45 && !cb.flags.is_top_layer()
46 {
47 let scroll_offset = *cb.scroll_offset();
48 let viewport_scroll = tree.viewport_scroll();
49 position.x += scroll_offset.x as f32 + viewport_scroll.x as f32;
50 position.y += scroll_offset.y as f32 + viewport_scroll.y as f32;
51 }
52
53 while let Some(id) = current {
54 if id == sc_root_id {
55 break;
56 }
57 let Some(node) = tree.get(id) else {
58 break;
59 };
60 let location = node.final_layout().location;
61 let scroll_offset = *node.scroll_offset();
62 position.x += location.x - scroll_offset.x as f32;
63 position.y += location.y - scroll_offset.y as f32;
64 current = node.containing_block();
65 }
66 position
67}
68
69#[derive(Debug, Clone)]
71pub struct HoistedPaintChild {
72 pub node_id: NodeId,
73 pub z_index: i32,
74 position: Cell<(u64, Point<f32>)>,
77}
78
79impl HoistedPaintChild {
80 fn new(node_id: NodeId, z_index: i32) -> Self {
81 Self {
82 node_id,
83 z_index,
84 position: Cell::new((0, Point::ZERO)),
85 }
86 }
87
88 pub fn position(&self, tree: &NodeTree, sc_root_id: NodeId) -> Point<f32> {
92 let generation = tree.geometry_generation();
93 let (stamp, cached) = self.position.get();
94 if stamp == generation {
95 return cached;
96 }
97 let position = hoisted_child_position(tree, sc_root_id, self.node_id);
98 self.position.set((generation, position));
99 position
100 }
101
102 pub fn position_is_cached(&self, tree: &NodeTree) -> bool {
104 self.position.get().0 == tree.geometry_generation()
105 }
106}
107
108#[derive(Debug)]
109pub struct StackingContext {
110 pub children: ThinVec<HoistedPaintChild>,
111 pub negative_z_count: u32,
113 hoisted_content_bbox: Cell<(u64, Rect<f32>)>,
116}
117
118impl StackingContext {
119 fn from_children(children: ThinVec<HoistedPaintChild>) -> Self {
121 let mut sc = Self {
122 children,
123 negative_z_count: 0,
124 hoisted_content_bbox: Cell::new((0, Rect::ZERO)),
125 };
126 sc.sort();
127 sc
128 }
129
130 pub fn hoisted_content_bbox(
136 &self,
137 tree: &NodeTree,
138 sc_root_id: NodeId,
139 scale: f64,
140 ) -> Rect<f32> {
141 let generation = tree.geometry_generation();
142 let (stamp, cached) = self.hoisted_content_bbox.get();
143 if stamp == generation {
144 return cached;
145 }
146
147 let child_rect = |child: &HoistedPaintChild| -> Rect<f32> {
148 let node = &tree[child.node_id];
149 let position = child.position(tree, sc_root_id);
150 let location = node.final_layout().location;
151 let mut overflow = *node.scrollable_overflow();
152 if let Some(transform) = node.transform().as_deref() {
153 overflow = transform.transform_rect_bbox(overflow);
154 }
155 let left = position.x + location.x;
156 let top = position.y + location.y;
157 Rect {
158 left: left + (overflow.x0 / scale) as f32,
159 top: top + (overflow.y0 / scale) as f32,
160 right: left + (overflow.x1 / scale) as f32,
161 bottom: top + (overflow.y1 / scale) as f32,
162 }
163 };
164
165 let mut area = Rect::ZERO;
166 if let Some((first, rest)) = self.children.split_first() {
167 area = child_rect(first);
168 for child in rest {
169 let pos = child_rect(child);
170 area.left = area.left.min(pos.left);
171 area.top = area.top.min(pos.top);
172 area.right = area.right.max(pos.right);
173 area.bottom = area.bottom.max(pos.bottom);
174 }
175 }
176 self.hoisted_content_bbox.set((generation, area));
177 area
178 }
179
180 pub fn sort(&mut self) {
181 self.children.sort_by_key(|c| c.z_index);
182 self.negative_z_count = self.children.iter().take_while(|c| c.z_index < 0).count() as u32;
183 }
184
185 pub fn neg_z_range(&self) -> Range<usize> {
186 0..(self.negative_z_count as usize)
187 }
188
189 pub fn pos_z_range(&self) -> Range<usize> {
190 (self.negative_z_count as usize)..self.children.len()
191 }
192
193 pub fn neg_z_hoisted_children(
194 &self,
195 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
196 self.children[self.neg_z_range()].iter()
197 }
198
199 pub fn pos_z_hoisted_children(
200 &self,
201 ) -> impl ExactSizeIterator<Item = &HoistedPaintChild> + DoubleEndedIterator {
202 self.children[self.pos_z_range()].iter()
203 }
204}
205
206impl BaseDocument {
207 pub(crate) fn build_paint_tree(&mut self, root_id: NodeId) {
218 self.build_paint_tree_impl(root_id, None);
219 }
220
221 fn build_paint_tree_impl(
224 &mut self,
225 node_id: NodeId,
226 stacking_context: Option<&mut ThinVec<HoistedPaintChild>>,
227 ) {
228 {
229 let node = &self.nodes[node_id];
230 let role_unchanged = node.stacking_context.is_some() == stacking_context.is_none();
242 let clean = role_unchanged
243 && !node
244 .damage()
245 .unwrap_or(ALL_DAMAGE)
246 .contains(RestyleDamage::REBUILD_STACKING_CONTEXT)
247 && !node.is_anonymous();
248 if clean {
249 if let Some(stacking_context) = stacking_context {
250 stacking_context.extend(node.sc_contribution_cache.borrow().iter().cloned());
251 }
252 return;
253 }
254 }
255
256 let Some(display) = self.nodes[node_id].display_style() else {
257 return;
258 };
259
260 let mut entries = {
265 let node = &mut self.nodes[node_id];
266 let mut entries = match stacking_context {
267 None => node
268 .stacking_context
269 .take()
270 .map(|sc| sc.children)
271 .unwrap_or_default(),
272 Some(_) => std::mem::take(&mut *node.sc_contribution_cache.borrow_mut()),
273 };
274 entries.clear();
275 entries
276 };
277
278 let children = self.nodes[node_id].layout_children.borrow_mut().take();
279 if let Some(children) = children {
280 let is_flex_or_grid =
281 matches!(display.inside(), DisplayInside::Flex | DisplayInside::Grid);
282
283 let owned_here = |doc: &Self, child_id: NodeId| -> bool {
289 doc.nodes[child_id]
290 .oof_containing_block
291 .get()
292 .is_none_or(|cb| cb == node_id)
293 };
294 let mut hoisted_past_parent: Vec<(usize, NodeId)> = self.nodes[node_id]
300 .hoisted_children
301 .borrow()
302 .iter()
303 .copied()
304 .filter(|&id| {
305 self.nodes
306 .get(id)
307 .is_some_and(|n| n.layout_parent.get() != Some(node_id))
308 })
309 .map(|id| {
310 let anchor = self
311 .layout_child_containing(node_id, id)
312 .and_then(|anchor| children.iter().position(|&c| c == anchor))
313 .unwrap_or(children.len());
314 (anchor, id)
315 })
316 .collect();
317 hoisted_past_parent.sort_by_key(|&(anchor, _)| anchor);
318 let mut next_hoisted = 0;
319
320 let mut paint_children = self.nodes[node_id]
321 .paint_children
322 .borrow_mut()
323 .take()
324 .unwrap_or_default();
325 paint_children.clear();
326 paint_children.reserve(children.len() + hoisted_past_parent.len());
327
328 for (index, &child_id) in children.iter().enumerate() {
329 if owned_here(self, child_id) {
330 let is_sc_root = self.nodes[child_id].is_stacking_context_root(is_flex_or_grid);
331 self.build_paint_tree_impl(
332 child_id,
333 match is_sc_root {
334 true => None,
335 false => Some(&mut entries),
336 },
337 );
338
339 let z_index = self.nodes[child_id].z_index();
343 if is_sc_root && z_index != 0 {
344 entries.push(HoistedPaintChild::new(child_id, z_index))
345 } else {
346 paint_children.push(child_id);
347 }
348 }
349
350 while let Some(&(anchor, hoisted_id)) = hoisted_past_parent.get(next_hoisted)
351 && anchor == index
352 {
353 self.attach_hoisted_past_parent(hoisted_id, &mut paint_children, &mut entries);
354 next_hoisted += 1;
355 }
356 }
357 for &(_, hoisted_id) in &hoisted_past_parent[next_hoisted..] {
358 self.attach_hoisted_past_parent(hoisted_id, &mut paint_children, &mut entries);
359 }
360
361 paint_children
362 .sort_by_cached_key(|id| node_to_paint_order(&self.nodes[*id], is_flex_or_grid));
363
364 let node = &self.nodes[node_id];
365 *node.paint_children.borrow_mut() = Some(paint_children);
366 *node.layout_children.borrow_mut() = Some(children);
367 }
368
369 let node = &mut self.nodes[node_id];
370 if let Some(stacking_context) = stacking_context {
371 node.stacking_context = None;
372 stacking_context.extend(entries.iter().cloned());
373 *node.sc_contribution_cache.borrow_mut() = entries;
374 } else {
375 node.sc_contribution_cache.borrow_mut().clear();
376 node.stacking_context = Some(Box::new(StackingContext::from_children(entries)));
377 }
378 }
379
380 fn layout_child_containing(&self, node_id: NodeId, id: NodeId) -> Option<NodeId> {
383 let mut current = id;
384 loop {
385 let parent = self.nodes.get(current)?.layout_parent.get()?;
386 if parent == node_id {
387 return Some(current);
388 }
389 current = parent;
390 }
391 }
392
393 fn attach_hoisted_past_parent(
399 &mut self,
400 child_id: NodeId,
401 paint_children: &mut ThinVec<NodeId>,
402 entries: &mut ThinVec<HoistedPaintChild>,
403 ) {
404 self.build_paint_tree_impl(
405 child_id,
406 match self.nodes[child_id].is_stacking_context_root(false) {
407 true => None,
408 false => Some(entries),
409 },
410 );
411
412 let z_index = self.nodes[child_id].z_index();
413 if z_index != 0 {
414 entries.push(HoistedPaintChild::new(child_id, z_index));
415 } else {
416 paint_children.push(child_id);
417 }
418 }
419}
420
421#[inline(always)]
422fn position_to_order(pos: Position) -> i32 {
423 match pos {
424 Position::Static => 0,
425 Position::Relative | Position::Sticky | Position::Absolute | Position::Fixed => 2,
429 }
430}
431#[inline(always)]
432fn float_to_order(pos: Float) -> i32 {
433 match pos {
434 Float::None => 0,
435 _ => 1,
436 }
437}
438
439#[inline(always)]
444fn node_to_paint_order(node: &Node, is_flex_or_grid: bool) -> (i32, i32) {
445 let Some(style) = node.primary_styles() else {
446 return (0, 0);
447 };
448 let position = style.clone_position();
449 if is_flex_or_grid {
450 match position {
451 Position::Static => (0, style.clone_order()),
452 Position::Relative | Position::Sticky => (2, style.clone_order()),
453 Position::Absolute | Position::Fixed => (2, 0),
456 }
457 } else {
458 (
459 position_to_order(position) + float_to_order(style.clone_float()),
460 0,
461 )
462 }
463}