1use std::borrow::Cow;
6use std::iter::repeat_n;
7
8use atomic_refcell::AtomicRef;
9use layout_api::wrapper_traits::ThreadSafeLayoutNode;
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::{BlockContainerBuilder, BlockFormattingContext};
26use crate::formatting_contexts::{
27 IndependentFormattingContext, IndependentFormattingContextContents,
28};
29use crate::fragment_tree::BaseFragmentInfo;
30use crate::layout_box_base::LayoutBoxBase;
31use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal};
32use crate::{PropagatedBoxTreeData, SharedStyle};
33
34#[derive(Debug)]
36pub(super) struct ResolvedSlotAndLocation<'a> {
37 pub cell: AtomicRef<'a, TableSlotCell>,
38 pub coords: TableSlotCoordinates,
39}
40
41impl ResolvedSlotAndLocation<'_> {
42 fn covers_cell_at(&self, coords: TableSlotCoordinates) -> bool {
43 let covered_in_x =
44 coords.x >= self.coords.x && coords.x < self.coords.x + self.cell.colspan;
45 let covered_in_y = coords.y >= self.coords.y &&
46 (self.cell.rowspan == 0 || coords.y < self.coords.y + self.cell.rowspan);
47 covered_in_x && covered_in_y
48 }
49}
50
51pub(crate) enum AnonymousTableContent<'dom> {
52 Text(NodeAndStyleInfo<'dom>, Cow<'dom, str>),
53 Element {
54 info: NodeAndStyleInfo<'dom>,
55 display: DisplayGeneratingBox,
56 contents: Contents,
57 box_slot: BoxSlot<'dom>,
58 },
59}
60
61impl AnonymousTableContent<'_> {
62 fn is_whitespace_only(&self) -> bool {
63 match self {
64 Self::Element { .. } => false,
65 Self::Text(_, text) => text.chars().all(char_is_whitespace),
66 }
67 }
68
69 fn contents_are_whitespace_only(contents: &[Self]) -> bool {
70 contents.iter().all(|content| content.is_whitespace_only())
71 }
72}
73
74impl Table {
75 pub(crate) fn construct(
76 context: &LayoutContext,
77 info: &NodeAndStyleInfo,
78 grid_style: Arc<ComputedValues>,
79 contents: NonReplacedContents,
80 propagated_data: PropagatedBoxTreeData,
81 ) -> Self {
82 let mut traversal = TableBuilderTraversal::new(context, info, grid_style, propagated_data);
83 contents.traverse(context, info, &mut traversal);
84 traversal.finish()
85 }
86
87 pub(crate) fn construct_anonymous<'dom>(
88 context: &LayoutContext,
89 parent_info: &NodeAndStyleInfo<'dom>,
90 contents: Vec<AnonymousTableContent<'dom>>,
91 propagated_data: PropagatedBoxTreeData,
92 ) -> (NodeAndStyleInfo<'dom>, IndependentFormattingContext) {
93 let table_info = parent_info
94 .with_pseudo_element(context, PseudoElement::ServoAnonymousTable)
95 .expect("Should never fail to create anonymous table info.");
96 let table_style = table_info.style.clone();
97 let mut table_builder =
98 TableBuilderTraversal::new(context, &table_info, table_style.clone(), propagated_data);
99
100 for content in contents {
101 match content {
102 AnonymousTableContent::Element {
103 info,
104 display,
105 contents,
106 box_slot,
107 } => {
108 table_builder.handle_element(&info, display, contents, box_slot);
109 },
110 AnonymousTableContent::Text(..) => {
111 },
115 }
116 }
117
118 let mut table = table_builder.finish();
119 table.anonymous = true;
120
121 let ifc = IndependentFormattingContext::new(
122 LayoutBoxBase::new((&table_info).into(), table_style),
123 IndependentFormattingContextContents::Table(table),
124 );
125
126 (table_info, ifc)
127 }
128
129 fn push_new_slot_to_last_row(&mut self, slot: TableSlot) {
131 let last_row = match self.slots.last_mut() {
132 Some(row) => row,
133 None => {
134 unreachable!("Should have some rows before calling `push_new_slot_to_last_row`")
135 },
136 };
137
138 self.size.width = self.size.width.max(last_row.len() + 1);
139 last_row.push(slot);
140 }
141
142 pub(super) fn resolve_slot_at(
148 &self,
149 coords: TableSlotCoordinates,
150 ) -> Vec<ResolvedSlotAndLocation<'_>> {
151 let slot = self.get_slot(coords);
152 match slot {
153 Some(TableSlot::Cell(cell)) => vec![ResolvedSlotAndLocation {
154 cell: cell.borrow(),
155 coords,
156 }],
157 Some(TableSlot::Spanned(offsets)) => offsets
158 .iter()
159 .flat_map(|offset| self.resolve_slot_at(coords - *offset))
160 .collect(),
161 Some(TableSlot::Empty) | None => {
162 warn!("Tried to resolve an empty or nonexistant slot!");
163 vec![]
164 },
165 }
166 }
167}
168
169impl TableSlot {
170 pub fn push_spanned(&mut self, new_offset: TableSlotOffset) {
172 match *self {
173 TableSlot::Cell { .. } => {
174 panic!(
175 "Should never have a table model error with an originating cell slot overlapping a spanned slot"
176 )
177 },
178 TableSlot::Spanned(ref mut vec) => vec.insert(0, new_offset),
179 TableSlot::Empty => {
180 panic!("Should never have a table model error with an empty slot");
181 },
182 }
183 }
184}
185
186pub struct TableBuilder {
187 table: Table,
189
190 pub incoming_rowspans: Vec<isize>,
200}
201
202impl TableBuilder {
203 pub(super) fn new(
204 style: Arc<ComputedValues>,
205 grid_style: Arc<ComputedValues>,
206 base_fragment_info: BaseFragmentInfo,
207 percentage_columns_allowed_for_inline_content_sizes: bool,
208 ) -> Self {
209 Self {
210 table: Table::new(
211 style,
212 grid_style,
213 base_fragment_info,
214 percentage_columns_allowed_for_inline_content_sizes,
215 ),
216 incoming_rowspans: Vec::new(),
217 }
218 }
219
220 pub fn new_for_tests() -> Self {
221 let testing_style =
222 ComputedValues::initial_values_with_font_override(Font::initial_values());
223 Self::new(
224 testing_style.clone(),
225 testing_style.clone(),
226 BaseFragmentInfo::anonymous(),
227 true, )
229 }
230
231 pub fn last_row_index_in_row_group_at_row_n(&self, n: usize) -> usize {
232 for row_group in self.table.row_groups.iter() {
236 let row_group = row_group.borrow();
237 if row_group.track_range.start > n {
238 return row_group.track_range.start - 1;
239 }
240 }
241 self.table.size.height - 1
242 }
243
244 pub fn finish(mut self) -> Table {
245 self.adjust_table_geometry_for_columns_and_colgroups();
246 self.do_missing_cells_fixup();
247 self.reorder_first_thead_and_tfoot();
248 self.do_final_rowspan_calculation();
249 self.table
250 }
251
252 fn do_missing_cells_fixup(&mut self) {
255 for row in self.table.slots.iter_mut() {
256 row.resize_with(self.table.size.width, || TableSlot::Empty);
257 }
258 }
259
260 fn adjust_table_geometry_for_columns_and_colgroups(&mut self) {
266 if self.table.rows.is_empty() && self.table.row_groups.is_empty() {
267 self.table.columns.truncate(0);
268 self.table.column_groups.truncate(0);
269 } else {
270 self.table.size.width = self.table.size.width.max(self.table.columns.len());
271 }
272 }
273
274 fn reorder_first_thead_and_tfoot(&mut self) {
279 let mut thead_index = None;
280 let mut tfoot_index = None;
281 for (row_group_index, row_group) in self.table.row_groups.iter().enumerate() {
282 let row_group = row_group.borrow();
283 if thead_index.is_none() && row_group.group_type == TableTrackGroupType::HeaderGroup {
284 thead_index = Some(row_group_index);
285 }
286 if tfoot_index.is_none() && row_group.group_type == TableTrackGroupType::FooterGroup {
287 tfoot_index = Some(row_group_index);
288 }
289 if thead_index.is_some() && tfoot_index.is_some() {
290 break;
291 }
292 }
293
294 if let Some(thead_index) = thead_index {
295 self.move_row_group_to_front(thead_index)
296 }
297
298 if let Some(mut tfoot_index) = tfoot_index {
299 if thead_index.unwrap_or(0) > tfoot_index {
302 tfoot_index += 1;
303 }
304 self.move_row_group_to_end(tfoot_index)
305 }
306 }
307
308 fn regenerate_track_ranges(&mut self) {
309 let mut current_row_group_index = None;
311 for (row_index, row) in self.table.rows.iter().enumerate() {
312 let row = row.borrow();
313 if current_row_group_index == row.group_index {
314 continue;
315 }
316
317 if let Some(current_group_index) = current_row_group_index {
319 self.table.row_groups[current_group_index]
320 .borrow_mut()
321 .track_range
322 .end = row_index;
323 }
324
325 current_row_group_index = row.group_index;
327 if let Some(current_group_index) = current_row_group_index {
328 self.table.row_groups[current_group_index]
329 .borrow_mut()
330 .track_range
331 .start = row_index;
332 }
333 }
334
335 if let Some(current_group_index) = current_row_group_index {
337 self.table.row_groups[current_group_index]
338 .borrow_mut()
339 .track_range
340 .end = self.table.rows.len();
341 }
342 }
343
344 fn move_row_group_to_front(&mut self, index_to_move: usize) {
345 if index_to_move > 0 {
347 let removed_row_group = self.table.row_groups.remove(index_to_move);
348 self.table.row_groups.insert(0, removed_row_group);
349
350 for row in self.table.rows.iter_mut() {
351 let mut row = row.borrow_mut();
352 match row.group_index.as_mut() {
353 Some(group_index) if *group_index < index_to_move => *group_index += 1,
354 Some(group_index) if *group_index == index_to_move => *group_index = 0,
355 _ => {},
356 }
357 }
358 }
359
360 let row_range = self.table.row_groups[0].borrow().track_range.clone();
361 if row_range.start > 0 {
362 let removed_slots: Vec<Vec<TableSlot>> = self
364 .table
365 .slots
366 .splice(row_range.clone(), std::iter::empty())
367 .collect();
368 self.table.slots.splice(0..0, removed_slots);
369
370 let removed_rows: Vec<_> = self
372 .table
373 .rows
374 .splice(row_range, std::iter::empty())
375 .collect();
376 self.table.rows.splice(0..0, removed_rows);
377
378 self.regenerate_track_ranges();
381 }
382 }
383
384 fn move_row_group_to_end(&mut self, index_to_move: usize) {
385 let last_row_group_index = self.table.row_groups.len() - 1;
386
387 if index_to_move < last_row_group_index {
389 let removed_row_group = self.table.row_groups.remove(index_to_move);
390 self.table.row_groups.push(removed_row_group);
391
392 for row in self.table.rows.iter_mut() {
393 let mut row = row.borrow_mut();
394 match row.group_index.as_mut() {
395 Some(group_index) if *group_index > index_to_move => *group_index -= 1,
396 Some(group_index) if *group_index == index_to_move => {
397 *group_index = last_row_group_index
398 },
399 _ => {},
400 }
401 }
402 }
403
404 let row_range = self.table.row_groups[last_row_group_index]
405 .borrow()
406 .track_range
407 .clone();
408 if row_range.end < self.table.rows.len() {
409 let removed_slots: Vec<Vec<TableSlot>> = self
411 .table
412 .slots
413 .splice(row_range.clone(), std::iter::empty())
414 .collect();
415 self.table.slots.extend(removed_slots);
416
417 let removed_rows: Vec<_> = self
419 .table
420 .rows
421 .splice(row_range, std::iter::empty())
422 .collect();
423 self.table.rows.extend(removed_rows);
424
425 self.regenerate_track_ranges();
426 }
427 }
428
429 fn do_final_rowspan_calculation(&mut self) {
433 for row_index in 0..self.table.size.height {
434 let last_row_index_in_group = self.last_row_index_in_row_group_at_row_n(row_index);
435 for cell in self.table.slots[row_index].iter_mut() {
436 if let TableSlot::Cell(cell) = cell {
437 let mut cell = cell.borrow_mut();
438 if cell.rowspan == 1 {
439 continue;
440 }
441 let rowspan_to_end_of_group = last_row_index_in_group - row_index + 1;
442 if cell.rowspan == 0 {
443 cell.rowspan = rowspan_to_end_of_group;
444 } else {
445 cell.rowspan = cell.rowspan.min(rowspan_to_end_of_group);
446 }
447 }
448 }
449 }
450 }
451
452 fn current_y(&self) -> Option<usize> {
453 self.table.slots.len().checked_sub(1)
454 }
455
456 fn current_x(&self) -> Option<usize> {
457 Some(self.table.slots[self.current_y()?].len())
458 }
459
460 fn current_coords(&self) -> Option<TableSlotCoordinates> {
461 Some(TableSlotCoordinates::new(
462 self.current_x()?,
463 self.current_y()?,
464 ))
465 }
466
467 pub fn start_row(&mut self) {
468 self.table.slots.push(Vec::new());
469 self.table.size.height += 1;
470 self.create_slots_for_cells_above_with_rowspan(true);
471 }
472
473 pub fn end_row(&mut self) {
474 let current_x = self
480 .current_x()
481 .expect("Should have rows before calling `end_row`");
482 for i in (current_x..self.incoming_rowspans.len()).rev() {
483 if self.incoming_rowspans[i] == 0 {
484 self.incoming_rowspans.pop();
485 } else {
486 break;
487 }
488 }
489
490 self.create_slots_for_cells_above_with_rowspan(false);
491 }
492
493 fn create_spanned_slot_based_on_cell_above(
499 &self,
500 target_coords: TableSlotCoordinates,
501 ) -> Option<TableSlot> {
502 let y_above = self.current_y()?.checked_sub(1)?;
503 let coords_for_slot_above = TableSlotCoordinates::new(target_coords.x, y_above);
504 let slots_covering_slot_above = self.table.resolve_slot_at(coords_for_slot_above);
505
506 let coords_of_slots_that_cover_target: Vec<_> = slots_covering_slot_above
507 .into_iter()
508 .filter(|slot| slot.covers_cell_at(target_coords))
509 .map(|slot| target_coords - slot.coords)
510 .collect();
511
512 if coords_of_slots_that_cover_target.is_empty() {
513 None
514 } else {
515 Some(TableSlot::Spanned(coords_of_slots_that_cover_target))
516 }
517 }
518
519 fn create_slots_for_cells_above_with_rowspan(&mut self, stop_at_cell_opportunity: bool) {
529 let mut current_coords = self
530 .current_coords()
531 .expect("Should have rows before calling `create_slots_for_cells_above_with_rowspan`");
532 while let Some(span) = self.incoming_rowspans.get_mut(current_coords.x) {
533 if *span == 0 && stop_at_cell_opportunity {
536 break;
537 }
538
539 let new_cell = if *span != 0 {
540 *span -= 1;
541 self.create_spanned_slot_based_on_cell_above(current_coords)
542 .expect(
543 "Nonzero incoming rowspan cannot occur without a cell spanning this slot",
544 )
545 } else {
546 TableSlot::Empty
547 };
548
549 self.table.push_new_slot_to_last_row(new_cell);
550 current_coords.x += 1;
551 }
552 debug_assert_eq!(Some(current_coords), self.current_coords());
553 }
554
555 pub fn add_cell(&mut self, cell: ArcRefCell<TableSlotCell>) {
559 let current_coords = self
562 .current_coords()
563 .expect("Should have rows before calling `add_cell`");
564
565 let (colspan, rowspan) = {
566 let cell = cell.borrow();
567 (cell.colspan, cell.rowspan)
568 };
569
570 if self.incoming_rowspans.len() < current_coords.x + colspan {
571 self.incoming_rowspans
572 .resize(current_coords.x + colspan, 0isize);
573 }
574
575 debug_assert_eq!(
576 self.incoming_rowspans[current_coords.x], 0,
577 "Added a cell in a position that also had an incoming rowspan!"
578 );
579
580 let outgoing_rowspan = rowspan as isize - 1;
582 self.table.push_new_slot_to_last_row(TableSlot::Cell(cell));
583 self.incoming_rowspans[current_coords.x] = outgoing_rowspan;
584
585 for colspan_offset in 1..colspan {
587 let current_x_plus_colspan_offset = current_coords.x + colspan_offset;
588 let new_offset = TableSlotOffset::new(colspan_offset, 0);
589 let incoming_rowspan = &mut self.incoming_rowspans[current_x_plus_colspan_offset];
590 let new_slot = if *incoming_rowspan == 0 {
591 *incoming_rowspan = outgoing_rowspan;
592 TableSlot::new_spanned(new_offset)
593 } else {
594 if *incoming_rowspan > 0 {
607 *incoming_rowspan = std::cmp::max(*incoming_rowspan - 1, outgoing_rowspan);
608 }
609
610 let coords_of_spanned_cell =
612 TableSlotCoordinates::new(current_x_plus_colspan_offset, current_coords.y);
613 let mut incoming_slot = self
614 .create_spanned_slot_based_on_cell_above(coords_of_spanned_cell)
615 .expect(
616 "Nonzero incoming rowspan cannot occur without a cell spanning this slot",
617 );
618 incoming_slot.push_spanned(new_offset);
619 incoming_slot
620 };
621 self.table.push_new_slot_to_last_row(new_slot);
622 }
623
624 debug_assert_eq!(
625 Some(TableSlotCoordinates::new(
626 current_coords.x + colspan,
627 current_coords.y
628 )),
629 self.current_coords(),
630 "Must have produced `colspan` slot entries!"
631 );
632 self.create_slots_for_cells_above_with_rowspan(true);
633 }
634}
635
636pub(crate) struct TableBuilderTraversal<'style, 'dom> {
637 context: &'style LayoutContext<'style>,
638 info: &'style NodeAndStyleInfo<'dom>,
639
640 current_propagated_data: PropagatedBoxTreeData,
643
644 builder: TableBuilder,
647
648 current_anonymous_row_content: Vec<AnonymousTableContent<'dom>>,
649
650 current_row_group_index: Option<usize>,
652}
653
654impl<'style, 'dom> TableBuilderTraversal<'style, 'dom> {
655 pub(crate) fn new(
656 context: &'style LayoutContext<'style>,
657 info: &'style NodeAndStyleInfo<'dom>,
658 grid_style: Arc<ComputedValues>,
659 propagated_data: PropagatedBoxTreeData,
660 ) -> Self {
661 TableBuilderTraversal {
662 context,
663 info,
664 current_propagated_data: propagated_data,
665 builder: TableBuilder::new(
666 info.style.clone(),
667 grid_style,
668 info.into(),
669 propagated_data.allow_percentage_column_in_tables,
670 ),
671 current_anonymous_row_content: Vec::new(),
672 current_row_group_index: None,
673 }
674 }
675
676 pub(crate) fn finish(mut self) -> Table {
677 self.finish_anonymous_row_if_needed();
678 self.builder.finish()
679 }
680
681 fn finish_anonymous_row_if_needed(&mut self) {
682 if AnonymousTableContent::contents_are_whitespace_only(&self.current_anonymous_row_content)
683 {
684 self.current_anonymous_row_content.clear();
685 return;
686 }
687
688 let row_content = std::mem::take(&mut self.current_anonymous_row_content);
689 let anonymous_info = self
690 .info
691 .with_pseudo_element(self.context, PseudoElement::ServoAnonymousTableRow)
692 .expect("Should never fail to create anonymous row info.");
693 let mut row_builder =
694 TableRowBuilder::new(self, &anonymous_info, self.current_propagated_data);
695
696 for cell_content in row_content {
697 match cell_content {
698 AnonymousTableContent::Element {
699 info,
700 display,
701 contents,
702 box_slot,
703 } => {
704 row_builder.handle_element(&info, display, contents, box_slot);
705 },
706 AnonymousTableContent::Text(info, text) => {
707 row_builder.handle_text(&info, text);
708 },
709 }
710 }
711
712 row_builder.finish();
713
714 let style = anonymous_info.style.clone();
715 let table_row = ArcRefCell::new(TableTrack {
716 base: LayoutBoxBase::new((&anonymous_info).into(), style.clone()),
717 group_index: self.current_row_group_index,
718 is_anonymous: true,
719 shared_background_style: SharedStyle::new(style),
720 });
721 self.push_table_row(table_row.clone());
722
723 anonymous_info
724 .node
725 .box_slot()
726 .set(LayoutBox::TableLevelBox(TableLevelBox::Track(table_row)))
727 }
728
729 fn push_table_row(&mut self, table_track: ArcRefCell<TableTrack>) {
730 self.builder.table.rows.push(table_track);
731
732 let last_row = self.builder.table.rows.len();
733 if let Some(index) = self.current_row_group_index {
734 let row_group = &mut self.builder.table.row_groups[index];
735 row_group.borrow_mut().track_range.end = last_row;
736 }
737 }
738}
739
740impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
741 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
742 self.current_anonymous_row_content
743 .push(AnonymousTableContent::Text(info.clone(), text));
744 }
745
746 fn handle_element(
748 &mut self,
749 info: &NodeAndStyleInfo<'dom>,
750 display: DisplayGeneratingBox,
751 contents: Contents,
752 box_slot: BoxSlot<'dom>,
753 ) {
754 match display {
755 DisplayGeneratingBox::LayoutInternal(internal) => match internal {
756 DisplayLayoutInternal::TableRowGroup |
757 DisplayLayoutInternal::TableFooterGroup |
758 DisplayLayoutInternal::TableHeaderGroup => {
759 self.finish_anonymous_row_if_needed();
760 self.builder.incoming_rowspans.clear();
761
762 let next_row_index = self.builder.table.rows.len();
763 let row_group = ArcRefCell::new(TableTrackGroup {
764 base: LayoutBoxBase::new(info.into(), info.style.clone()),
765 group_type: internal.into(),
766 track_range: next_row_index..next_row_index,
767 shared_background_style: SharedStyle::new(info.style.clone()),
768 });
769 self.builder.table.row_groups.push(row_group.clone());
770
771 let new_row_group_index = self.builder.table.row_groups.len() - 1;
772 self.current_row_group_index = Some(new_row_group_index);
773
774 let Contents::NonReplaced(non_replaced_contents) = contents else {
775 unreachable!("Replaced should not have a LayoutInternal display type.");
776 };
777 non_replaced_contents.traverse(self.context, info, self);
778 self.finish_anonymous_row_if_needed();
779
780 self.current_row_group_index = None;
781 self.builder.incoming_rowspans.clear();
782
783 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
784 row_group,
785 )));
786 },
787 DisplayLayoutInternal::TableRow => {
788 self.finish_anonymous_row_if_needed();
789
790 let context = self.context;
791 let mut row_builder =
792 TableRowBuilder::new(self, info, self.current_propagated_data);
793
794 let Contents::NonReplaced(non_replaced_contents) = contents else {
795 unreachable!("Replaced should not have a LayoutInternal display type.");
796 };
797 non_replaced_contents.traverse(context, info, &mut row_builder);
798 row_builder.finish();
799
800 let row = ArcRefCell::new(TableTrack {
801 base: LayoutBoxBase::new(info.into(), info.style.clone()),
802 group_index: self.current_row_group_index,
803 is_anonymous: false,
804 shared_background_style: SharedStyle::new(info.style.clone()),
805 });
806 self.push_table_row(row.clone());
807 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(row)));
808 },
809 DisplayLayoutInternal::TableColumn => {
810 let old_box = box_slot.take_layout_box_if_undamaged(info.damage);
811 let old_column = old_box.and_then(|layout_box| match layout_box {
812 LayoutBox::TableLevelBox(TableLevelBox::Track(column)) => Some(column),
813 _ => None,
814 });
815 let column = add_column(
816 &mut self.builder.table.columns,
817 info,
818 None, false, old_column,
821 );
822 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(column)));
823 },
824 DisplayLayoutInternal::TableColumnGroup => {
825 let column_group_index = self.builder.table.column_groups.len();
826 let mut column_group_builder = TableColumnGroupBuilder {
827 column_group_index,
828 columns: Vec::new(),
829 };
830
831 let Contents::NonReplaced(non_replaced_contents) = contents else {
832 unreachable!("Replaced should not have a LayoutInternal display type.");
833 };
834 non_replaced_contents.traverse(self.context, info, &mut column_group_builder);
835
836 let first_column = self.builder.table.columns.len();
837 if column_group_builder.columns.is_empty() {
838 add_column(
839 &mut self.builder.table.columns,
840 info,
841 Some(column_group_index),
842 true, None,
844 );
845 } else {
846 self.builder
847 .table
848 .columns
849 .extend(column_group_builder.columns);
850 }
851
852 let column_group = ArcRefCell::new(TableTrackGroup {
853 base: LayoutBoxBase::new(info.into(), info.style.clone()),
854 group_type: internal.into(),
855 track_range: first_column..self.builder.table.columns.len(),
856 shared_background_style: SharedStyle::new(info.style.clone()),
857 });
858 self.builder.table.column_groups.push(column_group.clone());
859 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
860 column_group,
861 )));
862 },
863 DisplayLayoutInternal::TableCaption => {
864 let Contents::NonReplaced(non_replaced_contents) = contents else {
865 unreachable!("Replaced should not have a LayoutInternal display type.");
866 };
867
868 let old_box = box_slot.take_layout_box_if_undamaged(info.damage);
869 let old_caption = old_box.and_then(|layout_box| match layout_box {
870 LayoutBox::TableLevelBox(TableLevelBox::Caption(caption)) => Some(caption),
871 _ => None,
872 });
873
874 let caption = old_caption.unwrap_or_else(|| {
875 let contents = IndependentFormattingContextContents::Flow(
876 BlockFormattingContext::construct(
877 self.context,
878 info,
879 non_replaced_contents,
880 self.current_propagated_data,
881 false, ),
883 );
884 let base = LayoutBoxBase::new(info.into(), info.style.clone());
885 ArcRefCell::new(TableCaption {
886 context: IndependentFormattingContext::new(base, contents),
887 })
888 });
889
890 self.builder.table.captions.push(caption.clone());
891 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Caption(caption)));
892 },
893 DisplayLayoutInternal::TableCell => {
894 self.current_anonymous_row_content
895 .push(AnonymousTableContent::Element {
896 info: info.clone(),
897 display,
898 contents,
899 box_slot,
900 });
901 },
902 },
903 _ => {
904 self.current_anonymous_row_content
905 .push(AnonymousTableContent::Element {
906 info: info.clone(),
907 display,
908 contents,
909 box_slot,
910 });
911 },
912 }
913 }
914}
915
916struct TableRowBuilder<'style, 'builder, 'dom, 'a> {
917 table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
918
919 info: &'a NodeAndStyleInfo<'dom>,
922
923 current_anonymous_cell_content: Vec<AnonymousTableContent<'dom>>,
924
925 propagated_data: PropagatedBoxTreeData,
927}
928
929impl<'style, 'builder, 'dom, 'a> TableRowBuilder<'style, 'builder, 'dom, 'a> {
930 fn new(
931 table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
932 info: &'a NodeAndStyleInfo<'dom>,
933 propagated_data: PropagatedBoxTreeData,
934 ) -> Self {
935 table_traversal.builder.start_row();
936
937 TableRowBuilder {
938 table_traversal,
939 info,
940 current_anonymous_cell_content: Vec::new(),
941 propagated_data,
942 }
943 }
944
945 fn finish(mut self) {
946 self.finish_current_anonymous_cell_if_needed();
947 self.table_traversal.builder.end_row();
948 }
949
950 fn finish_current_anonymous_cell_if_needed(&mut self) {
951 if AnonymousTableContent::contents_are_whitespace_only(&self.current_anonymous_cell_content)
952 {
953 self.current_anonymous_cell_content.clear();
954 return;
955 }
956
957 let context = self.table_traversal.context;
958 let anonymous_info = self
959 .info
960 .with_pseudo_element(context, PseudoElement::ServoAnonymousTableCell)
961 .expect("Should never fail to create anonymous table cell info");
962 let propagated_data = self.propagated_data.disallowing_percentage_table_columns();
963 let mut builder = BlockContainerBuilder::new(context, &anonymous_info, propagated_data);
964
965 for cell_content in self.current_anonymous_cell_content.drain(..) {
966 match cell_content {
967 AnonymousTableContent::Element {
968 info,
969 display,
970 contents,
971 box_slot,
972 } => {
973 builder.handle_element(&info, display, contents, box_slot);
974 },
975 AnonymousTableContent::Text(info, text) => {
976 builder.handle_text(&info, text);
977 },
978 }
979 }
980
981 let block_container = builder.finish();
982 let new_table_cell = ArcRefCell::new(TableSlotCell {
983 base: LayoutBoxBase::new(BaseFragmentInfo::anonymous(), anonymous_info.style),
984 contents: BlockFormattingContext::from_block_container(block_container),
985 colspan: 1,
986 rowspan: 1,
987 });
988 self.table_traversal
989 .builder
990 .add_cell(new_table_cell.clone());
991
992 anonymous_info
993 .node
994 .box_slot()
995 .set(LayoutBox::TableLevelBox(TableLevelBox::Cell(
996 new_table_cell,
997 )));
998 }
999}
1000
1001impl<'dom> TraversalHandler<'dom> for TableRowBuilder<'_, '_, 'dom, '_> {
1002 fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
1003 self.current_anonymous_cell_content
1004 .push(AnonymousTableContent::Text(info.clone(), text));
1005 }
1006
1007 fn handle_element(
1009 &mut self,
1010 info: &NodeAndStyleInfo<'dom>,
1011 display: DisplayGeneratingBox,
1012 contents: Contents,
1013 box_slot: BoxSlot<'dom>,
1014 ) {
1015 #[allow(clippy::collapsible_match)] match display {
1017 DisplayGeneratingBox::LayoutInternal(internal) => match internal {
1018 DisplayLayoutInternal::TableCell => {
1019 self.finish_current_anonymous_cell_if_needed();
1020
1021 let old_box = box_slot.take_layout_box_if_undamaged(info.damage);
1022 let old_cell = old_box.and_then(|layout_box| match layout_box {
1023 LayoutBox::TableLevelBox(TableLevelBox::Cell(cell)) => Some(cell),
1024 _ => None,
1025 });
1026
1027 let cell = old_cell.unwrap_or_else(|| {
1028 let (rowspan, colspan) = if info.pseudo_element_chain().is_empty() {
1031 let rowspan = info.node.get_rowspan().unwrap_or(1) as usize;
1032 let colspan = info.node.get_colspan().unwrap_or(1) as usize;
1033
1034 assert!((1..=1000).contains(&colspan));
1037 assert!((0..=65534).contains(&rowspan));
1038
1039 (rowspan, colspan)
1040 } else {
1041 (1, 1)
1042 };
1043
1044 let propagated_data =
1045 self.propagated_data.disallowing_percentage_table_columns();
1046 let Contents::NonReplaced(non_replaced_contents) = contents else {
1047 unreachable!("Replaced should not have a LayoutInternal display type.");
1048 };
1049
1050 let contents = BlockFormattingContext::construct(
1051 self.table_traversal.context,
1052 info,
1053 non_replaced_contents,
1054 propagated_data,
1055 false, );
1057
1058 ArcRefCell::new(TableSlotCell {
1059 base: LayoutBoxBase::new(info.into(), info.style.clone()),
1060 contents,
1061 colspan,
1062 rowspan,
1063 })
1064 });
1065
1066 self.table_traversal.builder.add_cell(cell.clone());
1067 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Cell(cell)));
1068 },
1069 _ => {
1070 self.current_anonymous_cell_content
1072 .push(AnonymousTableContent::Element {
1073 info: info.clone(),
1074 display,
1075 contents,
1076 box_slot,
1077 });
1078 },
1079 },
1080 _ => {
1081 self.current_anonymous_cell_content
1082 .push(AnonymousTableContent::Element {
1083 info: info.clone(),
1084 display,
1085 contents,
1086 box_slot,
1087 });
1088 },
1089 }
1090 }
1091}
1092
1093struct TableColumnGroupBuilder {
1094 column_group_index: usize,
1095 columns: Vec<ArcRefCell<TableTrack>>,
1096}
1097
1098impl<'dom> TraversalHandler<'dom> for TableColumnGroupBuilder {
1099 fn handle_text(&mut self, _info: &NodeAndStyleInfo<'dom>, _text: Cow<'dom, str>) {}
1100 fn handle_element(
1101 &mut self,
1102 info: &NodeAndStyleInfo<'dom>,
1103 display: DisplayGeneratingBox,
1104 _contents: Contents,
1105 box_slot: BoxSlot<'dom>,
1106 ) {
1107 if !matches!(
1108 display,
1109 DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableColumn)
1110 ) {
1111 ::std::mem::forget(box_slot);
1114 return;
1115 }
1116 let old_box = box_slot.take_layout_box_if_undamaged(info.damage);
1117 let old_column = old_box.and_then(|layout_box| match layout_box {
1118 LayoutBox::TableLevelBox(TableLevelBox::Track(column)) => Some(column),
1119 _ => None,
1120 });
1121 let column = add_column(
1122 &mut self.columns,
1123 info,
1124 Some(self.column_group_index),
1125 false, old_column,
1127 );
1128 box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(column)));
1129 }
1130}
1131
1132impl From<DisplayLayoutInternal> for TableTrackGroupType {
1133 fn from(value: DisplayLayoutInternal) -> Self {
1134 match value {
1135 DisplayLayoutInternal::TableColumnGroup => TableTrackGroupType::ColumnGroup,
1136 DisplayLayoutInternal::TableFooterGroup => TableTrackGroupType::FooterGroup,
1137 DisplayLayoutInternal::TableHeaderGroup => TableTrackGroupType::HeaderGroup,
1138 DisplayLayoutInternal::TableRowGroup => TableTrackGroupType::RowGroup,
1139 _ => unreachable!(),
1140 }
1141 }
1142}
1143
1144fn add_column(
1145 collection: &mut Vec<ArcRefCell<TableTrack>>,
1146 column_info: &NodeAndStyleInfo,
1147 group_index: Option<usize>,
1148 is_anonymous: bool,
1149 old_column: Option<ArcRefCell<TableTrack>>,
1150) -> ArcRefCell<TableTrack> {
1151 let span = if column_info.pseudo_element_chain().is_empty() {
1152 column_info.node.get_span().unwrap_or(1)
1153 } else {
1154 1
1155 };
1156
1157 assert!((1..=1000).contains(&span));
1159
1160 let column = old_column.unwrap_or_else(|| {
1161 ArcRefCell::new(TableTrack {
1162 base: LayoutBoxBase::new(column_info.into(), column_info.style.clone()),
1163 group_index,
1164 is_anonymous,
1165 shared_background_style: SharedStyle::new(column_info.style.clone()),
1166 })
1167 });
1168 collection.extend(repeat_n(column.clone(), span as usize));
1169 column
1170}