1use std::rc::Rc;
6use std::sync::Arc;
7
8use app_units::Au;
9use servo_base::id::ScrollTreeNodeId;
10use style::values::computed::TextDecorationLine;
11
12use crate::display_list::{
13 ClipId, FragmentTextDecoration, StackingContext, StackingContextFragments,
14};
15use crate::fragment_tree::{
16 BoxFragment, BoxFragmentWithStyle, Fragment, FragmentFlags, IFrameFragment, ImageFragment,
17 PositioningFragment, Tag, TextFragment,
18};
19use crate::geom::{PhysicalPoint, PhysicalRect};
20
21pub(crate) struct PaintTraversal<'a, Handler: PaintTraversalHandler> {
22 handler: &'a mut Handler,
23 outlines: Vec<(TraversalState, Arc<BoxFragment>)>,
24 floats: Vec<(TraversalState, Arc<BoxFragment>)>,
25}
26
27impl<'a, Handler: PaintTraversalHandler> PaintTraversal<'a, Handler> {
28 pub(crate) fn traverse(root_stacking_context: &StackingContext, handler: &'a mut Handler) {
29 Self {
30 handler,
31 outlines: Vec::new(),
32 floats: Vec::new(),
33 }
34 .traverse_stacking_context(&TraversalState::default(), root_stacking_context);
35 }
36
37 fn traverse_stacking_context(
39 &mut self,
40 state: &TraversalState,
41 stacking_context: &StackingContext,
42 ) {
43 let old_outlines_length = self.outlines.len();
44 let state = state.push_stacking_context(stacking_context);
45 let stacking_context_state = self.handler.visit_stacking_context(stacking_context);
46
47 if let StackingContextFragments::Root = &stacking_context.fragment {
57 self.handler.visit_box_for_root_background(&state);
58 }
59
60 let root_fragment = stacking_context.fragment();
63 let root_fragment = root_fragment
64 .as_ref()
65 .map(|root_fragment| root_fragment.with_style());
66 if let Some(root_fragment) = &root_fragment &&
67 !root_fragment.is_inline_box()
68 {
69 self.handle_box(&state, root_fragment);
70 }
71
72 let mut children = stacking_context.children.iter().peekable();
76 while children.peek().is_some_and(|child| child.z_index < 0) {
77 self.traverse_stacking_context(
78 &state,
79 children.next().expect("Should have a value due to peek."),
80 );
81 }
82
83 if let Some(root_fragment) = &root_fragment {
84 self.traverse_stacking_context_inner(&state, root_fragment);
85 }
86
87 for child in children {
98 assert!(child.z_index >= 0);
99 self.traverse_stacking_context(&state, child);
100 }
101
102 if old_outlines_length < self.outlines.len() {
106 for (state, outline_fragment) in &self.outlines.split_off(old_outlines_length) {
107 self.handler.visit_box_for_outline(state, outline_fragment);
108 }
109 }
110
111 self.handler
112 .leave_stacking_context(&state, stacking_context_state);
113 }
114
115 fn traverse_stacking_context_inner(
116 &mut self,
117 state: &TraversalState,
118 root: &BoxFragmentWithStyle<'_>,
119 ) {
120 let old_float_length = self.floats.len();
121 let mut saw_inline_level_or_replaced = root.is_replaced();
122
123 let inner_state = state.push_box_fragment(root);
126 for child in root.children.iter() {
127 saw_inline_level_or_replaced |=
128 self.traverse_block_level_descendants_decorations(&inner_state, child);
129 }
130
131 if root.is_table_grid_with_collapsed_borders() {
134 self.handler
135 .visit_box_for_collapsed_table_borders(state, root);
136 }
137
138 if old_float_length < self.floats.len() {
141 for (state, float_fragment) in &self.floats.split_off(old_float_length) {
142 let float_fragment = &float_fragment.with_style();
143 self.handle_box(state, float_fragment);
144 self.traverse_stacking_context_inner(state, float_fragment);
145 }
146 }
147
148 if root.is_inline_box() {
150 let line_box_rect = PhysicalRect::new(inner_state.origin, root.content_rect().size);
160 self.traverse_box_in_a_line_box(
161 state,
162 root,
163 line_box_rect,
164 true, );
166 } else if saw_inline_level_or_replaced {
167 self.traverse_line_boxes_and_replaced_for_box(
176 state, root, true, );
178 }
179 }
180
181 fn traverse_stacking_container(
187 &mut self,
188 state: &TraversalState,
189 root: &BoxFragmentWithStyle<'_>,
190 is_block_level: bool,
191 ) {
192 let old_outlines_length = self.outlines.len();
193
194 if is_block_level {
197 self.handle_box(state, root);
198 }
199
200 self.traverse_stacking_context_inner(state, root);
202
203 if old_outlines_length < self.outlines.len() {
207 for (state, outline_fragment) in &self.outlines.split_off(old_outlines_length) {
208 self.handler.visit_box_for_outline(state, outline_fragment);
209 }
210 }
211 }
212
213 fn traverse_block_level_descendants_decorations(
214 &mut self,
215 state: &TraversalState,
216 fragment: &Fragment,
217 ) -> bool {
218 let mut saw_inline_level_or_replaced = false;
219
220 match fragment {
221 Fragment::LayoutRoot(layout_root_fragment) => {
222 saw_inline_level_or_replaced = self.traverse_block_level_descendants_decorations(
223 state,
224 &layout_root_fragment.inner(),
225 );
226 },
227 Fragment::Box(box_fragment) => {
228 let box_fragment = &box_fragment.with_style();
229 if box_fragment.stacking_context_type().is_some() {
233 return false;
234 }
235
236 if box_fragment.is_atomic_inline_level() || box_fragment.is_flex_or_grid_item() {
238 return true;
239 }
240
241 if box_fragment.is_inline_box() {
243 saw_inline_level_or_replaced = true;
244 } else {
245 self.handle_box(state, box_fragment);
246 }
247
248 if box_fragment.is_replaced() {
249 return true;
250 }
251
252 let state_for_children = state.push_box_fragment(box_fragment);
253 for child in box_fragment.children.iter() {
254 saw_inline_level_or_replaced |= self
255 .traverse_block_level_descendants_decorations(&state_for_children, child);
256 }
257
258 if box_fragment.is_table_grid_with_collapsed_borders() {
261 self.handler
262 .visit_box_for_collapsed_table_borders(state, box_fragment);
263 }
264 },
265 Fragment::Float(float_box_fragment) => {
266 if float_box_fragment.stacking_context_type().is_none() {
267 self.floats
268 .push((state.without_text_decorations(), float_box_fragment.clone()));
269 }
270 },
271 Fragment::Positioning(positioning_fragment) => {
272 self.handler.visit_positioning(state, positioning_fragment);
273
274 if positioning_fragment.is_line_box() {
275 saw_inline_level_or_replaced = true;
276 }
277
278 if !positioning_fragment.children.is_empty() {
279 let state = state.push_positioning_fragment(positioning_fragment);
280 for child in positioning_fragment.children.iter() {
281 saw_inline_level_or_replaced |=
282 self.traverse_block_level_descendants_decorations(&state, child);
283 }
284 }
285 },
286 Fragment::AbsoluteOrFixedPositionedPlaceholder(..) |
287 Fragment::Text(..) |
288 Fragment::Image(..) |
289 Fragment::IFrame(..) => {},
290 }
291
292 saw_inline_level_or_replaced
293 }
294
295 fn traverse_line_boxes_and_replaced_for_box(
296 &mut self,
297 state: &TraversalState,
298 fragment: &BoxFragmentWithStyle<'_>,
299 at_root_of_stacking_context: bool,
300 ) {
301 let is_flex_or_grid = fragment.is_flex_or_grid_item();
302 if fragment.is_replaced() {
303 if is_flex_or_grid {
304 self.handle_box(state, fragment);
305 }
306
307 let inner_state = state.push_box_fragment(fragment);
308 self.traverse_replaced_content(&inner_state, fragment);
309 return;
310 }
311
312 if !at_root_of_stacking_context && is_flex_or_grid {
313 self.traverse_stacking_container(state, fragment, true );
314 return;
315 }
316
317 let inner_state = state.push_box_fragment(fragment);
318 for child in fragment.children.iter() {
319 self.traverse_line_boxes_and_replaced(
320 &inner_state,
321 child,
322 false, );
324 }
325 }
326
327 fn traverse_line_boxes_and_replaced(
328 &mut self,
329 state: &TraversalState,
330 fragment: &Fragment,
331 at_root_of_stacking_context: bool,
332 ) {
333 match fragment {
334 Fragment::LayoutRoot(layout_root_fragment) => self.traverse_line_boxes_and_replaced(
335 state,
336 &layout_root_fragment.inner(),
337 at_root_of_stacking_context,
338 ),
339 Fragment::Box(box_fragment) => {
340 if box_fragment.stacking_context_type().is_some() {
344 return;
345 }
346 self.traverse_line_boxes_and_replaced_for_box(
347 state,
348 &box_fragment.with_style(),
349 at_root_of_stacking_context,
350 );
351 },
352 Fragment::Positioning(positioning_fragment) if positioning_fragment.is_line_box() => {
353 let state = state.push_positioning_fragment(positioning_fragment);
354 let line_box_rect =
355 PhysicalRect::new(state.origin, positioning_fragment.base.rect().size);
356 for child in &positioning_fragment.children {
357 self.traverse_fragment_in_a_line_box(&state, child, line_box_rect);
358 }
359 },
360 Fragment::Positioning(positioning_fragment) => {
361 if !positioning_fragment.children.is_empty() {
362 let state = state.push_positioning_fragment(positioning_fragment);
363 for child in &positioning_fragment.children {
364 self.traverse_line_boxes_and_replaced(
365 &state, child, false, );
367 }
368 }
369 },
370 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) |
371 Fragment::Float(..) |
372 Fragment::IFrame(_) |
373 Fragment::Image(_) |
374 Fragment::Text(..) => {},
375 }
376 }
377
378 fn traverse_fragment_in_a_line_box(
379 &mut self,
380 state: &TraversalState,
381 fragment: &Fragment,
382 line_box_rect: PhysicalRect<Au>,
383 ) {
384 match fragment {
385 Fragment::LayoutRoot(layout_root_fragment) => self.traverse_fragment_in_a_line_box(
386 state,
387 &layout_root_fragment.inner(),
388 line_box_rect,
389 ),
390 Fragment::Box(box_fragment) => self.traverse_box_in_a_line_box(
391 state,
392 &box_fragment.with_style(),
393 line_box_rect,
394 false, ),
396 Fragment::Text(text_fragment) => {
397 self.handler.visit_text(state, line_box_rect, text_fragment);
398 },
399 Fragment::AbsoluteOrFixedPositionedPlaceholder(..) | Fragment::Float(..) => {},
400 Fragment::Positioning(..) => {
401 unreachable!("Unexpected direct descendant PositioningContext of inline.")
402 },
403 Fragment::Image(..) | Fragment::IFrame(..) => {
404 unreachable!("Unexpected replaced content direct descendant of inline.")
405 },
406 }
407 }
408
409 fn traverse_box_in_a_line_box(
411 &mut self,
412 state: &TraversalState,
413 box_fragment: &BoxFragmentWithStyle<'_>,
414 line_box_rect: PhysicalRect<Au>,
415 at_stacking_context_root: bool,
416 ) {
417 if !at_stacking_context_root && box_fragment.stacking_context_type().is_some() {
421 return;
422 }
423
424 let is_atomic_inline_level = box_fragment.is_atomic_inline_level();
426 if !is_atomic_inline_level && !box_fragment.is_inline_box() {
427 self.traverse_line_boxes_and_replaced_for_box(
428 state,
429 box_fragment,
430 at_stacking_context_root,
431 );
432 return;
433 }
434
435 self.handle_box(state, box_fragment);
438
439 if box_fragment.is_replaced() {
447 let state = state.push_box_fragment(box_fragment);
448 self.traverse_replaced_content(&state, box_fragment);
449
450 } else if is_atomic_inline_level || box_fragment.is_flex_or_grid_item() {
453 self.traverse_stacking_container(state, box_fragment, false );
454
455 } else {
458 let state = state.push_box_fragment(box_fragment);
459 for child in &box_fragment.children {
460 self.traverse_fragment_in_a_line_box(&state, child, line_box_rect);
461 }
462 }
463 }
464
465 fn traverse_replaced_content(
466 &mut self,
467 state: &TraversalState,
468 box_fragment: &Arc<BoxFragment>,
469 ) {
470 for child in &box_fragment.children {
471 match child {
472 Fragment::LayoutRoot(layout_root_fragment) => {
473 self.traverse_stacking_container(
474 &state.without_text_decorations(),
475 &layout_root_fragment.inner_box_fragment().with_style(),
476 true, );
478 },
479 Fragment::Image(image_fragment) => {
480 let containing_block =
481 PhysicalRect::new(state.origin, box_fragment.content_rect().size);
482 self.handler
483 .visit_image(state, containing_block, image_fragment);
484 },
485 Fragment::IFrame(iframe_fragment) => {
486 self.handler.visit_iframe(state, iframe_fragment);
487 },
488 Fragment::Box(box_fragment) => {
489 self.traverse_stacking_container(
490 &state.without_text_decorations(),
491 &box_fragment.with_style(),
492 true, );
494 },
495 _ => {},
496 }
497 }
498 }
499
500 fn handle_box(&mut self, state: &TraversalState, fragment: &BoxFragmentWithStyle<'_>) {
501 if fragment.has_outline() {
502 self.outlines
503 .push((state.clone(), fragment.box_fragment.clone()));
504 }
505 self.handler.visit_box(state, fragment);
506 }
507}
508
509pub(crate) trait PaintTraversalHandler {
510 type StackingContextState;
511
512 fn visit_stacking_context(
513 &mut self,
514 stacking_context: &StackingContext,
515 ) -> Self::StackingContextState;
516 fn leave_stacking_context(
517 &mut self,
518 state: &TraversalState,
519 stacking_context_state: Self::StackingContextState,
520 );
521
522 fn visit_box(&mut self, state: &TraversalState, fragment: &BoxFragmentWithStyle<'_>);
523 fn visit_iframe(&mut self, _state: &TraversalState, _fragment: &Arc<IFrameFragment>) {}
524 fn visit_image(
525 &mut self,
526 _state: &TraversalState,
527 _containing_block: PhysicalRect<Au>,
528 _fragment: &Arc<ImageFragment>,
529 ) {
530 }
531 fn visit_text(
532 &mut self,
533 state: &TraversalState,
534 line_box_rect: PhysicalRect<Au>,
535 fragment: &Arc<TextFragment>,
536 );
537 fn visit_positioning(&mut self, _state: &TraversalState, _fragment: &Arc<PositioningFragment>) {
538 }
539
540 fn visit_box_for_root_background(&mut self, _state: &TraversalState) {}
541 fn visit_box_for_outline(&mut self, _state: &TraversalState, _fragment: &Arc<BoxFragment>) {}
542 fn visit_box_for_collapsed_table_borders(
543 &mut self,
544 _state: &TraversalState,
545 _fragment: &BoxFragmentWithStyle<'_>,
546 ) {
547 }
548}
549
550#[derive(Clone, Debug, Default)]
551pub(crate) struct TraversalState {
552 pub spatial_id: ScrollTreeNodeId,
553 pub clip_id: ClipId,
554 pub origin: PhysicalPoint<Au>,
555 pub text_decorations: Rc<Vec<FragmentTextDecoration>>,
556 pub containing_element_tag: Option<Tag>,
560}
561
562impl TraversalState {
563 pub(crate) fn push_box_fragment(&self, box_fragment: &BoxFragmentWithStyle<'_>) -> Self {
564 let style = box_fragment.style();
565
566 let mut propagated_text_decorations = self.text_decorations.clone();
575 if box_fragment.is_atomic_inline_level() ||
576 box_fragment.base.flags.contains(
577 FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER | FragmentFlags::IS_REPLACED,
578 )
579 {
580 propagated_text_decorations = Default::default();
581 }
582
583 let text_decorations = match style.get_text_decoration_line() {
584 &TextDecorationLine::NONE => propagated_text_decorations,
585 line => {
586 let mut new_vector = (*propagated_text_decorations).clone();
587 let color = &style.get_inherited_text().color;
588 new_vector.push(FragmentTextDecoration {
589 line: *line,
590 color: style.get_text_decoration_color().resolve_to_absolute(color),
591 style: style.slow_clone_text_decoration_style(),
592 thickness: style.slow_clone_text_decoration_thickness(),
593 });
594 Rc::new(new_vector)
595 },
596 };
597
598 Self {
599 origin: self.origin + box_fragment.content_rect().origin.to_vector(),
600 spatial_id: box_fragment
601 .generated_scroll_tree_node_id()
602 .unwrap_or(self.spatial_id),
603 clip_id: box_fragment.generated_clip_id().unwrap_or(self.clip_id),
604 text_decorations,
605 containing_element_tag: box_fragment.base.tag.or(self.containing_element_tag),
606 }
607 }
608
609 pub(crate) fn without_text_decorations(&self) -> Self {
610 Self {
611 text_decorations: Default::default(),
612 ..*self
613 }
614 }
615
616 pub(crate) fn push_positioning_fragment(
617 &self,
618 positioning_fragment: &PositioningFragment,
619 ) -> Self {
620 Self {
621 origin: self.origin + positioning_fragment.base.rect().origin.to_vector(),
622 spatial_id: self.spatial_id,
623 clip_id: self.clip_id,
624 text_decorations: self.text_decorations.clone(),
625 containing_element_tag: self.containing_element_tag,
626 }
627 }
628
629 pub(crate) fn push_stacking_context(&self, stacking_context: &StackingContext) -> Self {
630 Self {
631 origin: stacking_context.containing_block_origin,
632 spatial_id: stacking_context.scroll_tree_node_id,
633 clip_id: stacking_context.clip_id,
634 text_decorations: stacking_context.text_decorations.clone(),
635 containing_element_tag: self.containing_element_tag,
636 }
637 }
638}