Skip to main content

layout/table/
construct.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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/// A reference to a slot and its coordinates in the table
36#[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    // If all contents are whitespace only, it removes them except for unclosed
74    // EnterDisplayContents, and returns true. Otherwise, it returns false.
75    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                    // This only happens if there was whitespace between our internal table elements.
135                    // We only collect that whitespace in case we need to re-emit trailing whitespace
136                    // after we've added our anonymous table.
137                },
138                // Since the table builder skips text, we don't have to handle `display: contents` there.
139                // But we need to handle it for the parent builder, because it may contain trailing
140                // whitespace which won't be placed inside the table.
141                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    /// Push a new slot into the last row of this table.
161    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    /// Find [`ResolvedSlotAndLocation`] of all the slots that cover the slot at the given
174    /// coordinates. This recursively resolves all of the [`TableSlotCell`]s that cover
175    /// the target and returns a [`ResolvedSlotAndLocation`] for each of them. If there is
176    /// no slot at the given coordinates or that slot is an empty space, an empty vector
177    /// is returned.
178    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    /// Merge a TableSlot::Spanned(x, y) with this (only for model errors)
202    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    /// The table that we are building.
219    table: Table,
220
221    /// An incoming rowspan is a value indicating that a cell in a row above the current row,
222    /// had a rowspan value other than 1. The values in this array indicate how many more
223    /// rows the cell should span. For example, a value of 0 at an index before `current_x()`
224    /// indicates that the cell on that column will not span into the next row, and at an index
225    /// after `current_x()` it indicates that the cell will not span into the current row.
226    /// A negative value means that the cell will span all remaining rows in the row group.
227    ///
228    /// As each column in a row is processed, the values in this vector are updated for the
229    /// next row.
230    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, /* percentage_columns_allowed_for_inline_content_sizes */
259        )
260    }
261
262    pub fn last_row_index_in_row_group_at_row_n(&self, n: usize) -> usize {
263        // TODO: This is just a linear search, because the idea is that there are
264        // generally less than or equal to three row groups, but if we notice a lot
265        // of web content with more, we can consider a binary search here.
266        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    /// Do <https://drafts.csswg.org/css-tables/#missing-cells-fixup> which ensures
284    /// that every row has the same number of cells.
285    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    /// It's possible to define more table columns via `<colgroup>` and `<col>` elements
292    /// than actually exist in the table. In that case, increase the size of the table.
293    ///
294    /// However, if the table has no row nor row group, remove the extra columns instead.
295    /// This matches WebKit, and some tests require it, but Gecko and Blink don't do it.
296    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    /// Reorder the first `<thead>` and `<tbody>` to be the first and last row groups respectively.
306    /// This requires fixing up all row group indices.
307    /// See <https://drafts.csswg.org/css-tables/#table-header-group> and
308    /// <https://drafts.csswg.org/css-tables/#table-footer-group>.
309    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            // We may have moved a `<thead>` which means the original index we
331            // we found for this this <tfoot>` also needs to be updated!
332            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        // Now update all track group ranges.
341        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            // Finish any row group that is currently being processed.
349            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            // Start processing this new row group and update its starting index.
357            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        // Finish the last row group.
367        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        // Move the group itself.
377        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            // Move the slots associated with the moved group.
394            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            // Move the rows associated with the moved group.
402            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            // Do this now, rather than after possibly moving a `<tfoot>` row group to the end,
410            // because moving row groups depends on an accurate `track_range` in every group.
411            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        // Move the group itself.
419        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            // Move the slots associated with the moved group.
441            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            // Move the rows associated with the moved group.
449            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    /// Turn all rowspan=0 rows into the real value to avoid having to make the calculation
461    /// continually during layout. In addition, make sure that there are no rowspans that extend
462    /// past the end of their row group.
463    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        // TODO: We need to insert a cell for any leftover non-table-like
506        // content in the TableRowBuilder.
507
508        // Truncate entries that are zero at the end of [`Self::incoming_rowspans`]. This
509        // prevents padding the table with empty cells when it isn't necessary.
510        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    /// Create a [`TableSlot::Spanned`] for the target cell at the given coordinates. If
525    /// no slots cover the target, then this returns [`None`]. Note: This does not handle
526    /// slots that cover the target using `colspan`, but instead only considers slots that
527    /// cover this slot via `rowspan`. `colspan` should be handled by appending to the
528    /// return value of this function.
529    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    /// When not in the process of filling a cell, make sure any incoming rowspans are
551    /// filled so that the next specified cell comes after them. Should have been called before
552    /// [`Self::add_cell`]
553    ///
554    /// if `stop_at_cell_opportunity` is set, this will stop at the first slot with
555    /// `incoming_rowspans` equal to zero. If not, it will insert [`TableSlot::Empty`] and
556    /// continue to look for more incoming rowspans (which should only be done once we're
557    /// finished processing the cells in a row, and after calling truncating cells with
558    /// remaining rowspan from the end of `incoming_rowspans`.
559    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            // This column has no incoming rowspanned cells and `stop_at_zero` is true, so
565            // we should stop to process new cells defined in the current row.
566            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    /// <https://html.spec.whatwg.org/multipage/#algorithm-for-processing-rows>
587    /// Push a single cell onto the slot map, handling any colspans it may have, and
588    /// setting up the outgoing rowspans.
589    pub fn add_cell(&mut self, cell: ArcRefCell<TableSlotCell>) {
590        // Make sure the incoming_rowspans table is large enough
591        // because we will be writing to it.
592        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        // If `rowspan` is zero, this is automatically negative and will stay negative.
612        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        // Draw colspanned cells
617        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                // This means we have a table model error.
626
627                // if `incoming_rowspan` is greater than zero, a cell from above is spanning
628                // into our row, colliding with the cells we are creating via colspan. In
629                // that case, set the incoming rowspan to the highest of two possible
630                // outgoing rowspan values (the incoming rowspan minus one, OR this cell's
631                // outgoing rowspan).  `spanned_slot()`` will handle filtering out
632                // inapplicable spans when it needs to.
633                //
634                // If the `incoming_rowspan` is negative we are in `rowspan=0` mode, (i.e.
635                // rowspan=infinity), so we don't have to worry about the current cell
636                // making it larger. In that case, don't change the rowspan.
637                if *incoming_rowspan > 0 {
638                    *incoming_rowspan = std::cmp::max(*incoming_rowspan - 1, outgoing_rowspan);
639                }
640
641                // This code creates a new slot in the case that there is a table model error.
642                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    /// The value of the [`PropagatedBoxTreeData`] to use, either for the row group
672    /// if processing one or for the table itself if outside a row group.
673    current_propagated_data: PropagatedBoxTreeData,
674
675    /// The [`TableBuilder`] for this [`TableBuilderTraversal`]. This is separated
676    /// into another struct so that we can write unit tests against the builder.
677    builder: TableBuilder,
678
679    current_anonymous_row_content: Vec<AnonymousTableContent<'dom>>,
680
681    /// The index of the current row group, if there is one.
682    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    /// <https://html.spec.whatwg.org/multipage/#forming-a-table>
794    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,  /* group_index */
870                        false, /* is_anonymous */
871                        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, /* is_anonymous */
894                            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, /* is_list_item */
932                            ),
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        // Row groups are only opened from TableBuilderTraversal, never nested, so current_row_group_index is always None here.
985        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    /// The [`NodeAndStyleInfo`] of this table row, which we use to
1127    /// construct anonymous table cells.
1128    info: &'a NodeAndStyleInfo<'dom>,
1129
1130    current_anonymous_cell_content: Vec<AnonymousTableContent<'dom>>,
1131
1132    /// The [`PropagatedBoxTreeData`] to use for all children of this row.
1133    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    /// <https://html.spec.whatwg.org/multipage/#algorithm-for-processing-rows>
1238    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)] //// TODO: Remove once the other cases are handled
1246        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                        // This value will already have filtered out rowspan=0
1259                        // in quirks mode, so we don't have to worry about that.
1260                        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                            // The HTML specification clamps value of `rowspan` to [0, 65534] and
1265                            // `colspan` to [1, 1000].
1266                            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, /* is_list_item */
1286                        );
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                    //// TODO: Properly handle other table-like elements in the middle of a row.
1304                    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            // The BoxSlot destructor will check to ensure that it isn't empty but in this case, the
1347            // DOM node doesn't produce any box, so explicitly skip the destructor here.
1348            ::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, /* is_anonymous */
1361            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    // The HTML specification clamps value of `span` for `<col>` to [1, 1000].
1393    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}