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, 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 self.traverse_box_in_a_line_box(state, root, true );
154 } else if saw_inline_level_or_replaced {
155 self.traverse_line_boxes_and_replaced_for_box(
164 state, root, true, );
166 }
167 }
168
169 fn traverse_stacking_container(
175 &mut self,
176 state: &TraversalState,
177 root: &BoxFragmentWithStyle<'_>,
178 is_block_level: bool,
179 ) {
180 let old_outlines_length = self.outlines.len();
181
182 if is_block_level {
185 self.handle_box(state, root);
186 }
187
188 self.traverse_stacking_context_inner(state, root);
190
191 if old_outlines_length < self.outlines.len() {
195 for (state, outline_fragment) in &self.outlines.split_off(old_outlines_length) {
196 self.handler.visit_box_for_outline(state, outline_fragment);
197 }
198 }
199 }
200
201 fn traverse_block_level_descendants_decorations(
202 &mut self,
203 state: &TraversalState,
204 fragment: &Fragment,
205 ) -> bool {
206 let mut saw_inline_level_or_replaced = false;
207
208 match fragment {
209 Fragment::LayoutRoot(layout_root_fragment) => {
210 saw_inline_level_or_replaced = self.traverse_block_level_descendants_decorations(
211 state,
212 &layout_root_fragment.inner(),
213 );
214 },
215 Fragment::Box(box_fragment) => {
216 let box_fragment = &box_fragment.with_style();
217 if box_fragment.stacking_context_type().is_some() {
221 return false;
222 }
223
224 if box_fragment.is_atomic_inline_level() || box_fragment.is_flex_or_grid_item() {
226 return true;
227 }
228
229 if box_fragment.is_inline_box() {
231 saw_inline_level_or_replaced = true;
232 } else {
233 self.handle_box(state, box_fragment);
234 }
235
236 if box_fragment.is_replaced() {
237 return true;
238 }
239
240 let state_for_children = state.push_box_fragment(box_fragment);
241 for child in box_fragment.children.iter() {
242 saw_inline_level_or_replaced |= self
243 .traverse_block_level_descendants_decorations(&state_for_children, child);
244 }
245
246 if box_fragment.is_table_grid_with_collapsed_borders() {
249 self.handler
250 .visit_box_for_collapsed_table_borders(state, box_fragment);
251 }
252 },
253 Fragment::Float(float_box_fragment) => {
254 if float_box_fragment.stacking_context_type().is_none() {
255 self.floats
256 .push((state.without_text_decorations(), float_box_fragment.clone()));
257 }
258 },
259 Fragment::Positioning(positioning_fragment) => {
260 self.handler.visit_positioning(state, positioning_fragment);
261
262 if positioning_fragment.is_line_box() {
263 saw_inline_level_or_replaced = true;
264 }
265
266 if !positioning_fragment.children.is_empty() {
267 let state = state.push_positioning_fragment(positioning_fragment);
268 for child in positioning_fragment.children.iter() {
269 saw_inline_level_or_replaced |=
270 self.traverse_block_level_descendants_decorations(&state, child);
271 }
272 }
273 },
274 Fragment::AbsoluteOrFixedPositionedPlaceholder(..) |
275 Fragment::Text(..) |
276 Fragment::Image(..) |
277 Fragment::IFrame(..) => {},
278 }
279
280 saw_inline_level_or_replaced
281 }
282
283 fn traverse_line_boxes_and_replaced_for_box(
284 &mut self,
285 state: &TraversalState,
286 fragment: &BoxFragmentWithStyle<'_>,
287 at_root_of_stacking_context: bool,
288 ) {
289 let is_flex_or_grid = fragment.is_flex_or_grid_item();
290 if fragment.is_replaced() {
291 if is_flex_or_grid {
292 self.handle_box(state, fragment);
293 }
294
295 let inner_state = state.push_box_fragment(fragment);
296 self.traverse_replaced_content(&inner_state, fragment);
297 return;
298 }
299
300 if !at_root_of_stacking_context && is_flex_or_grid {
301 self.traverse_stacking_container(state, fragment, true );
302 return;
303 }
304
305 let inner_state = state.push_box_fragment(fragment);
306 for child in fragment.children.iter() {
307 self.traverse_line_boxes_and_replaced(
308 &inner_state,
309 child,
310 false, );
312 }
313 }
314
315 fn traverse_line_boxes_and_replaced(
316 &mut self,
317 state: &TraversalState,
318 fragment: &Fragment,
319 at_root_of_stacking_context: bool,
320 ) {
321 match fragment {
322 Fragment::LayoutRoot(layout_root_fragment) => self.traverse_line_boxes_and_replaced(
323 state,
324 &layout_root_fragment.inner(),
325 at_root_of_stacking_context,
326 ),
327 Fragment::Box(box_fragment) => {
328 if box_fragment.stacking_context_type().is_some() {
332 return;
333 }
334 self.traverse_line_boxes_and_replaced_for_box(
335 state,
336 &box_fragment.with_style(),
337 at_root_of_stacking_context,
338 );
339 },
340 Fragment::Positioning(positioning_fragment) if positioning_fragment.is_line_box() => {
341 let state = state.push_positioning_fragment(positioning_fragment);
342 for child in &positioning_fragment.children {
343 self.traverse_fragment_in_a_line_box(&state, child);
344 }
345 },
346 Fragment::Positioning(positioning_fragment) => {
347 if !positioning_fragment.children.is_empty() {
348 let state = state.push_positioning_fragment(positioning_fragment);
349 for child in &positioning_fragment.children {
350 self.traverse_line_boxes_and_replaced(
351 &state, child, false, );
353 }
354 }
355 },
356 Fragment::AbsoluteOrFixedPositionedPlaceholder(_) |
357 Fragment::Float(..) |
358 Fragment::IFrame(_) |
359 Fragment::Image(_) |
360 Fragment::Text(..) => {},
361 }
362 }
363
364 fn traverse_fragment_in_a_line_box(&mut self, state: &TraversalState, fragment: &Fragment) {
365 match fragment {
366 Fragment::LayoutRoot(layout_root_fragment) => {
367 self.traverse_fragment_in_a_line_box(state, &layout_root_fragment.inner())
368 },
369 Fragment::Box(box_fragment) => self.traverse_box_in_a_line_box(
370 state,
371 &box_fragment.with_style(),
372 false, ),
374 Fragment::Text(text_fragment) => {
375 let containing_block =
378 PhysicalRect::new(state.origin, text_fragment.base.rect().size);
379 self.handler
380 .visit_text(state, containing_block, text_fragment);
381 },
382 Fragment::AbsoluteOrFixedPositionedPlaceholder(..) | Fragment::Float(..) => {},
383 Fragment::Positioning(..) => {
384 unreachable!("Unexpected direct descendant PositioningContext of inline.")
385 },
386 Fragment::Image(..) | Fragment::IFrame(..) => {
387 unreachable!("Unexpected replaced content direct descendant of inline.")
388 },
389 }
390 }
391
392 fn traverse_box_in_a_line_box(
394 &mut self,
395 state: &TraversalState,
396 box_fragment: &BoxFragmentWithStyle<'_>,
397 at_stacking_context_root: bool,
398 ) {
399 if !at_stacking_context_root && box_fragment.stacking_context_type().is_some() {
403 return;
404 }
405
406 let is_atomic_inline_level = box_fragment.is_atomic_inline_level();
408 if !is_atomic_inline_level && !box_fragment.is_inline_box() {
409 self.traverse_line_boxes_and_replaced_for_box(
410 state,
411 box_fragment,
412 at_stacking_context_root,
413 );
414 return;
415 }
416
417 self.handle_box(state, box_fragment);
420
421 if box_fragment.is_replaced() {
429 let state = state.push_box_fragment(box_fragment);
430 self.traverse_replaced_content(&state, box_fragment);
431
432 } else if is_atomic_inline_level || box_fragment.is_flex_or_grid_item() {
435 self.traverse_stacking_container(state, box_fragment, false );
436
437 } else {
440 let state = state.push_box_fragment(box_fragment);
441 for child in &box_fragment.children {
442 self.traverse_fragment_in_a_line_box(&state, child);
443 }
444 }
445 }
446
447 fn traverse_replaced_content(
448 &mut self,
449 state: &TraversalState,
450 box_fragment: &Arc<BoxFragment>,
451 ) {
452 for child in &box_fragment.children {
453 match child {
454 Fragment::LayoutRoot(layout_root_fragment) => {
455 self.traverse_stacking_container(
456 &state.without_text_decorations(),
457 &layout_root_fragment.inner_box_fragment().with_style(),
458 true, );
460 },
461 Fragment::Image(image_fragment) => {
462 let containing_block =
463 PhysicalRect::new(state.origin, box_fragment.content_rect().size);
464 self.handler
465 .visit_image(state, containing_block, image_fragment);
466 },
467 Fragment::IFrame(iframe_fragment) => {
468 self.handler.visit_iframe(state, iframe_fragment);
469 },
470 Fragment::Box(box_fragment) => {
471 self.traverse_stacking_container(
472 &state.without_text_decorations(),
473 &box_fragment.with_style(),
474 true, );
476 },
477 _ => {},
478 }
479 }
480 }
481
482 fn handle_box(&mut self, state: &TraversalState, fragment: &BoxFragmentWithStyle<'_>) {
483 if fragment.has_outline() {
484 self.outlines
485 .push((state.clone(), fragment.box_fragment.clone()));
486 }
487 self.handler.visit_box(state, fragment);
488 }
489}
490
491pub(crate) trait PaintTraversalHandler {
492 type StackingContextState;
493
494 fn visit_stacking_context(
495 &mut self,
496 stacking_context: &StackingContext,
497 ) -> Self::StackingContextState;
498 fn leave_stacking_context(
499 &mut self,
500 state: &TraversalState,
501 stacking_context_state: Self::StackingContextState,
502 );
503
504 fn visit_box(&mut self, state: &TraversalState, fragment: &BoxFragmentWithStyle<'_>);
505 fn visit_iframe(&mut self, _state: &TraversalState, _fragment: &Arc<IFrameFragment>) {}
506 fn visit_image(
507 &mut self,
508 _state: &TraversalState,
509 _containing_block: PhysicalRect<Au>,
510 _fragment: &Arc<ImageFragment>,
511 ) {
512 }
513 fn visit_text(
514 &mut self,
515 state: &TraversalState,
516 containing_block: PhysicalRect<Au>,
517 fragment: &Arc<TextFragment>,
518 );
519 fn visit_positioning(&mut self, _state: &TraversalState, _fragment: &Arc<PositioningFragment>) {
520 }
521
522 fn visit_box_for_root_background(&mut self, _state: &TraversalState) {}
523 fn visit_box_for_outline(&mut self, _state: &TraversalState, _fragment: &Arc<BoxFragment>) {}
524 fn visit_box_for_collapsed_table_borders(
525 &mut self,
526 _state: &TraversalState,
527 _fragment: &BoxFragmentWithStyle<'_>,
528 ) {
529 }
530}
531
532#[derive(Clone, Debug, Default)]
533pub(crate) struct TraversalState {
534 pub spatial_id: ScrollTreeNodeId,
535 pub clip_id: ClipId,
536 pub origin: PhysicalPoint<Au>,
537 pub text_decorations: Rc<Vec<FragmentTextDecoration>>,
538}
539
540impl TraversalState {
541 pub(crate) fn push_box_fragment(&self, box_fragment: &BoxFragmentWithStyle<'_>) -> Self {
542 let style = box_fragment.style();
543
544 let mut propagated_text_decorations = self.text_decorations.clone();
553 if box_fragment.is_atomic_inline_level() ||
554 box_fragment.base.flags.contains(
555 FragmentFlags::IS_OUTSIDE_LIST_ITEM_MARKER | FragmentFlags::IS_REPLACED,
556 )
557 {
558 propagated_text_decorations = Default::default();
559 }
560
561 let text_decorations = match &style.get_text().text_decoration_line {
562 &TextDecorationLine::NONE => propagated_text_decorations,
563 line => {
564 let mut new_vector = (*propagated_text_decorations).clone();
565 let color = &style.get_inherited_text().color;
566 new_vector.push(FragmentTextDecoration {
567 line: *line,
568 color: style
569 .clone_text_decoration_color()
570 .resolve_to_absolute(color),
571 style: style.clone_text_decoration_style(),
572 });
573 Rc::new(new_vector)
574 },
575 };
576
577 Self {
578 origin: self.origin + box_fragment.content_rect().origin.to_vector(),
579 spatial_id: box_fragment
580 .generated_scroll_tree_node_id()
581 .unwrap_or(self.spatial_id),
582 clip_id: box_fragment.generated_clip_id().unwrap_or(self.clip_id),
583 text_decorations,
584 }
585 }
586
587 pub(crate) fn without_text_decorations(&self) -> Self {
588 Self {
589 text_decorations: Default::default(),
590 ..*self
591 }
592 }
593
594 pub(crate) fn push_positioning_fragment(
595 &self,
596 positioning_fragment: &PositioningFragment,
597 ) -> Self {
598 Self {
599 origin: self.origin + positioning_fragment.base.rect().origin.to_vector(),
600 spatial_id: self.spatial_id,
601 clip_id: self.clip_id,
602 text_decorations: self.text_decorations.clone(),
603 }
604 }
605
606 pub(crate) fn push_stacking_context(&self, stacking_context: &StackingContext) -> Self {
607 Self {
608 origin: stacking_context.containing_block_origin,
609 spatial_id: stacking_context.scroll_tree_node_id,
610 clip_id: stacking_context.clip_id,
611 text_decorations: stacking_context.text_decorations.clone(),
612 }
613 }
614}