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