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