1use std::borrow::Cow;
6use std::iter::repeat_n;
7
8use atomic_refcell::AtomicRef;
9use layout_api::LayoutNode;
10use log::warn;
11use servo_arc::Arc;
12use style::properties::ComputedValues;
13use style::properties::style_structs::Font;
14use style::selector_parser::PseudoElement;
15use style::str::char_is_whitespace;
16
17use super::{
18 Table, TableCaption, TableLevelBox, TableSlot, TableSlotCell, TableSlotCoordinates,
19 TableSlotOffset, TableTrack, TableTrackGroup, TableTrackGroupType,
20};
21use crate::cell::ArcRefCell;
22use crate::context::LayoutContext;
23use crate::dom::{BoxSlot, LayoutBox, NodeExt};
24use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler};
25use crate::flow::inline::SharedInlineStyles;
26use crate::flow::{BlockContainerBuilder, BlockFormattingContext};
27use crate::formatting_contexts::{
28 IndependentFormattingContext, IndependentFormattingContextContents,
29};
30use crate::fragment_tree::BaseFragmentInfo;
31use crate::layout_box_base::LayoutBoxBase;
32use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal};
33use crate::{PropagatedBoxTreeData, SharedStyle};
34
35#[derive(Debug)]
37pub(super) struct ResolvedSlotAndLocation<'a> {
38 pub cell: AtomicRef<'a, TableSlotCell>,
39 pub coords: TableSlotCoordinates,
40}
41
42impl ResolvedSlotAndLocation<'_> {
43 fn covers_cell_at(&self, coords: TableSlotCoordinates) -> bool {
44 let covered_in_x =
45 coords.x >= self.coords.x && coords.x < self.coords.x + self.cell.colspan;
46 let covered_in_y = coords.y >= self.coords.y &&
47 (self.cell.rowspan == 0 || coords.y < self.coords.y + self.cell.rowspan);
48 covered_in_x && covered_in_y
49 }
50}
51
52pub(crate) enum AnonymousTableContent<'dom> {
53 Text(NodeAndStyleInfo<'dom>, Cow<'dom, str>),
54 EnterDisplayContents(SharedInlineStyles),
55 LeaveDisplayContents,
56 Element {
57 info: NodeAndStyleInfo<'dom>,
58 display: DisplayGeneratingBox,
59 contents: Contents,
60 box_slot: BoxSlot<'dom>,
61 },
62}
63
64impl AnonymousTableContent<'_> {
65 fn is_whitespace_only(&self) -> bool {
66 match self {
67 Self::Element { .. } => false,
68 Self::Text(_, text) => text.chars().all(char_is_whitespace),
69 Self::EnterDisplayContents(_) | Self::LeaveDisplayContents => true,
70 }
71 }
72
73 fn contents_are_whitespace_only(contents: &[Self]) -> bool {
74 contents.iter().all(|content| content.is_whitespace_only())
75 }
76}
77
78impl Table {
79 pub(crate) fn construct(
80 context: &LayoutContext,
81 info: &NodeAndStyleInfo,
82 grid_style: Arc<ComputedValues>,
83 contents: NonReplacedContents,
84 propagated_data: PropagatedBoxTreeData,
85 ) -> Self {
86 let mut traversal = TableBuilderTraversal::new(context, info, grid_style, propagated_data);
87 contents.traverse(context, info, &mut traversal);
88 traversal.finish()
89 }
90
91 pub(crate) fn construct_anonymous<'dom>(
92 context: &LayoutContext,
93 parent: &mut impl TraversalHandler<'dom>,
94 parent_info: &NodeAndStyleInfo<'dom>,
95 contents: Vec<AnonymousTableContent<'dom>>,
96 propagated_data: PropagatedBoxTreeData,
97 ) -> (NodeAndStyleInfo<'dom>, IndependentFormattingContext) {
98 let table_info = parent_info
99 .with_pseudo_element(context, PseudoElement::ServoAnonymousTable)
100 .expect("Should never fail to create anonymous table info.");
101 let table_style = table_info.style.clone();
102 let mut table_builder =
103 TableBuilderTraversal::new(context, &table_info, table_style.clone(), propagated_data);
104
105 for content in contents {
106 match content {
107 AnonymousTableContent::Element {
108 info,
109 display,
110 contents,
111 box_slot,
112 } => {
113 table_builder.handle_element(&info, display, contents, box_slot);
114 },
115 AnonymousTableContent::Text(..) => {
116 },
120 AnonymousTableContent::EnterDisplayContents(styles) => {
124 parent.enter_display_contents(styles)
125 },
126 AnonymousTableContent::LeaveDisplayContents => parent.leave_display_contents(),
127 }
128 }
129
130 let mut table = table_builder.finish();
131 table.anonymous = true;
132
133 let ifc = IndependentFormattingContext::new(
134 LayoutBoxBase::new((&table_info).into(), table_style),
135 IndependentFormattingContextContents::Table(table),
136 propagated_data,
137 );
138
139 (table_info, ifc)
140 }
141
142 fn push_new_slot_to_last_row(&mut self, slot: TableSlot) {
144 let last_row = match self.slots.last_mut() {
145 Some(row) => row,
146 None => {
147 unreachable!("Should have some rows before calling `push_new_slot_to_last_row`")
148 },
149 };
150
151 self.size.width = self.size.width.max(last_row.len() + 1);
152 last_row.push(slot);
153 }
154
155 pub(super) fn resolve_slot_at(
161 &self,
162 coords: TableSlotCoordinates,
163 ) -> Vec<ResolvedSlotAndLocation<'_>> {
164 let slot = self.get_slot(coords);
165 match slot {
166 Some(TableSlot::Cell(cell)) => vec![ResolvedSlotAndLocation {
167 cell: cell.borrow(),
168 coords,
169 }],
170 Some(TableSlot::Spanned(offsets)) => offsets
171 .iter()
172 .flat_map(|offset| self.resolve_slot_at(coords - *offset))
173 .collect(),
174 Some(TableSlot::Empty) | None => {
175 warn!("Tried to resolve an empty or nonexistant slot!");
176 vec![]
177 },
178 }
179 }
180}
181
182impl TableSlot {
183 pub fn push_spanned(&mut self, new_offset: TableSlotOffset) {
185 match *self {
186 TableSlot::Cell { .. } => {
187 panic!(
188 "Should never have a table model error with an originating cell slot overlapping a spanned slot"
189 )
190 },
191 TableSlot::Spanned(ref mut vec) => vec.insert(0, new_offset),
192 TableSlot::Empty => {
193 panic!("Should never have a table model error with an empty slot");
194 },
195 }
196 }
197}
198
199pub struct TableBuilder {
200 table: Table,
202
203 pub incoming_rowspans: Vec<isize>,
213}
214
215impl TableBuilder {
216 pub(super) fn new(
217 style: Arc<ComputedValues>,
218 grid_style: Arc<ComputedValues>,
219 base_fragment_info: BaseFragmentInfo,
220 percentage_columns_allowed_for_inline_content_sizes: bool,
221 ) -> Self {
222 Self {
223 table: Table::new(
224 style,
225 grid_style,
226 base_fragment_info,
227 percentage_columns_allowed_for_inline_content_sizes,
228 ),
229 incoming_rowspans: Vec::new(),
230 }
231 }
232
233 pub fn new_for_tests() -> Self {
234 let testing_style =
235 ComputedValues::initial_values_with_font_override(Font::initial_values());
236 Self::new(
237 testing_style.clone(),
238 testing_style,
239 BaseFragmentInfo::anonymous(),
240 true, )
242 }
243
244 pub fn last_row_index_in_row_group_at_row_n(&self, n: usize) -> usize {
245 for row_group in self.table.row_groups.iter() {
249 let row_group = row_group.borrow();
250 if row_group.track_range.start > n {
251 return row_group.track_range.start - 1;
252 }
253 }
254 self.table.size.height - 1
255 }
256
257 pub fn finish(mut self) -> Table {
258 self.adjust_table_geometry_for_columns_and_colgroups();
259 self.do_missing_cells_fixup();
260 self.reorder_first_thead_and_tfoot();
261 self.do_final_rowspan_calculation();
262 self.table
263 }
264
265 fn do_missing_cells_fixup(&mut self) {
268 for row in self.table.slots.iter_mut() {
269 row.resize_with(self.table.size.width, || TableSlot::Empty);
270 }
271 }
272
273 fn adjust_table_geometry_for_columns_and_colgroups(&mut self) {
279 if self.table.rows.is_empty() && self.table.row_groups.is_empty() {
280 self.table.columns.truncate(0);
281 self.table.column_groups.truncate(0);
282 } else {
283 self.table.size.width = self.table.size.width.max(self.table.columns.len());
284 }
285 }
286
287 fn reorder_first_thead_and_tfoot(&mut self) {
292 let mut thead_index = None;
293 let mut tfoot_index = None;
294 for (row_group_index, row_group) in self.table.row_groups.iter().enumerate() {
295 let row_group = row_group.borrow();
296 if thead_index.is_none() && row_group.group_type == TableTrackGroupType::HeaderGroup {
297 thead_index = Some(row_group_index);
298 }
299 if tfoot_index.is_none() && row_group.group_type == TableTrackGroupType::FooterGroup {
300 tfoot_index = Some(row_group_index);
301 }
302 if thead_index.is_some() && tfoot_index.is_some() {
303 break;
304 }
305 }
306
307 if let Some(thead_index) = thead_index {
308 self.move_row_group_to_front(thead_index)
309 }
310
311 if let Some(mut tfoot_index) = tfoot_index {
312 if thead_index.unwrap_or(0) > tfoot_index {
315 tfoot_index += 1;
316 }
317 self.move_row_group_to_end(tfoot_index)
318 }
319 }
320
321 fn regenerate_track_ranges(&mut self) {
322 let mut current_row_group_index = None;
324 for (row_index, row) in self.table.rows.iter().enumerate() {
325 let row = row.borrow();
326 if current_row_group_index == row.group_index {
327 continue;
328 }
329
330 if let Some(current_group_index) = current_row_group_index {
332 self.table.row_groups[current_group_index]
333 .borrow_mut()
334 .track_range
335 .end = row_index;
336 }
337
338 current_row_group_index = row.group_index;
340 if let Some(current_group_index) = current_row_group_index {
341 self.table.row_groups[current_group_index]
342 .borrow_mut()
343 .track_range
344 .start = row_index;
345 }
346 }
347
348 if let Some(current_group_index) = current_row_group_index {
350 self.table.row_groups[current_group_index]
351 .borrow_mut()
352 .track_range
353 .end = self.table.rows.len();
354 }
355 }
356
357 fn move_row_group_to_front(&mut self, index_to_move: usize) {
358 if index_to_move > 0 {
360 let removed_row_group = self.table.row_groups.remove(index_to_move);
361 self.table.row_groups.insert(0, removed_row_group);
362
363 for row in self.table.rows.iter_mut() {
364 let mut row = row.borrow_mut();
365 match row.group_index.as_mut() {
366 Some(group_index) if *group_index < index_to_move => *group_index += 1,
367 Some(group_index) if *group_index == index_to_move => *group_index = 0,
368 _ => {},
369 }
370 }
371 }
372
373 let row_range = self.table.row_groups[0].borrow().track_range.clone();
374 if row_range.start > 0 {
375 let removed_slots: Vec<Vec<TableSlot>> = self
377 .table
378 .slots
379 .splice(row_range.clone(), std::iter::empty())
380 .collect();
381 self.table.slots.splice(0..0, removed_slots);
382
383 let removed_rows: Vec<_> = self
385 .table
386 .rows
387 .splice(row_range, std::iter::empty())
388 .collect();
389 self.table.rows.splice(0..0, removed_rows);
390
391 self.regenerate_track_ranges();
394 }
395 }
396
397 fn move_row_group_to_end(&mut self, index_to_move: usize) {
398 let last_row_group_index = self.table.row_groups.len() - 1;
399
400 if index_to_move < last_row_group_index {
402 let removed_row_group = self.table.row_groups.remove(index_to_move);
403 self.table.row_groups.push(removed_row_group);
404
405 for row in self.table.rows.iter_mut() {
406 let mut row = row.borrow_mut();
407 match row.group_index.as_mut() {
408 Some(group_index) if *group_index > index_to_move => *group_index -= 1,
409 Some(group_index) if *group_index == index_to_move => {
410 *group_index = last_row_group_index
411 },
412 _ => {},
413 }
414 }
415 }
416
417 let row_range = self.table.row_groups[last_row_group_index]
418 .borrow()
419 .track_range
420 .clone();
421 if row_range.end < self.table.rows.len() {
422 let removed_slots: Vec<Vec<TableSlot>> = self
424 .table
425 .slots
426 .splice(row_range.clone(), std::iter::empty())
427 .collect();
428 self.table.slots.extend(removed_slots);
429
430 let removed_rows: Vec<_> = self
432 .table
433 .rows
434 .splice(row_range, std::iter::empty())
435 .collect();
436 self.table.rows.extend(removed_rows);
437
438 self.regenerate_track_ranges();
439 }
440 }
441
442 fn do_final_rowspan_calculation(&mut self) {
446 for row_index in 0..self.table.size.height {
447 let last_row_index_in_group = self.last_row_index_in_row_group_at_row_n(row_index);
448 for cell in self.table.slots[row_index].iter_mut() {
449 if let TableSlot::Cell(cell) = cell {
450 let mut cell = cell.borrow_mut();
451 if cell.rowspan == 1 {
452 continue;
453 }
454 let rowspan_to_end_of_group = last_row_index_in_group - row_index + 1;
455 if cell.rowspan == 0 {
456 cell.rowspan = rowspan_to_end_of_group;
457 } else {
458 cell.rowspan = cell.rowspan.min(rowspan_to_end_of_group);
459 }
460 }
461 }
462 }
463 }
464
465 fn current_y(&self) -> Option<usize> {
466 self.table.slots.len().checked_sub(1)
467 }
468
469 fn current_x(&self) -> Option<usize> {
470 Some(self.table.slots[self.current_y()?].len())
471 }
472
473 fn current_coords(&self) -> Option<TableSlotCoordinates> {
474 Some(TableSlotCoordinates::new(
475 self.current_x()?,
476 self.current_y()?,
477 ))
478 }
479
480 pub fn start_row(&mut self) {
481 self.table.slots.push(Vec::new());
482 self.table.size.height += 1;
483 self.create_slots_for_cells_above_with_rowspan(true);
484 }
485
486 pub fn end_row(&mut self) {
487 let current_x = self
493 .current_x()
494 .expect("Should have rows before calling `end_row`");
495 for i in (current_x..self.incoming_rowspans.len()).rev() {
496 if self.incoming_rowspans[i] == 0 {
497 self.incoming_rowspans.pop();
498 } else {
499 break;
500 }
501 }
502
503 self.create_slots_for_cells_above_with_rowspan(false);
504 }
505
506 fn create_spanned_slot_based_on_cell_above(
512 &self,
513 target_coords: TableSlotCoordinates,
514 ) -> Option<TableSlot> {
515 let y_above = self.current_y()?.checked_sub(1)?;
516 let coords_for_slot_above = TableSlotCoordinates::new(target_coords.x, y_above);
517 let slots_covering_slot_above = self.table.resolve_slot_at(coords_for_slot_above);
518
519 let coords_of_slots_that_cover_target: Vec<_> = slots_covering_slot_above
520 .into_iter()
521 .filter(|slot| slot.covers_cell_at(target_coords))
522 .map(|slot| target_coords - slot.coords)
523 .collect();
524
525 if coords_of_slots_that_cover_target.is_empty() {
526 None
527 } else {
528 Some(TableSlot::Spanned(coords_of_slots_that_cover_target))
529 }
530 }
531
532 fn create_slots_for_cells_above_with_rowspan(&mut self, stop_at_cell_opportunity: bool) {
542 let mut current_coords = self
543 .current_coords()
544 .expect("Should have rows before calling `create_slots_for_cells_above_with_rowspan`");
545 while let Some(span) = self.incoming_rowspans.get_mut(current_coords.x) {
546 if *span == 0 && stop_at_cell_opportunity {
549 break;
550 }
551
552 let new_cell = if *span != 0 {
553 *span -= 1;
554 self.create_spanned_slot_based_on_cell_above(current_coords)
555 .expect(
556 "Nonzero incoming rowspan cannot occur without a cell spanning this slot",
557 )
558 } else {
559 TableSlot::Empty
560 };
561
562 self.table.push_new_slot_to_last_row(new_cell);
563 current_coords.x += 1;
564 }
565 debug_assert_eq!(Some(current_coords), self.current_coords());
566 }
567
568 pub fn add_cell(&mut self, cell: ArcRefCell<TableSlotCell>) {
572 let current_coords = self
575 .current_coords()
576 .expect("Should have rows before calling `add_cell`");
577
578 let (colspan, rowspan) = {
579 let cell = cell.borrow();
580 (cell.colspan, cell.rowspan)
581 };
582
583 if self.incoming_rowspans.len() < current_coords.x + colspan {
584 self.incoming_rowspans
585 .resize(current_coords.x + colspan, 0isize);
586 }
587
588 debug_assert_eq!(
589 self.incoming_rowspans[current_coords.x], 0,
590 "Added a cell in a position that also had an incoming rowspan!"
591 );
592
593 let outgoing_rowspan = rowspan as isize - 1;
595 self.table.push_new_slot_to_last_row(TableSlot::Cell(cell));
596 self.incoming_rowspans[current_coords.x] = outgoing_rowspan;
597
598 for colspan_offset in 1..colspan {
600 let current_x_plus_colspan_offset = current_coords.x + colspan_offset;
601 let new_offset = TableSlotOffset::new(colspan_offset, 0);
602 let incoming_rowspan = &mut self.incoming_rowspans[current_x_plus_colspan_offset];
603 let new_slot = if *incoming_rowspan == 0 {
604 *incoming_rowspan = outgoing_rowspan;
605 TableSlot::new_spanned(new_offset)
606 } else {
607 if *incoming_rowspan > 0 {
620 *incoming_rowspan = std::cmp::max(*incoming_rowspan - 1, outgoing_rowspan);
621 }
622
623 let coords_of_spanned_cell =
625 TableSlotCoordinates::new(current_x_plus_colspan_offset, current_coords.y);
626 let mut incoming_slot = self
627 .create_spanned_slot_based_on_cell_above(coords_of_spanned_cell)
628 .expect(
629 "Nonzero incoming rowspan cannot occur without a cell spanning this slot",
630 );
631 incoming_slot.push_spanned(new_offset);
632 incoming_slot
633 };
634 self.table.push_new_slot_to_last_row(new_slot);
635 }
636
637 debug_assert_eq!(
638 Some(TableSlotCoordinates::new(
639 current_coords.x + colspan,
640 current_coords.y
641 )),
642 self.current_coords(),
643 "Must have produced `colspan` slot entries!"
644 );
645 self.create_slots_for_cells_above_with_rowspan(true);
646 }
647}
648
649pub(crate) struct TableBuilderTraversal<'style, 'dom> {
650 context: &'style LayoutContext<'style>,
651 info: &'style NodeAndStyleInfo<'dom>,
652
653 current_propagated_data: PropagatedBoxTreeData,
656
657 builder: TableBuilder,
660
661 current_anonymous_row_content: Vec<AnonymousTableContent<'dom>>,
662
663 current_row_group_index: Option<usize>,
665}
666
667impl<'style, 'dom> TableBuilderTraversal<'style, 'dom> {
668 pub(crate) fn new(
669 context: &'style LayoutContext<'style>,
670 info: &'style NodeAndStyleInfo<'dom>,
671 grid_style: Arc<ComputedValues>,
672 propagated_data: PropagatedBoxTreeData,
673 ) -> Self {
674 TableBuilderTraversal {
675 context,
676 info,
677 current_propagated_data: propagated_data,
678 builder: TableBuilder::new(
679 info.style.clone(),
680 grid_style,
681 info.into(),
682 propagated_data.allow_percentage_column_in_tables,
683 ),
684 current_anonymous_row_content: Vec::new(),
685 current_row_group_index: None,
686 }
687 }
688
689 pub(crate) fn finish(mut self) -> Table {
690 self.finish_anonymous_row_if_needed();
691 self.builder.finish()
692 }
693
694 fn finish_anonymous_row_if_needed(&mut self) {
695 if AnonymousTableContent::contents_are_whitespace_only(&self.current_anonymous_row_content)
696 {
697 self.current_anonymous_row_content.clear();
698 return;
699 }
700
701 let row_content = std::mem::take(&mut self.current_anonymous_row_content);
702 let anonymous_info = self
703 .info
704 .with_pseudo_element(self.context, PseudoElement::ServoAnonymousTableRow)
705 .expect("Should never fail to create anonymous row info.");
706 let mut row_builder =
707 TableRowBuilder::new(self, &anonymous_info, self.current_propagated_data);
708
709 for cell_content in row_content {
710 match cell_content {
711 AnonymousTableContent::Element {
712 info,
713 display,
714 contents,
715 box_slot,
716 } => {
717 row_builder.handle_element(&info, display, contents, box_slot);
718 },
719 AnonymousTableContent::Text(info, text) => {
720 row_builder.handle_text(&info, text);
721 },
722 AnonymousTableContent::EnterDisplayContents(styles) => {
723 row_builder.enter_display_contents(styles)
724 },
725 AnonymousTableContent::LeaveDisplayContents => row_builder.leave_display_contents(),
726 }
727 }
728
729 row_builder.finish();
730
731 let style = anonymous_info.style.clone();
732 let table_row = ArcRefCell::new(TableTrack {
733 base: LayoutBoxBase::new((&anonymous_info).into(), style.clone()),
734 group_index: self.current_row_group_index,
735 is_anonymous: true,
736 shared_background_style: SharedStyle::new(style),
737 });
738 self.push_table_row(table_row.clone());
739
740 anonymous_info
741 .node
742 .box_slot()
743 .set(LayoutBox::TableLevelBox(TableLevelBox::Track(table_row)))
744 }
745
746 fn push_table_row(&mut self, table_track: ArcRefCell<TableTrack>) {
747 self.builder.table.rows.push(table_track);
748
749 let last_row = self.builder.table.rows.len();
750 if let Some(index) = self.current_row_group_index {
751 let row_group = &mut self.builder.table.row_groups[index];
752 row_group.borrow_mut().track_range.end = last_row;
753 }
754 }
755}
756
757impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
758 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
759 self.current_anonymous_row_content
760 .push(AnonymousTableContent::Text(info.clone(), text));
761 }
762
763 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
764 self.current_anonymous_row_content
765 .push(AnonymousTableContent::EnterDisplayContents(styles));
766 }
767
768 fn leave_display_contents(&mut self) {
769 self.current_anonymous_row_content
770 .push(AnonymousTableContent::LeaveDisplayContents);
771 }
772
773 fn handle_element(
775 &mut self,
776 info: &NodeAndStyleInfo<'dom>,
777 display: DisplayGeneratingBox,
778 contents: Contents,
779 box_slot: BoxSlot<'dom>,
780 ) {
781 match display {
782 DisplayGeneratingBox::LayoutInternal(internal) => match internal {
783 DisplayLayoutInternal::TableRowGroup |
784 DisplayLayoutInternal::TableFooterGroup |
785 DisplayLayoutInternal::TableHeaderGroup => {
786 self.finish_anonymous_row_if_needed();
787 self.builder.incoming_rowspans.clear();
788
789 let next_row_index = self.builder.table.rows.len();
790 let row_group = ArcRefCell::new(TableTrackGroup {
791 base: LayoutBoxBase::new(info.into(), info.style.clone()),
792 group_type: internal.into(),
793 track_range: next_row_index..next_row_index,
794 shared_background_style: SharedStyle::new(info.style.clone()),
795 });
796 self.builder.table.row_groups.push(row_group.clone());
797
798 let new_row_group_index = self.builder.table.row_groups.len() - 1;
799 let context = self.context;
800 let mut row_group_builder = TableRowGroupBuilder::new(
801 self,
802 info,
803 self.current_propagated_data,
804 new_row_group_index,
805 );
806
807 contents
808 .non_replaced_contents()
809 .expect("Replaced should not have a LayoutInternal display type.")
810 .traverse(context, info, &mut row_group_builder);
811
812 row_group_builder.finish();
813
814 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
815 row_group,
816 )));
817 },
818 DisplayLayoutInternal::TableRow => {
819 self.finish_anonymous_row_if_needed();
820
821 let context = self.context;
822 let mut row_builder =
823 TableRowBuilder::new(self, info, self.current_propagated_data);
824
825 contents
826 .non_replaced_contents()
827 .expect("Replaced should not have a LayoutInternal display type.")
828 .traverse(context, info, &mut row_builder);
829 row_builder.finish();
830
831 let row = ArcRefCell::new(TableTrack {
832 base: LayoutBoxBase::new(info.into(), info.style.clone()),
833 group_index: self.current_row_group_index,
834 is_anonymous: false,
835 shared_background_style: SharedStyle::new(info.style.clone()),
836 });
837 self.push_table_row(row.clone());
838 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(row)));
839 },
840 DisplayLayoutInternal::TableColumn => {
841 let old_box = box_slot.take_layout_box();
842 let old_column = old_box.and_then(|layout_box| match layout_box {
843 LayoutBox::TableLevelBox(TableLevelBox::Track(column)) => Some(column),
844 _ => None,
845 });
846 let column = add_column(
847 &mut self.builder.table.columns,
848 info,
849 None, false, old_column,
852 );
853 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(column)));
854 },
855 DisplayLayoutInternal::TableColumnGroup => {
856 let column_group_index = self.builder.table.column_groups.len();
857 let mut column_group_builder = TableColumnGroupBuilder {
858 column_group_index,
859 columns: Vec::new(),
860 };
861
862 contents
863 .non_replaced_contents()
864 .expect("Replaced should not have a LayoutInternal display type.")
865 .traverse(self.context, info, &mut column_group_builder);
866
867 let first_column = self.builder.table.columns.len();
868 if column_group_builder.columns.is_empty() {
869 add_column(
870 &mut self.builder.table.columns,
871 info,
872 Some(column_group_index),
873 true, None,
875 );
876 } else {
877 self.builder
878 .table
879 .columns
880 .extend(column_group_builder.columns);
881 }
882
883 let column_group = ArcRefCell::new(TableTrackGroup {
884 base: LayoutBoxBase::new(info.into(), info.style.clone()),
885 group_type: internal.into(),
886 track_range: first_column..self.builder.table.columns.len(),
887 shared_background_style: SharedStyle::new(info.style.clone()),
888 });
889 self.builder.table.column_groups.push(column_group.clone());
890 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
891 column_group,
892 )));
893 },
894 DisplayLayoutInternal::TableCaption => {
895 let old_box = box_slot.take_layout_box();
896 let old_caption = old_box.and_then(|layout_box| match layout_box {
897 LayoutBox::TableLevelBox(TableLevelBox::Caption(caption)) => Some(caption),
898 _ => None,
899 });
900
901 let caption = old_caption.unwrap_or_else(|| {
902 let non_replaced_contents = contents
903 .non_replaced_contents()
904 .expect("Replaced should not have a LayoutInternal display type.");
905 let contents = IndependentFormattingContextContents::Flow(
906 BlockFormattingContext::construct(
907 self.context,
908 info,
909 non_replaced_contents,
910 self.current_propagated_data,
911 false, ),
913 );
914 let base = LayoutBoxBase::new(info.into(), info.style.clone());
915 ArcRefCell::new(TableCaption {
916 context: IndependentFormattingContext::new(
917 base,
918 contents,
919 self.current_propagated_data,
920 ),
921 })
922 });
923
924 self.builder.table.captions.push(caption.clone());
925 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Caption(caption)));
926 },
927 DisplayLayoutInternal::TableCell => {
928 self.current_anonymous_row_content
929 .push(AnonymousTableContent::Element {
930 info: info.clone(),
931 display,
932 contents,
933 box_slot,
934 });
935 },
936 },
937 _ => {
938 self.current_anonymous_row_content
939 .push(AnonymousTableContent::Element {
940 info: info.clone(),
941 display,
942 contents,
943 box_slot,
944 });
945 },
946 }
947 }
948}
949
950struct TableRowGroupBuilder<'style, 'builder, 'dom, 'a> {
951 table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
952 info: &'a NodeAndStyleInfo<'dom>,
953 propagated_data: PropagatedBoxTreeData,
954 current_anonymous_row_content: Vec<AnonymousTableContent<'dom>>,
955}
956
957impl<'style, 'builder, 'dom, 'a> TableRowGroupBuilder<'style, 'builder, 'dom, 'a> {
958 fn new(
959 table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
960 info: &'a NodeAndStyleInfo<'dom>,
961 propagated_data: PropagatedBoxTreeData,
962 row_group_index: usize,
963 ) -> Self {
964 debug_assert!(table_traversal.current_row_group_index.is_none());
966 table_traversal.current_row_group_index = Some(row_group_index);
967
968 Self {
969 table_traversal,
970 info,
971 propagated_data,
972 current_anonymous_row_content: Vec::new(),
973 }
974 }
975
976 fn finish(mut self) {
977 self.finish_anonymous_row_if_needed();
978 self.table_traversal.current_row_group_index = None;
979 self.table_traversal.builder.incoming_rowspans.clear();
980 }
981
982 fn finish_anonymous_row_if_needed(&mut self) {
983 if AnonymousTableContent::contents_are_whitespace_only(&self.current_anonymous_row_content)
984 {
985 self.current_anonymous_row_content.clear();
986 return;
987 }
988
989 let row_content = std::mem::take(&mut self.current_anonymous_row_content);
990 let anonymous_info = self
991 .info
992 .with_pseudo_element(
993 self.table_traversal.context,
994 PseudoElement::ServoAnonymousTableRow,
995 )
996 .expect("Should never fail to create anonymous row info.");
997
998 let mut row_builder =
999 TableRowBuilder::new(self.table_traversal, &anonymous_info, self.propagated_data);
1000
1001 for cell_content in row_content {
1002 match cell_content {
1003 AnonymousTableContent::Element {
1004 info,
1005 display,
1006 contents,
1007 box_slot,
1008 } => {
1009 row_builder.handle_element(&info, display, contents, box_slot);
1010 },
1011 AnonymousTableContent::Text(info, text) => {
1012 row_builder.handle_text(&info, text);
1013 },
1014 AnonymousTableContent::EnterDisplayContents(styles) => {
1015 row_builder.enter_display_contents(styles)
1016 },
1017 AnonymousTableContent::LeaveDisplayContents => row_builder.leave_display_contents(),
1018 }
1019 }
1020
1021 row_builder.finish();
1022
1023 let style = anonymous_info.style.clone();
1024 let table_row = ArcRefCell::new(TableTrack {
1025 base: LayoutBoxBase::new((&anonymous_info).into(), style.clone()),
1026 group_index: self.table_traversal.current_row_group_index,
1027 is_anonymous: true,
1028 shared_background_style: SharedStyle::new(style),
1029 });
1030 self.table_traversal.push_table_row(table_row.clone());
1031
1032 anonymous_info
1033 .node
1034 .box_slot()
1035 .set(LayoutBox::TableLevelBox(TableLevelBox::Track(table_row)));
1036 }
1037}
1038
1039impl<'dom> TraversalHandler<'dom> for TableRowGroupBuilder<'_, '_, 'dom, '_> {
1040 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
1041 self.current_anonymous_row_content
1042 .push(AnonymousTableContent::Text(info.clone(), text));
1043 }
1044
1045 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
1046 self.current_anonymous_row_content
1047 .push(AnonymousTableContent::EnterDisplayContents(styles));
1048 }
1049
1050 fn leave_display_contents(&mut self) {
1051 self.current_anonymous_row_content
1052 .push(AnonymousTableContent::LeaveDisplayContents);
1053 }
1054
1055 fn handle_element(
1056 &mut self,
1057 info: &NodeAndStyleInfo<'dom>,
1058 display: DisplayGeneratingBox,
1059 contents: Contents,
1060 box_slot: BoxSlot<'dom>,
1061 ) {
1062 match display {
1063 DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableRow) => {
1064 self.finish_anonymous_row_if_needed();
1065
1066 let context = self.table_traversal.context;
1067 let mut row_builder =
1068 TableRowBuilder::new(self.table_traversal, info, self.propagated_data);
1069
1070 contents
1071 .non_replaced_contents()
1072 .expect("Replaced should not have a LayoutInternal display type.")
1073 .traverse(context, info, &mut row_builder);
1074 row_builder.finish();
1075
1076 let row = ArcRefCell::new(TableTrack {
1077 base: LayoutBoxBase::new(info.into(), info.style.clone()),
1078 group_index: self.table_traversal.current_row_group_index,
1079 is_anonymous: false,
1080 shared_background_style: SharedStyle::new(info.style.clone()),
1081 });
1082 self.table_traversal.push_table_row(row.clone());
1083 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(row)));
1084 },
1085
1086 _ => {
1087 self.current_anonymous_row_content
1088 .push(AnonymousTableContent::Element {
1089 info: info.clone(),
1090 display,
1091 contents,
1092 box_slot,
1093 });
1094 },
1095 }
1096 }
1097}
1098
1099struct TableRowBuilder<'style, 'builder, 'dom, 'a> {
1100 table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
1101
1102 info: &'a NodeAndStyleInfo<'dom>,
1105
1106 current_anonymous_cell_content: Vec<AnonymousTableContent<'dom>>,
1107
1108 propagated_data: PropagatedBoxTreeData,
1110}
1111
1112impl<'style, 'builder, 'dom, 'a> TableRowBuilder<'style, 'builder, 'dom, 'a> {
1113 fn new(
1114 table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
1115 info: &'a NodeAndStyleInfo<'dom>,
1116 propagated_data: PropagatedBoxTreeData,
1117 ) -> Self {
1118 table_traversal.builder.start_row();
1119
1120 TableRowBuilder {
1121 table_traversal,
1122 info,
1123 current_anonymous_cell_content: Vec::new(),
1124 propagated_data,
1125 }
1126 }
1127
1128 fn finish(mut self) {
1129 self.finish_current_anonymous_cell_if_needed();
1130 self.table_traversal.builder.end_row();
1131 }
1132
1133 fn finish_current_anonymous_cell_if_needed(&mut self) {
1134 if AnonymousTableContent::contents_are_whitespace_only(&self.current_anonymous_cell_content)
1135 {
1136 self.current_anonymous_cell_content.clear();
1137 return;
1138 }
1139
1140 let context = self.table_traversal.context;
1141 let anonymous_info = self
1142 .info
1143 .with_pseudo_element(context, PseudoElement::ServoAnonymousTableCell)
1144 .expect("Should never fail to create anonymous table cell info");
1145 let propagated_data = self.propagated_data.disallowing_percentage_table_columns();
1146 let mut builder = BlockContainerBuilder::new(context, &anonymous_info, propagated_data);
1147
1148 for cell_content in self.current_anonymous_cell_content.drain(..) {
1149 match cell_content {
1150 AnonymousTableContent::Element {
1151 info,
1152 display,
1153 contents,
1154 box_slot,
1155 } => {
1156 builder.handle_element(&info, display, contents, box_slot);
1157 },
1158 AnonymousTableContent::Text(info, text) => {
1159 builder.handle_text(&info, text);
1160 },
1161 AnonymousTableContent::EnterDisplayContents(styles) => {
1162 builder.enter_display_contents(styles)
1163 },
1164 AnonymousTableContent::LeaveDisplayContents => builder.leave_display_contents(),
1165 }
1166 }
1167
1168 let block_container = builder.finish();
1169 let new_table_cell = ArcRefCell::new(TableSlotCell {
1170 context: IndependentFormattingContext::new(
1171 LayoutBoxBase::new(BaseFragmentInfo::anonymous(), anonymous_info.style),
1172 IndependentFormattingContextContents::Flow(
1173 BlockFormattingContext::from_block_container(block_container),
1174 ),
1175 propagated_data,
1176 ),
1177 colspan: 1,
1178 rowspan: 1,
1179 });
1180 self.table_traversal
1181 .builder
1182 .add_cell(new_table_cell.clone());
1183
1184 anonymous_info
1185 .node
1186 .box_slot()
1187 .set(LayoutBox::TableLevelBox(TableLevelBox::Cell(
1188 new_table_cell,
1189 )));
1190 }
1191}
1192
1193impl<'dom> TraversalHandler<'dom> for TableRowBuilder<'_, '_, 'dom, '_> {
1194 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
1195 self.current_anonymous_cell_content
1196 .push(AnonymousTableContent::Text(info.clone(), text));
1197 }
1198
1199 fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
1200 self.current_anonymous_cell_content
1201 .push(AnonymousTableContent::EnterDisplayContents(styles));
1202 }
1203
1204 fn leave_display_contents(&mut self) {
1205 self.current_anonymous_cell_content
1206 .push(AnonymousTableContent::LeaveDisplayContents);
1207 }
1208
1209 fn handle_element(
1211 &mut self,
1212 info: &NodeAndStyleInfo<'dom>,
1213 display: DisplayGeneratingBox,
1214 contents: Contents,
1215 box_slot: BoxSlot<'dom>,
1216 ) {
1217 #[allow(clippy::collapsible_match)] match display {
1219 DisplayGeneratingBox::LayoutInternal(internal) => match internal {
1220 DisplayLayoutInternal::TableCell => {
1221 self.finish_current_anonymous_cell_if_needed();
1222
1223 let old_box = box_slot.take_layout_box();
1224 let old_cell = old_box.and_then(|layout_box| match layout_box {
1225 LayoutBox::TableLevelBox(TableLevelBox::Cell(cell)) => Some(cell),
1226 _ => None,
1227 });
1228
1229 let cell = old_cell.unwrap_or_else(|| {
1230 let (rowspan, colspan) = if info.pseudo_element_chain().is_empty() {
1233 let rowspan = info.node.table_rowspan().unwrap_or(1) as usize;
1234 let colspan = info.node.table_colspan().unwrap_or(1) as usize;
1235
1236 assert!((1..=1000).contains(&colspan));
1239 assert!((0..=65534).contains(&rowspan));
1240
1241 (rowspan, colspan)
1242 } else {
1243 (1, 1)
1244 };
1245
1246 let propagated_data =
1247 self.propagated_data.disallowing_percentage_table_columns();
1248 let non_replaced_contents = contents
1249 .non_replaced_contents()
1250 .expect("Replaced should not have a LayoutInternal display type.");
1251
1252 let contents = BlockFormattingContext::construct(
1253 self.table_traversal.context,
1254 info,
1255 non_replaced_contents,
1256 propagated_data,
1257 false, );
1259
1260 ArcRefCell::new(TableSlotCell {
1261 context: IndependentFormattingContext::new(
1262 LayoutBoxBase::new(info.into(), info.style.clone()),
1263 IndependentFormattingContextContents::Flow(contents),
1264 propagated_data,
1265 ),
1266 colspan,
1267 rowspan,
1268 })
1269 });
1270
1271 self.table_traversal.builder.add_cell(cell.clone());
1272 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Cell(cell)));
1273 },
1274 _ => {
1275 self.current_anonymous_cell_content
1277 .push(AnonymousTableContent::Element {
1278 info: info.clone(),
1279 display,
1280 contents,
1281 box_slot,
1282 });
1283 },
1284 },
1285 _ => {
1286 self.current_anonymous_cell_content
1287 .push(AnonymousTableContent::Element {
1288 info: info.clone(),
1289 display,
1290 contents,
1291 box_slot,
1292 });
1293 },
1294 }
1295 }
1296}
1297
1298struct TableColumnGroupBuilder {
1299 column_group_index: usize,
1300 columns: Vec<ArcRefCell<TableTrack>>,
1301}
1302
1303impl<'dom> TraversalHandler<'dom> for TableColumnGroupBuilder {
1304 fn handle_text(&mut self, _info: &NodeAndStyleInfo<'dom>, _text: Cow<'dom, str>) {}
1305 fn enter_display_contents(&mut self, _: SharedInlineStyles) {}
1306 fn leave_display_contents(&mut self) {}
1307 fn handle_element(
1308 &mut self,
1309 info: &NodeAndStyleInfo<'dom>,
1310 display: DisplayGeneratingBox,
1311 _contents: Contents,
1312 box_slot: BoxSlot<'dom>,
1313 ) {
1314 if !matches!(
1315 display,
1316 DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableColumn)
1317 ) {
1318 ::std::mem::forget(box_slot);
1321 return;
1322 }
1323 let old_box = box_slot.take_layout_box();
1324 let old_column = old_box.and_then(|layout_box| match layout_box {
1325 LayoutBox::TableLevelBox(TableLevelBox::Track(column)) => Some(column),
1326 _ => None,
1327 });
1328 let column = add_column(
1329 &mut self.columns,
1330 info,
1331 Some(self.column_group_index),
1332 false, old_column,
1334 );
1335 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(column)));
1336 }
1337}
1338
1339impl From<DisplayLayoutInternal> for TableTrackGroupType {
1340 fn from(value: DisplayLayoutInternal) -> Self {
1341 match value {
1342 DisplayLayoutInternal::TableColumnGroup => TableTrackGroupType::ColumnGroup,
1343 DisplayLayoutInternal::TableFooterGroup => TableTrackGroupType::FooterGroup,
1344 DisplayLayoutInternal::TableHeaderGroup => TableTrackGroupType::HeaderGroup,
1345 DisplayLayoutInternal::TableRowGroup => TableTrackGroupType::RowGroup,
1346 _ => unreachable!(),
1347 }
1348 }
1349}
1350
1351fn add_column(
1352 collection: &mut Vec<ArcRefCell<TableTrack>>,
1353 column_info: &NodeAndStyleInfo,
1354 group_index: Option<usize>,
1355 is_anonymous: bool,
1356 old_column: Option<ArcRefCell<TableTrack>>,
1357) -> ArcRefCell<TableTrack> {
1358 let span = if column_info.pseudo_element_chain().is_empty() {
1359 column_info.node.table_span().unwrap_or(1)
1360 } else {
1361 1
1362 };
1363
1364 assert!((1..=1000).contains(&span));
1366
1367 let column = match old_column {
1368 Some(column) => {
1369 column.borrow_mut().group_index = group_index;
1370 column
1371 },
1372 None => ArcRefCell::new(TableTrack {
1373 base: LayoutBoxBase::new(column_info.into(), column_info.style.clone()),
1374 group_index,
1375 is_anonymous,
1376 shared_background_style: SharedStyle::new(column_info.style.clone()),
1377 }),
1378 };
1379 collection.extend(repeat_n(column.clone(), span as usize));
1380 column
1381}