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