1use core::cmp::Ordering;
6use std::mem;
7use std::ops::Range;
8
9use app_units::Au;
10use atomic_refcell::AtomicRef;
11use log::warn;
12use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
13use servo_arc::Arc;
14use style::Zero;
15use style::computed_values::border_collapse::T as BorderCollapse;
16use style::computed_values::box_sizing::T as BoxSizing;
17use style::computed_values::caption_side::T as CaptionSide;
18use style::computed_values::empty_cells::T as EmptyCells;
19use style::computed_values::position::T as Position;
20use style::computed_values::table_layout::T as TableLayoutMode;
21use style::computed_values::visibility::T as Visibility;
22use style::properties::ComputedValues;
23use style::values::computed::{
24 BorderStyle, LengthPercentage as ComputedLengthPercentage, Percentage,
25};
26use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, VerticalAlignKeyword};
27
28use super::{
29 ArcRefCell, CollapsedBorder, CollapsedBorderLine, SpecificTableGridInfo, Table, TableCaption,
30 TableLayoutStyle, TableSlot, TableSlotCell, TableSlotCoordinates, TableTrack, TableTrackGroup,
31};
32use crate::context::LayoutContext;
33use crate::formatting_contexts::Baselines;
34use crate::fragment_tree::{
35 BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags,
36 PositioningFragment, SpecificLayoutInfo,
37};
38use crate::geom::{
39 LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
40 PhysicalSides, PhysicalVec, ToLogical, ToLogicalWithContainingBlock,
41};
42use crate::layout_box_base::CacheableLayoutResult;
43use crate::positioned::{PositioningContext, PositioningContextLength, relative_adjustement};
44use crate::sizing::{
45 ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, Size, SizeConstraint,
46};
47use crate::style_ext::{
48 BorderStyleColor, Clamp, ComputedValuesExt, LayoutStyle, PaddingBorderMargin,
49};
50use crate::{
51 ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock, WritingMode,
52};
53
54struct CellLayout {
58 layout: CacheableLayoutResult,
59 padding: LogicalSides<Au>,
60 border: LogicalSides<Au>,
61 positioning_context: PositioningContext,
62}
63
64impl CellLayout {
65 fn ascent(&self) -> Au {
66 self.layout
67 .baselines
68 .first
69 .unwrap_or(self.layout.content_block_size)
70 }
71
72 fn outer_block_size(&self) -> Au {
74 self.layout.content_block_size + self.border.block_sum() + self.padding.block_sum()
75 }
76
77 fn is_empty(&self) -> bool {
80 self.layout.fragments.is_empty()
81 }
82
83 fn is_empty_for_empty_cells(&self) -> bool {
85 self.layout
86 .fragments
87 .iter()
88 .all(|fragment| matches!(fragment, Fragment::AbsoluteOrFixedPositioned(_)))
89 }
90}
91
92#[derive(Clone, Debug, Default)]
94struct RowLayout {
95 constrained: bool,
96 has_cell_with_span_greater_than_one: bool,
97 percent: Percentage,
98}
99
100#[derive(Clone, Debug, Default)]
102struct ColumnLayout {
103 constrained: bool,
104 has_originating_cells: bool,
105 content_sizes: ContentSizes,
106 percentage: Option<Percentage>,
107}
108
109fn max_two_optional_percentages(
110 a: Option<Percentage>,
111 b: Option<Percentage>,
112) -> Option<Percentage> {
113 match (a, b) {
114 (Some(a), Some(b)) => Some(Percentage(a.0.max(b.0))),
115 _ => a.or(b),
116 }
117}
118
119impl ColumnLayout {
120 fn incorporate_cell_measure(&mut self, cell_measure: &CellOrTrackMeasure) {
121 self.content_sizes.max_assign(cell_measure.content_sizes);
122 self.percentage = max_two_optional_percentages(self.percentage, cell_measure.percentage);
123 }
124}
125
126impl CollapsedBorder {
127 fn new(style_color: BorderStyleColor, width: Au) -> Self {
128 Self { style_color, width }
129 }
130
131 fn from_layout_style(
132 layout_style: &LayoutStyle,
133 writing_mode: WritingMode,
134 ) -> LogicalSides<Self> {
135 let border_style_color = layout_style.style().border_style_color(writing_mode);
136 let border_width = layout_style.border_width(writing_mode);
137 LogicalSides {
138 inline_start: Self::new(border_style_color.inline_start, border_width.inline_start),
139 inline_end: Self::new(border_style_color.inline_end, border_width.inline_end),
140 block_start: Self::new(border_style_color.block_start, border_width.block_start),
141 block_end: Self::new(border_style_color.block_end, border_width.block_end),
142 }
143 }
144
145 fn max_assign(&mut self, other: &Self) {
146 if *self < *other {
147 *self = other.clone();
148 }
149 }
150
151 fn max_assign_to_slice(&self, slice: &mut [CollapsedBorder]) {
152 for collapsed_border in slice {
153 collapsed_border.max_assign(self)
154 }
155 }
156
157 fn hide(&mut self) {
158 self.style_color = BorderStyleColor::hidden();
159 self.width = Au::zero();
160 }
161}
162
163impl PartialOrd for CollapsedBorder {
170 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
171 let is_hidden = |border: &Self| border.style_color.style == BorderStyle::Hidden;
172 let style_specificity = |border: &Self| match border.style_color.style {
173 BorderStyle::None => 0,
174 BorderStyle::Inset => 1,
175 BorderStyle::Groove => 2,
176 BorderStyle::Outset => 3,
177 BorderStyle::Ridge => 4,
178 BorderStyle::Dotted => 5,
179 BorderStyle::Dashed => 6,
180 BorderStyle::Solid => 7,
181 BorderStyle::Double => 8,
182 BorderStyle::Hidden => 9,
183 };
184 let candidate = (is_hidden(self).cmp(&is_hidden(other)))
185 .then_with(|| self.width.cmp(&other.width))
186 .then_with(|| style_specificity(self).cmp(&style_specificity(other)));
187 if !candidate.is_eq() || self.style_color.color == other.style_color.color {
188 Some(candidate)
189 } else {
190 None
191 }
192 }
193}
194
195impl Eq for CollapsedBorder {}
196
197type CollapsedBorders = LogicalVec2<Vec<CollapsedBorderLine>>;
198
199pub(crate) struct TableLayout<'a> {
203 table: &'a Table,
204 pbm: PaddingBorderMargin,
205 rows: Vec<RowLayout>,
206 columns: Vec<ColumnLayout>,
207 cell_measures: Vec<Vec<LogicalVec2<CellOrTrackMeasure>>>,
208 table_width: Au,
211 assignable_width: Au,
214 final_table_height: Au,
215 distributed_column_widths: Vec<Au>,
216 row_sizes: Vec<Au>,
217 row_baselines: Vec<Au>,
219 cells_laid_out: Vec<Vec<Option<CellLayout>>>,
220 basis_for_cell_padding_percentage: Au,
221 collapsed_borders: Option<CollapsedBorders>,
223 is_in_fixed_mode: bool,
224}
225
226#[derive(Clone, Debug)]
227struct CellOrTrackMeasure {
228 content_sizes: ContentSizes,
229 percentage: Option<Percentage>,
230}
231
232impl Zero for CellOrTrackMeasure {
233 fn zero() -> Self {
234 Self {
235 content_sizes: ContentSizes::zero(),
236 percentage: None,
237 }
238 }
239
240 fn is_zero(&self) -> bool {
241 self.content_sizes.is_zero() && self.percentage.is_none()
242 }
243}
244
245impl<'a> TableLayout<'a> {
246 fn new(table: &'a Table) -> TableLayout<'a> {
247 let style = &table.style;
250 let is_in_fixed_mode = style.get_table().table_layout == TableLayoutMode::Fixed &&
251 !style.box_size(style.writing_mode).inline.is_initial();
252 Self {
253 table,
254 pbm: PaddingBorderMargin::zero(),
255 rows: Vec::new(),
256 columns: Vec::new(),
257 cell_measures: Vec::new(),
258 table_width: Au::zero(),
259 assignable_width: Au::zero(),
260 final_table_height: Au::zero(),
261 distributed_column_widths: Vec::new(),
262 row_sizes: Vec::new(),
263 row_baselines: Vec::new(),
264 cells_laid_out: Vec::new(),
265 basis_for_cell_padding_percentage: Au::zero(),
266 collapsed_borders: None,
267 is_in_fixed_mode,
268 }
269 }
270
271 pub(crate) fn compute_cell_measures(
274 &mut self,
275 layout_context: &LayoutContext,
276 writing_mode: WritingMode,
277 ) {
278 let row_measures = vec![LogicalVec2::zero(); self.table.size.width];
279 self.cell_measures = vec![row_measures; self.table.size.height];
280
281 for row_index in 0..self.table.size.height {
282 for column_index in 0..self.table.size.width {
283 let cell = match self.table.slots[row_index][column_index] {
284 TableSlot::Cell(ref cell) => cell,
285 _ => continue,
286 }
287 .borrow();
288
289 let layout_style = cell.layout_style();
290 let padding = layout_style
291 .padding(writing_mode)
292 .percentages_relative_to(Au::zero());
293 let border = self
294 .get_collapsed_border_widths_for_area(LogicalSides {
295 inline_start: column_index,
296 inline_end: column_index + cell.colspan,
297 block_start: row_index,
298 block_end: row_index + cell.rowspan,
299 })
300 .unwrap_or_else(|| layout_style.border_width(writing_mode));
301
302 let padding_border_sums = LogicalVec2 {
303 inline: padding.inline_sum() + border.inline_sum(),
304 block: padding.block_sum() + border.block_sum(),
305 };
306
307 let CellOrColumnOuterSizes {
308 preferred: preferred_size,
309 min: min_size,
310 max: max_size,
311 percentage: percentage_size,
312 } = CellOrColumnOuterSizes::new(
313 &cell.base.style,
314 writing_mode,
315 &padding_border_sums,
316 self.is_in_fixed_mode,
317 );
318
319 let inline_measure = if self.is_in_fixed_mode {
324 if row_index > 0 {
325 CellOrTrackMeasure::zero()
326 } else {
327 CellOrTrackMeasure {
328 content_sizes: preferred_size.inline.into(),
329 percentage: percentage_size.inline,
330 }
331 }
332 } else {
333 let inline_content_sizes = cell.inline_content_sizes(layout_context) +
334 padding_border_sums.inline.into();
335 assert!(
336 inline_content_sizes.max_content >= inline_content_sizes.min_content,
337 "the max-content size should never be smaller than the min-content size"
338 );
339
340 let outer_min_content_width = inline_content_sizes
342 .min_content
343 .clamp_between_extremums(min_size.inline, max_size.inline);
344 let outer_max_content_width = if self.columns[column_index].constrained {
345 inline_content_sizes
346 .min_content
347 .max(preferred_size.inline)
348 .clamp_between_extremums(min_size.inline, max_size.inline)
349 } else {
350 inline_content_sizes
351 .max_content
352 .max(preferred_size.inline)
353 .clamp_between_extremums(min_size.inline, max_size.inline)
354 };
355 assert!(outer_min_content_width <= outer_max_content_width);
356
357 CellOrTrackMeasure {
358 content_sizes: ContentSizes {
359 min_content: outer_min_content_width,
360 max_content: outer_max_content_width,
361 },
362 percentage: percentage_size.inline,
363 }
364 };
365
366 let block_measure = CellOrTrackMeasure {
370 content_sizes: preferred_size.block.into(),
371 percentage: percentage_size.block,
372 };
373
374 self.cell_measures[row_index][column_index] = LogicalVec2 {
375 inline: inline_measure,
376 block: block_measure,
377 };
378 }
379 }
380 }
381
382 fn compute_track_constrainedness_and_has_originating_cells(
388 &mut self,
389 writing_mode: WritingMode,
390 ) {
391 self.rows = vec![RowLayout::default(); self.table.size.height];
392 self.columns = vec![ColumnLayout::default(); self.table.size.width];
393
394 let is_length = |size: &Size<ComputedLengthPercentage>| {
395 size.to_numeric().is_some_and(|size| !size.has_percentage())
396 };
397
398 for column_index in 0..self.table.size.width {
399 if let Some(column) = self.table.columns.get(column_index) {
400 let column = column.borrow();
401 if is_length(&column.base.style.box_size(writing_mode).inline) {
402 self.columns[column_index].constrained = true;
403 continue;
404 }
405 if let Some(column_group_index) = column.group_index {
406 let column_group = self.table.column_groups[column_group_index].borrow();
407 if is_length(&column_group.base.style.box_size(writing_mode).inline) {
408 self.columns[column_index].constrained = true;
409 continue;
410 }
411 }
412 }
413 }
414
415 for row_index in 0..self.table.size.height {
416 if let Some(row) = self.table.rows.get(row_index) {
417 let row = row.borrow();
418 if is_length(&row.base.style.box_size(writing_mode).block) {
419 self.rows[row_index].constrained = true;
420 continue;
421 }
422 if let Some(row_group_index) = row.group_index {
423 let row_group = self.table.row_groups[row_group_index].borrow();
424 if is_length(&row_group.base.style.box_size(writing_mode).block) {
425 self.rows[row_index].constrained = true;
426 continue;
427 }
428 }
429 }
430 }
431
432 for column_index in 0..self.table.size.width {
433 for row_index in 0..self.table.size.height {
434 let coords = TableSlotCoordinates::new(column_index, row_index);
435 let cell_constrained = match self.table.resolve_first_cell(coords) {
436 Some(cell) if cell.colspan == 1 => {
437 cell.base.style.box_size(writing_mode).map(is_length)
438 },
439 _ => LogicalVec2::default(),
440 };
441
442 let rowspan_greater_than_1 = match self.table.slots[row_index][column_index] {
443 TableSlot::Cell(ref cell) => cell.borrow().rowspan > 1,
444 _ => false,
445 };
446
447 self.rows[row_index].has_cell_with_span_greater_than_one |= rowspan_greater_than_1;
448 self.rows[row_index].constrained |= cell_constrained.block;
449
450 let has_originating_cell =
451 matches!(self.table.get_slot(coords), Some(TableSlot::Cell(_)));
452 self.columns[column_index].has_originating_cells |= has_originating_cell;
453 self.columns[column_index].constrained |= cell_constrained.inline;
454 }
455 }
456 }
457
458 fn compute_column_measures(&mut self, writing_mode: WritingMode) {
461 let mut colspan_cell_constraints = Vec::new();
492 for column_index in 0..self.table.size.width {
493 let column = &mut self.columns[column_index];
494
495 let column_measure = self.table.get_column_measure_for_column_at_index(
496 writing_mode,
497 column_index,
498 self.is_in_fixed_mode,
499 );
500 column.content_sizes = column_measure.content_sizes;
501 column.percentage = column_measure.percentage;
502
503 for row_index in 0..self.table.size.height {
504 let coords = TableSlotCoordinates::new(column_index, row_index);
505 let cell_measure = &self.cell_measures[row_index][column_index].inline;
506
507 let cell = match self.table.get_slot(coords) {
508 Some(TableSlot::Cell(cell)) => cell,
509 _ => continue,
510 }
511 .borrow();
512
513 if cell.colspan != 1 {
514 colspan_cell_constraints.push(ColspanToDistribute {
515 starting_column: column_index,
516 span: cell.colspan,
517 content_sizes: cell_measure.content_sizes,
518 percentage: cell_measure.percentage,
519 });
520 continue;
521 }
522
523 column.incorporate_cell_measure(cell_measure);
526 }
527 }
528
529 colspan_cell_constraints.sort_by(ColspanToDistribute::comparison_for_sort);
531
532 self.distribute_colspanned_cells_to_columns(colspan_cell_constraints);
534
535 let mut total_intrinsic_percentage_width = 0.;
542 for column in self.columns.iter_mut() {
543 if let Some(ref mut percentage) = column.percentage {
544 let final_intrinsic_percentage_width =
545 percentage.0.min(1. - total_intrinsic_percentage_width);
546 total_intrinsic_percentage_width += final_intrinsic_percentage_width;
547 *percentage = Percentage(final_intrinsic_percentage_width);
548 }
549 }
550 }
551
552 fn distribute_colspanned_cells_to_columns(
553 &mut self,
554 colspan_cell_constraints: Vec<ColspanToDistribute>,
555 ) {
556 for colspan_cell_constraints in colspan_cell_constraints {
557 self.distribute_colspanned_cell_to_columns(colspan_cell_constraints);
558 }
559 }
560
561 fn distribute_colspanned_cell_to_columns(
566 &mut self,
567 colspan_cell_constraints: ColspanToDistribute,
568 ) {
569 let border_spacing = self.table.border_spacing().inline;
570 let column_range = colspan_cell_constraints.range();
571 let column_count = column_range.len();
572 let total_border_spacing =
573 border_spacing.scale_by((colspan_cell_constraints.span - 1) as f32);
574
575 let mut percent_columns_count = 0;
576 let mut columns_percent_sum = 0.;
577 let mut columns_non_percent_max_inline_size_sum = Au::zero();
578 for column in self.columns[column_range.clone()].iter() {
579 if let Some(percentage) = column.percentage {
580 percent_columns_count += 1;
581 columns_percent_sum += percentage.0;
582 } else {
583 columns_non_percent_max_inline_size_sum += column.content_sizes.max_content;
584 }
585 }
586
587 let colspan_percentage = colspan_cell_constraints.percentage.unwrap_or_default();
588 let surplus_percent = colspan_percentage.0 - columns_percent_sum;
589 if surplus_percent > 0. && column_count > percent_columns_count {
590 for column in self.columns[column_range.clone()].iter_mut() {
591 if column.percentage.is_some() {
592 continue;
593 }
594
595 let ratio = if columns_non_percent_max_inline_size_sum.is_zero() {
596 1. / ((column_count - percent_columns_count) as f32)
597 } else {
598 column.content_sizes.max_content.to_f32_px() /
599 columns_non_percent_max_inline_size_sum.to_f32_px()
600 };
601 column.percentage = Some(Percentage(surplus_percent * ratio));
602 }
603 }
604
605 let colspan_cell_min_size = (colspan_cell_constraints.content_sizes.min_content -
606 total_border_spacing)
607 .max(Au::zero());
608 let distributed_minimum = Self::distribute_width_to_columns(
609 colspan_cell_min_size,
610 &self.columns[column_range.clone()],
611 );
612 {
613 let column_span = &mut self.columns[colspan_cell_constraints.range()];
614 for (column, minimum_size) in column_span.iter_mut().zip(distributed_minimum) {
615 column.content_sizes.min_content.max_assign(minimum_size);
616 }
617 }
618
619 let colspan_cell_max_size = (colspan_cell_constraints.content_sizes.max_content -
620 total_border_spacing)
621 .max(Au::zero());
622 let distributed_maximum = Self::distribute_width_to_columns(
623 colspan_cell_max_size,
624 &self.columns[colspan_cell_constraints.range()],
625 );
626 {
627 let column_span = &mut self.columns[colspan_cell_constraints.range()];
628 for (column, maximum_size) in column_span.iter_mut().zip(distributed_maximum) {
629 column
630 .content_sizes
631 .max_content
632 .max_assign(maximum_size.max(column.content_sizes.min_content));
633 }
634 }
635 }
636
637 fn compute_measures(&mut self, layout_context: &LayoutContext, writing_mode: WritingMode) {
638 self.compute_track_constrainedness_and_has_originating_cells(writing_mode);
639 self.compute_cell_measures(layout_context, writing_mode);
640 self.compute_column_measures(writing_mode);
641 }
642
643 fn compute_grid_min_max(&self) -> ContentSizes {
645 let mut largest_percentage_column_max_size = Au::zero();
658 let mut percent_sum = 0.;
659 let mut non_percent_columns_max_sum = Au::zero();
660 let mut grid_min_max = ContentSizes::zero();
661 for column in self.columns.iter() {
662 match column.percentage {
663 Some(percentage) if !percentage.is_zero() => {
664 largest_percentage_column_max_size.max_assign(
665 column
666 .content_sizes
667 .max_content
668 .scale_by(1.0 / percentage.0),
669 );
670 percent_sum += percentage.0;
671 },
672 _ => {
673 non_percent_columns_max_sum += column.content_sizes.max_content;
674 },
675 }
676
677 grid_min_max += column.content_sizes;
678 }
679
680 grid_min_max
681 .max_content
682 .max_assign(largest_percentage_column_max_size);
683
684 if !percent_sum.is_zero() &&
688 self.table
689 .percentage_columns_allowed_for_inline_content_sizes
690 {
691 let total_inline_size =
692 non_percent_columns_max_sum.scale_by(1.0 / (1.0 - percent_sum.min(1.0)));
693 grid_min_max.max_content.max_assign(total_inline_size);
694 }
695
696 assert!(
697 grid_min_max.min_content <= grid_min_max.max_content,
698 "GRIDMAX should never be smaller than GRIDMIN {:?}",
699 grid_min_max
700 );
701
702 let inline_border_spacing = self.table.total_border_spacing().inline;
703 grid_min_max.min_content += inline_border_spacing;
704 grid_min_max.max_content += inline_border_spacing;
705 grid_min_max
706 }
707
708 fn compute_caption_minimum_inline_size(&self, layout_context: &LayoutContext) -> Au {
710 let containing_block = IndefiniteContainingBlock {
711 size: LogicalVec2::default(),
712 writing_mode: self.table.style.writing_mode,
713 };
714 self.table
715 .captions
716 .iter()
717 .map(|caption| {
718 caption
719 .borrow()
720 .context
721 .outer_inline_content_sizes(
722 layout_context,
723 &containing_block,
724 &LogicalVec2::zero(),
725 false, )
727 .sizes
728 .min_content
729 })
730 .max()
731 .unwrap_or_default()
732 }
733
734 fn compute_table_width(&mut self, containing_block_for_children: &ContainingBlock) {
735 self.table_width = containing_block_for_children.size.inline;
740
741 self.assignable_width = self.table_width - self.table.total_border_spacing().inline;
745
746 self.basis_for_cell_padding_percentage =
749 self.table_width - self.table.border_spacing().inline * 2;
750 }
751
752 fn distribute_width_to_columns(target_inline_size: Au, columns: &[ColumnLayout]) -> Vec<Au> {
755 if columns.is_empty() {
758 return Vec::new();
759 }
760
761 let mut min_content_sizing_guesses = Vec::new();
790 let mut min_content_percentage_sizing_guesses = Vec::new();
791 let mut min_content_specified_sizing_guesses = Vec::new();
792 let mut max_content_sizing_guesses = Vec::new();
793
794 for column in columns {
795 let min_content_width = column.content_sizes.min_content;
796 let max_content_width = column.content_sizes.max_content;
797 let constrained = column.constrained;
798
799 let (
800 min_content_percentage_sizing_guess,
801 min_content_specified_sizing_guess,
802 max_content_sizing_guess,
803 ) = if let Some(percentage) = column.percentage {
804 let resolved = target_inline_size.scale_by(percentage.0);
805 let percent_guess = min_content_width.max(resolved);
806 (percent_guess, percent_guess, percent_guess)
807 } else if constrained {
808 (min_content_width, max_content_width, max_content_width)
809 } else {
810 (min_content_width, min_content_width, max_content_width)
811 };
812
813 min_content_sizing_guesses.push(min_content_width);
814 min_content_percentage_sizing_guesses.push(min_content_percentage_sizing_guess);
815 min_content_specified_sizing_guesses.push(min_content_specified_sizing_guess);
816 max_content_sizing_guesses.push(max_content_sizing_guess);
817 }
818
819 fn sum(guesses: &[Au]) -> Au {
827 guesses.iter().fold(Au::zero(), |sum, guess| sum + *guess)
828 }
829
830 let max_content_sizing_sum = sum(&max_content_sizing_guesses);
831 if target_inline_size >= max_content_sizing_sum {
832 Self::distribute_extra_width_to_columns(
833 columns,
834 &mut max_content_sizing_guesses,
835 max_content_sizing_sum,
836 target_inline_size,
837 );
838 return max_content_sizing_guesses;
839 }
840 let min_content_specified_sizing_sum = sum(&min_content_specified_sizing_guesses);
841 if target_inline_size == min_content_specified_sizing_sum {
842 return min_content_specified_sizing_guesses;
843 }
844 let min_content_percentage_sizing_sum = sum(&min_content_percentage_sizing_guesses);
845 if target_inline_size == min_content_percentage_sizing_sum {
846 return min_content_percentage_sizing_guesses;
847 }
848 let min_content_sizes_sum = sum(&min_content_sizing_guesses);
849 if target_inline_size <= min_content_sizes_sum {
850 return min_content_sizing_guesses;
851 }
852
853 let bounds = |sum_a, sum_b| target_inline_size > sum_a && target_inline_size < sum_b;
854
855 let blend = |a: &[Au], sum_a: Au, b: &[Au], sum_b: Au| {
856 let weight_a = (target_inline_size - sum_b).to_f32_px() / (sum_a - sum_b).to_f32_px();
858 let weight_b = 1.0 - weight_a;
859
860 let mut remaining_assignable_width = target_inline_size;
861 let mut widths: Vec<Au> = a
862 .iter()
863 .zip(b.iter())
864 .map(|(guess_a, guess_b)| {
865 let column_width = guess_a.scale_by(weight_a) + guess_b.scale_by(weight_b);
866 let column_width = column_width.min(remaining_assignable_width);
869 remaining_assignable_width -= column_width;
870 column_width
871 })
872 .collect();
873
874 if !remaining_assignable_width.is_zero() {
875 debug_assert!(
879 remaining_assignable_width >= Au::zero(),
880 "Sum of columns shouldn't exceed the assignable table width"
881 );
882 debug_assert!(
883 remaining_assignable_width <= Au::new(widths.len() as i32),
884 "A deviation of more than one Au per column is unlikely to be caused by float imprecision"
885 );
886
887 widths[0] += remaining_assignable_width;
890 }
891
892 debug_assert!(widths.iter().sum::<Au>() == target_inline_size);
893
894 widths
895 };
896
897 if bounds(min_content_sizes_sum, min_content_percentage_sizing_sum) {
898 return blend(
899 &min_content_sizing_guesses,
900 min_content_sizes_sum,
901 &min_content_percentage_sizing_guesses,
902 min_content_percentage_sizing_sum,
903 );
904 }
905
906 if bounds(
907 min_content_percentage_sizing_sum,
908 min_content_specified_sizing_sum,
909 ) {
910 return blend(
911 &min_content_percentage_sizing_guesses,
912 min_content_percentage_sizing_sum,
913 &min_content_specified_sizing_guesses,
914 min_content_specified_sizing_sum,
915 );
916 }
917
918 assert!(bounds(
919 min_content_specified_sizing_sum,
920 max_content_sizing_sum
921 ));
922 blend(
923 &min_content_specified_sizing_guesses,
924 min_content_specified_sizing_sum,
925 &max_content_sizing_guesses,
926 max_content_sizing_sum,
927 )
928 }
929
930 fn distribute_extra_width_to_columns(
933 columns: &[ColumnLayout],
934 column_sizes: &mut [Au],
935 column_sizes_sum: Au,
936 assignable_width: Au,
937 ) {
938 let all_columns = 0..columns.len();
939 let extra_inline_size = assignable_width - column_sizes_sum;
940
941 let has_originating_cells =
942 |column_index: &usize| columns[*column_index].has_originating_cells;
943 let is_constrained = |column_index: &usize| columns[*column_index].constrained;
944 let is_unconstrained = |column_index: &usize| !is_constrained(column_index);
945 let has_percent_greater_than_zero = |column_index: &usize| {
946 columns[*column_index]
947 .percentage
948 .is_some_and(|percentage| percentage.0 > 0.)
949 };
950 let has_percent_zero = |column_index: &usize| !has_percent_greater_than_zero(column_index);
951 let has_max_content =
952 |column_index: &usize| !columns[*column_index].content_sizes.max_content.is_zero();
953
954 let max_content_sum = |column_index: usize| columns[column_index].content_sizes.max_content;
955
956 let unconstrained_max_content_columns = all_columns
962 .clone()
963 .filter(is_unconstrained)
964 .filter(has_originating_cells)
965 .filter(has_percent_zero)
966 .filter(has_max_content);
967 let total_max_content_width = unconstrained_max_content_columns
968 .clone()
969 .map(max_content_sum)
970 .fold(Au::zero(), |a, b| a + b);
971 if !total_max_content_width.is_zero() {
972 for column_index in unconstrained_max_content_columns {
973 column_sizes[column_index] += extra_inline_size.scale_by(
974 columns[column_index].content_sizes.max_content.to_f32_px() /
975 total_max_content_width.to_f32_px(),
976 );
977 }
978 return;
979 }
980
981 let unconstrained_no_percent_columns = all_columns
987 .clone()
988 .filter(is_unconstrained)
989 .filter(has_originating_cells)
990 .filter(has_percent_zero);
991 let total_unconstrained_no_percent = unconstrained_no_percent_columns.clone().count();
992 if total_unconstrained_no_percent > 0 {
993 let extra_space_per_column =
994 extra_inline_size.scale_by(1.0 / total_unconstrained_no_percent as f32);
995 for column_index in unconstrained_no_percent_columns {
996 column_sizes[column_index] += extra_space_per_column;
997 }
998 return;
999 }
1000
1001 let constrained_max_content_columns = all_columns
1007 .clone()
1008 .filter(is_constrained)
1009 .filter(has_originating_cells)
1010 .filter(has_percent_zero)
1011 .filter(has_max_content);
1012 let total_max_content_width = constrained_max_content_columns
1013 .clone()
1014 .map(max_content_sum)
1015 .fold(Au::zero(), |a, b| a + b);
1016 if !total_max_content_width.is_zero() {
1017 for column_index in constrained_max_content_columns {
1018 column_sizes[column_index] += extra_inline_size.scale_by(
1019 columns[column_index].content_sizes.max_content.to_f32_px() /
1020 total_max_content_width.to_f32_px(),
1021 );
1022 }
1023 return;
1024 }
1025
1026 let columns_with_percentage = all_columns.clone().filter(has_percent_greater_than_zero);
1032 let total_percent = columns_with_percentage
1033 .clone()
1034 .map(|column_index| columns[column_index].percentage.unwrap_or_default().0)
1035 .sum::<f32>();
1036 if total_percent > 0. {
1037 for column_index in columns_with_percentage {
1038 let column_percentage = columns[column_index].percentage.unwrap_or_default();
1039 column_sizes[column_index] +=
1040 extra_inline_size.scale_by(column_percentage.0 / total_percent);
1041 }
1042 return;
1043 }
1044
1045 let has_originating_cells_columns = all_columns.clone().filter(has_originating_cells);
1049 let total_has_originating_cells = has_originating_cells_columns.clone().count();
1050 if total_has_originating_cells > 0 {
1051 let extra_space_per_column =
1052 extra_inline_size.scale_by(1.0 / total_has_originating_cells as f32);
1053 for column_index in has_originating_cells_columns {
1054 column_sizes[column_index] += extra_space_per_column;
1055 }
1056 return;
1057 }
1058
1059 let extra_space_for_all_columns = extra_inline_size.scale_by(1.0 / columns.len() as f32);
1062 for guess in column_sizes.iter_mut() {
1063 *guess += extra_space_for_all_columns;
1064 }
1065 }
1066
1067 fn layout_cells_in_row(
1070 &mut self,
1071 layout_context: &LayoutContext,
1072 containing_block_for_table: &ContainingBlock,
1073 ) {
1074 let layout_table_slot = |coordinate: TableSlotCoordinates, slot: &TableSlot| {
1075 let TableSlot::Cell(cell) = slot else {
1076 return None;
1077 };
1078
1079 let cell = cell.borrow();
1080 let area = LogicalSides {
1081 inline_start: coordinate.x,
1082 inline_end: coordinate.x + cell.colspan,
1083 block_start: coordinate.y,
1084 block_end: coordinate.y + cell.rowspan,
1085 };
1086 let layout_style = cell.layout_style();
1087 let border = self
1088 .get_collapsed_border_widths_for_area(area)
1089 .unwrap_or_else(|| {
1090 layout_style.border_width(containing_block_for_table.style.writing_mode)
1091 });
1092 let padding: LogicalSides<Au> = layout_style
1093 .padding(containing_block_for_table.style.writing_mode)
1094 .percentages_relative_to(self.basis_for_cell_padding_percentage);
1095 let inline_border_padding_sum = border.inline_sum() + padding.inline_sum();
1096 let border_spacing_spanned =
1097 self.table.border_spacing().inline * (cell.colspan - 1) as i32;
1098
1099 let mut total_cell_width: Au = (coordinate.x..coordinate.x + cell.colspan)
1100 .map(|column_index| self.distributed_column_widths[column_index])
1101 .sum::<Au>() -
1102 inline_border_padding_sum +
1103 border_spacing_spanned;
1104 total_cell_width = total_cell_width.max(Au::zero());
1105
1106 let containing_block_for_children = ContainingBlock {
1107 size: ContainingBlockSize {
1108 inline: total_cell_width,
1109 block: SizeConstraint::default(),
1110 },
1111 style: &cell.base.style,
1112 };
1113
1114 let mut positioning_context = PositioningContext::default();
1115 let layout = cell.contents.layout(
1116 layout_context,
1117 &mut positioning_context,
1118 &containing_block_for_children,
1119 );
1120
1121 Some(CellLayout {
1122 layout,
1123 padding,
1124 border,
1125 positioning_context,
1126 })
1127 };
1128
1129 self.cells_laid_out = if layout_context.use_rayon {
1130 self.table
1131 .slots
1132 .par_iter()
1133 .enumerate()
1134 .map(|(row_index, row_slots)| {
1135 row_slots
1136 .par_iter()
1137 .enumerate()
1138 .map(|(column_index, slot)| {
1139 layout_table_slot(
1140 TableSlotCoordinates::new(column_index, row_index),
1141 slot,
1142 )
1143 })
1144 .collect()
1145 })
1146 .collect()
1147 } else {
1148 self.table
1149 .slots
1150 .iter()
1151 .enumerate()
1152 .map(|(row_index, row_slots)| {
1153 row_slots
1154 .iter()
1155 .enumerate()
1156 .map(|(column_index, slot)| {
1157 layout_table_slot(
1158 TableSlotCoordinates::new(column_index, row_index),
1159 slot,
1160 )
1161 })
1162 .collect()
1163 })
1164 .collect()
1165 };
1166
1167 for row_index in 0..self.table.size.height {
1170 for column_index in 0..self.table.size.width {
1171 let Some(layout) = &self.cells_laid_out[row_index][column_index] else {
1172 continue;
1173 };
1174
1175 self.cell_measures[row_index][column_index]
1176 .block
1177 .content_sizes
1178 .max_assign(layout.outer_block_size().into());
1179 }
1180 }
1181 }
1182
1183 fn do_first_row_layout(&mut self, writing_mode: WritingMode) -> Vec<Au> {
1186 let mut row_sizes = (0..self.table.size.height)
1187 .map(|row_index| {
1188 let (mut max_ascent, mut max_descent, mut max_row_height) =
1189 (Au::zero(), Au::zero(), Au::zero());
1190
1191 for column_index in 0..self.table.size.width {
1192 let cell = match self.table.slots[row_index][column_index] {
1193 TableSlot::Cell(ref cell) => cell,
1194 _ => continue,
1195 };
1196
1197 let layout = match self.cells_laid_out[row_index][column_index] {
1198 Some(ref layout) => layout,
1199 None => {
1200 warn!(
1201 "Did not find a layout at a slot index with an originating cell."
1202 );
1203 continue;
1204 },
1205 };
1206
1207 let cell = cell.borrow();
1208 let outer_block_size = layout.outer_block_size();
1209 if cell.rowspan == 1 {
1210 max_row_height.max_assign(outer_block_size);
1211 }
1212
1213 if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline {
1214 let ascent = layout.ascent();
1215 let border_padding_start =
1216 layout.border.block_start + layout.padding.block_start;
1217 let border_padding_end = layout.border.block_end + layout.padding.block_end;
1218 max_ascent.max_assign(ascent + border_padding_start);
1219
1220 if cell.rowspan == 1 {
1224 max_descent.max_assign(
1225 layout.layout.content_block_size - ascent + border_padding_end,
1226 );
1227 }
1228 }
1229 }
1230 self.row_baselines.push(max_ascent);
1231 max_row_height.max(max_ascent + max_descent)
1232 })
1233 .collect();
1234 self.calculate_row_sizes_after_first_layout(&mut row_sizes, writing_mode);
1235 row_sizes
1236 }
1237
1238 #[allow(clippy::ptr_arg)] fn calculate_row_sizes_after_first_layout(
1243 &mut self,
1244 row_sizes: &mut Vec<Au>,
1245 writing_mode: WritingMode,
1246 ) {
1247 let mut cells_to_distribute = Vec::new();
1248 let mut total_percentage = 0.;
1249 #[allow(clippy::needless_range_loop)] for row_index in 0..self.table.size.height {
1251 let row_measure = self
1252 .table
1253 .get_row_measure_for_row_at_index(writing_mode, row_index);
1254 row_sizes[row_index].max_assign(row_measure.content_sizes.min_content);
1255
1256 let mut percentage = row_measure.percentage.unwrap_or_default().0;
1257 for column_index in 0..self.table.size.width {
1258 let cell_percentage = self.cell_measures[row_index][column_index]
1259 .block
1260 .percentage
1261 .unwrap_or_default()
1262 .0;
1263 percentage = percentage.max(cell_percentage);
1264
1265 let cell_measure = &self.cell_measures[row_index][column_index].block;
1266 let cell = match self.table.slots[row_index][column_index] {
1267 TableSlot::Cell(ref cell) if cell.borrow().rowspan > 1 => cell,
1268 TableSlot::Cell(_) => {
1269 row_sizes[row_index].max_assign(cell_measure.content_sizes.max_content);
1272 continue;
1273 },
1274 _ => continue,
1275 };
1276
1277 cells_to_distribute.push(RowspanToDistribute {
1278 coordinates: TableSlotCoordinates::new(column_index, row_index),
1279 cell: cell.borrow(),
1280 measure: cell_measure,
1281 });
1282 }
1283
1284 self.rows[row_index].percent = Percentage(percentage.min(1. - total_percentage));
1285 total_percentage += self.rows[row_index].percent.0;
1286 }
1287
1288 cells_to_distribute.sort_by(|a, b| {
1289 if a.range() == b.range() {
1290 return a
1291 .measure
1292 .content_sizes
1293 .min_content
1294 .cmp(&b.measure.content_sizes.min_content);
1295 }
1296 if a.fully_encloses(b) {
1297 return std::cmp::Ordering::Greater;
1298 }
1299 if b.fully_encloses(a) {
1300 return std::cmp::Ordering::Less;
1301 }
1302 a.coordinates.y.cmp(&b.coordinates.y)
1303 });
1304
1305 for rowspan_to_distribute in cells_to_distribute {
1306 let rows_spanned = rowspan_to_distribute.range();
1307 let current_rows_size: Au = rows_spanned.clone().map(|index| row_sizes[index]).sum();
1308 let border_spacing_spanned =
1309 self.table.border_spacing().block * (rows_spanned.len() - 1) as i32;
1310 let excess_size = (rowspan_to_distribute.measure.content_sizes.min_content -
1311 current_rows_size -
1312 border_spacing_spanned)
1313 .max(Au::zero());
1314
1315 self.distribute_extra_size_to_rows(
1316 excess_size,
1317 rows_spanned,
1318 row_sizes,
1319 None,
1320 true, );
1322 }
1323 }
1324
1325 fn distribute_extra_size_to_rows(
1328 &self,
1329 mut excess_size: Au,
1330 track_range: Range<usize>,
1331 track_sizes: &mut [Au],
1332 percentage_resolution_size: Option<Au>,
1333 rowspan_distribution: bool,
1334 ) {
1335 if excess_size.is_zero() {
1336 return;
1337 }
1338
1339 let is_constrained = |track_index: &usize| self.rows[*track_index].constrained;
1340 let is_unconstrained = |track_index: &usize| !is_constrained(track_index);
1341 let is_empty: Vec<bool> = track_sizes.iter().map(|size| size.is_zero()).collect();
1342 let is_not_empty = |track_index: &usize| !is_empty[*track_index];
1343 let other_row_that_starts_a_rowspan = |track_index: &usize| {
1344 *track_index != track_range.start &&
1345 self.rows[*track_index].has_cell_with_span_greater_than_one
1346 };
1347
1348 if let Some(percentage_resolution_size) = percentage_resolution_size {
1352 let get_percent_block_size_deficit = |row_index: usize, track_size: Au| {
1353 let size_needed_for_percent =
1354 percentage_resolution_size.scale_by(self.rows[row_index].percent.0);
1355 (size_needed_for_percent - track_size).max(Au::zero())
1356 };
1357 let percent_block_size_deficit: Au = track_range
1358 .clone()
1359 .map(|index| get_percent_block_size_deficit(index, track_sizes[index]))
1360 .sum();
1361 let percent_distributable_block_size = percent_block_size_deficit.min(excess_size);
1362 if percent_distributable_block_size > Au::zero() {
1363 for track_index in track_range.clone() {
1364 let row_deficit =
1365 get_percent_block_size_deficit(track_index, track_sizes[track_index]);
1366 if row_deficit > Au::zero() {
1367 let ratio =
1368 row_deficit.to_f32_px() / percent_block_size_deficit.to_f32_px();
1369 let size = percent_distributable_block_size.scale_by(ratio);
1370 track_sizes[track_index] += size;
1371 excess_size -= size;
1372 }
1373 }
1374 }
1375 }
1376
1377 if rowspan_distribution {
1380 let rows_that_start_rowspan: Vec<usize> = track_range
1381 .clone()
1382 .filter(other_row_that_starts_a_rowspan)
1383 .collect();
1384 if !rows_that_start_rowspan.is_empty() {
1385 let scale = 1.0 / rows_that_start_rowspan.len() as f32;
1386 for track_index in rows_that_start_rowspan.iter() {
1387 track_sizes[*track_index] += excess_size.scale_by(scale);
1388 }
1389 return;
1390 }
1391 }
1392
1393 let unconstrained_non_empty_rows: Vec<usize> = track_range
1395 .clone()
1396 .filter(is_unconstrained)
1397 .filter(is_not_empty)
1398 .collect();
1399 if !unconstrained_non_empty_rows.is_empty() {
1400 let total_size: Au = unconstrained_non_empty_rows
1401 .iter()
1402 .map(|index| track_sizes[*index])
1403 .sum();
1404 for track_index in unconstrained_non_empty_rows.iter() {
1405 let scale = track_sizes[*track_index].to_f32_px() / total_size.to_f32_px();
1406 track_sizes[*track_index] += excess_size.scale_by(scale);
1407 }
1408 return;
1409 }
1410
1411 let (non_empty_rows, empty_rows): (Vec<usize>, Vec<usize>) =
1412 track_range.clone().partition(is_not_empty);
1413 let only_have_empty_rows = empty_rows.len() == track_range.len();
1414 if !empty_rows.is_empty() {
1415 if rowspan_distribution && only_have_empty_rows {
1418 track_sizes[*empty_rows.last().unwrap()] += excess_size;
1419 return;
1420 }
1421
1422 let non_empty_rows_all_constrained = !non_empty_rows.iter().any(is_unconstrained);
1425 if only_have_empty_rows || non_empty_rows_all_constrained {
1426 let mut rows_to_grow = &empty_rows;
1429 let unconstrained_empty_rows: Vec<usize> = rows_to_grow
1430 .iter()
1431 .copied()
1432 .filter(is_unconstrained)
1433 .collect();
1434 if !unconstrained_empty_rows.is_empty() {
1435 rows_to_grow = &unconstrained_empty_rows;
1436 }
1437
1438 let scale = 1.0 / rows_to_grow.len() as f32;
1440 for track_index in rows_to_grow.iter() {
1441 track_sizes[*track_index] += excess_size.scale_by(scale);
1442 }
1443 return;
1444 }
1445 }
1446
1447 if !non_empty_rows.is_empty() {
1450 let total_size: Au = non_empty_rows.iter().map(|index| track_sizes[*index]).sum();
1451 for track_index in non_empty_rows.iter() {
1452 let scale = track_sizes[*track_index].to_f32_px() / total_size.to_f32_px();
1453 track_sizes[*track_index] += excess_size.scale_by(scale);
1454 }
1455 }
1456 }
1457
1458 fn compute_table_height_and_final_row_heights(
1461 &mut self,
1462 mut row_sizes: Vec<Au>,
1463 containing_block_for_children: &ContainingBlock,
1464 ) {
1465 let table_height_from_style = match containing_block_for_children.size.block {
1472 SizeConstraint::Definite(size) => size,
1473 SizeConstraint::MinMax(min, _) => min,
1474 };
1475
1476 let block_border_spacing = self.table.total_border_spacing().block;
1477 let table_height_from_rows = row_sizes.iter().sum::<Au>() + block_border_spacing;
1478 self.final_table_height = table_height_from_rows.max(table_height_from_style);
1479
1480 if self.final_table_height == table_height_from_rows {
1483 self.row_sizes = row_sizes;
1484 return;
1485 }
1486
1487 self.distribute_extra_size_to_rows(
1492 self.final_table_height - table_height_from_rows,
1493 0..self.table.size.height,
1494 &mut row_sizes,
1495 Some(self.final_table_height),
1496 false, );
1498 self.row_sizes = row_sizes;
1499 }
1500
1501 fn layout_caption(
1502 &self,
1503 caption: &TableCaption,
1504 layout_context: &LayoutContext,
1505 parent_positioning_context: &mut PositioningContext,
1506 ) -> BoxFragment {
1507 let containing_block = &ContainingBlock {
1508 size: ContainingBlockSize {
1509 inline: self.table_width + self.pbm.padding_border_sums.inline,
1510 block: SizeConstraint::default(),
1511 },
1512 style: &self.table.style,
1513 };
1514
1515 let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
1519
1520 let mut positioning_context =
1521 PositioningContext::new_for_layout_box_base(&caption.context.base);
1522 let mut box_fragment = caption.context.layout_in_flow_block_level(
1523 layout_context,
1524 positioning_context
1525 .as_mut()
1526 .unwrap_or(parent_positioning_context),
1527 containing_block,
1528 None, ignore_block_margins_for_stretch,
1530 );
1531
1532 if let Some(mut positioning_context) = positioning_context.take() {
1533 positioning_context.layout_collected_children(layout_context, &mut box_fragment);
1534 parent_positioning_context.append(positioning_context);
1535 }
1536
1537 box_fragment
1538 }
1539
1540 #[servo_tracing::instrument(name = "Table::layout", skip_all)]
1543 fn layout(
1544 mut self,
1545 layout_context: &LayoutContext,
1546 positioning_context: &mut PositioningContext,
1547 containing_block_for_children: &ContainingBlock,
1548 containing_block_for_table: &ContainingBlock,
1549 ) -> CacheableLayoutResult {
1550 let table_writing_mode = containing_block_for_children.style.writing_mode;
1551 self.compute_border_collapse(table_writing_mode);
1552 let layout_style = self.table.layout_style(Some(&self));
1553
1554 self.pbm = layout_style
1555 .padding_border_margin_with_writing_mode_and_containing_block_inline_size(
1556 table_writing_mode,
1557 containing_block_for_table.size.inline,
1558 );
1559 self.compute_measures(layout_context, table_writing_mode);
1560 self.compute_table_width(containing_block_for_children);
1561
1562 let containing_block_for_logical_conversion = ContainingBlock {
1577 size: ContainingBlockSize {
1578 inline: self.table_width,
1579 block: containing_block_for_table.size.block,
1580 },
1581 style: containing_block_for_children.style,
1582 };
1583 let offset_from_wrapper = -self.pbm.padding - self.pbm.border;
1584 let mut current_block_offset = offset_from_wrapper.block_start;
1585
1586 let mut table_layout = CacheableLayoutResult {
1587 fragments: Vec::new(),
1588 content_block_size: Zero::zero(),
1589 content_inline_size_for_table: None,
1590 baselines: Baselines::default(),
1591 depends_on_block_constraints: true,
1592 specific_layout_info: Some(SpecificLayoutInfo::TableWrapper),
1593 collapsible_margins_in_children: CollapsedBlockMargins::zero(),
1594 };
1595
1596 table_layout
1597 .fragments
1598 .extend(self.table.captions.iter().filter_map(|caption| {
1599 let caption = caption.borrow();
1600 if caption.context.style().clone_caption_side() != CaptionSide::Top {
1601 return None;
1602 }
1603
1604 let original_positioning_context_length = positioning_context.len();
1605 let mut caption_fragment =
1606 self.layout_caption(&caption, layout_context, positioning_context);
1607
1608 let caption_pbm = caption_fragment
1611 .padding_border_margin()
1612 .to_logical(table_writing_mode);
1613
1614 let caption_relative_offset = match caption_fragment.style.clone_position() {
1615 Position::Relative => {
1616 relative_adjustement(&caption_fragment.style, containing_block_for_children)
1617 },
1618 _ => LogicalVec2::zero(),
1619 };
1620
1621 caption_fragment.content_rect = LogicalRect {
1622 start_corner: LogicalVec2 {
1623 inline: offset_from_wrapper.inline_start + caption_pbm.inline_start,
1624 block: current_block_offset + caption_pbm.block_start,
1625 } + caption_relative_offset,
1626 size: caption_fragment
1627 .content_rect
1628 .size
1629 .to_logical(table_writing_mode),
1630 }
1631 .as_physical(Some(&containing_block_for_logical_conversion));
1632
1633 current_block_offset += caption_fragment
1634 .margin_rect()
1635 .size
1636 .to_logical(table_writing_mode)
1637 .block;
1638
1639 let caption_fragment = Fragment::Box(ArcRefCell::new(caption_fragment));
1640 positioning_context.adjust_static_position_of_hoisted_fragments(
1641 &caption_fragment,
1642 original_positioning_context_length,
1643 );
1644
1645 caption.context.base.set_fragment(caption_fragment.clone());
1646 Some(caption_fragment)
1647 }));
1648
1649 let original_positioning_context_length = positioning_context.len();
1650 let mut grid_fragment = self.layout_grid(
1651 layout_context,
1652 positioning_context,
1653 &containing_block_for_logical_conversion,
1654 containing_block_for_children,
1655 );
1656
1657 let logical_grid_content_rect = grid_fragment
1660 .content_rect
1661 .to_logical(&containing_block_for_logical_conversion);
1662 let grid_pbm = grid_fragment
1663 .padding_border_margin()
1664 .to_logical(table_writing_mode);
1665 table_layout.baselines = grid_fragment.baselines(table_writing_mode).offset(
1666 current_block_offset +
1667 logical_grid_content_rect.start_corner.block +
1668 grid_pbm.block_start,
1669 );
1670
1671 grid_fragment.content_rect = LogicalRect {
1672 start_corner: LogicalVec2 {
1673 inline: offset_from_wrapper.inline_start + grid_pbm.inline_start,
1674 block: current_block_offset + grid_pbm.block_start,
1675 },
1676 size: grid_fragment
1677 .content_rect
1678 .size
1679 .to_logical(table_writing_mode),
1680 }
1681 .as_physical(Some(&containing_block_for_logical_conversion));
1682
1683 current_block_offset += grid_fragment
1684 .border_rect()
1685 .size
1686 .to_logical(table_writing_mode)
1687 .block;
1688 if logical_grid_content_rect.size.inline < self.table_width {
1689 table_layout.content_inline_size_for_table =
1691 Some(logical_grid_content_rect.size.inline);
1692 }
1693
1694 let grid_fragment = Fragment::Box(ArcRefCell::new(grid_fragment));
1695 positioning_context.adjust_static_position_of_hoisted_fragments(
1696 &grid_fragment,
1697 original_positioning_context_length,
1698 );
1699 table_layout.fragments.push(grid_fragment);
1700
1701 table_layout
1702 .fragments
1703 .extend(self.table.captions.iter().filter_map(|caption| {
1704 let caption = caption.borrow();
1705 if caption.context.style().clone_caption_side() != CaptionSide::Bottom {
1706 return None;
1707 }
1708
1709 let original_positioning_context_length = positioning_context.len();
1710 let mut caption_fragment =
1711 self.layout_caption(&caption, layout_context, positioning_context);
1712
1713 let caption_pbm = caption_fragment
1716 .padding_border_margin()
1717 .to_logical(table_writing_mode);
1718 caption_fragment.content_rect = LogicalRect {
1719 start_corner: LogicalVec2 {
1720 inline: offset_from_wrapper.inline_start + caption_pbm.inline_start,
1721 block: current_block_offset + caption_pbm.block_start,
1722 },
1723 size: caption_fragment
1724 .content_rect
1725 .size
1726 .to_logical(table_writing_mode),
1727 }
1728 .as_physical(Some(&containing_block_for_logical_conversion));
1729
1730 current_block_offset += caption_fragment
1731 .margin_rect()
1732 .size
1733 .to_logical(table_writing_mode)
1734 .block;
1735
1736 let caption_fragment = Fragment::Box(ArcRefCell::new(caption_fragment));
1737 positioning_context.adjust_static_position_of_hoisted_fragments(
1738 &caption_fragment,
1739 original_positioning_context_length,
1740 );
1741
1742 caption.context.base.set_fragment(caption_fragment.clone());
1743 Some(caption_fragment)
1744 }));
1745
1746 table_layout.content_block_size = current_block_offset + offset_from_wrapper.block_end;
1747 table_layout
1748 }
1749
1750 fn layout_grid(
1753 &mut self,
1754 layout_context: &LayoutContext,
1755 positioning_context: &mut PositioningContext,
1756 containing_block_for_logical_conversion: &ContainingBlock,
1757 containing_block_for_children: &ContainingBlock,
1758 ) -> BoxFragment {
1759 self.distributed_column_widths =
1760 Self::distribute_width_to_columns(self.assignable_width, &self.columns);
1761 self.layout_cells_in_row(layout_context, containing_block_for_children);
1762 let table_writing_mode = containing_block_for_children.style.writing_mode;
1763 let first_layout_row_heights = self.do_first_row_layout(table_writing_mode);
1764 self.compute_table_height_and_final_row_heights(
1765 first_layout_row_heights,
1766 containing_block_for_children,
1767 );
1768
1769 assert_eq!(self.table.size.height, self.row_sizes.len());
1770 assert_eq!(self.table.size.width, self.distributed_column_widths.len());
1771
1772 if self.table.size.width == 0 && self.table.size.height == 0 {
1773 let content_rect = LogicalRect {
1774 start_corner: LogicalVec2::zero(),
1775 size: LogicalVec2 {
1776 inline: self.table_width,
1777 block: self.final_table_height,
1778 },
1779 }
1780 .as_physical(Some(containing_block_for_logical_conversion));
1781 return BoxFragment::new(
1782 self.table.grid_base_fragment_info,
1783 self.table.grid_style.clone(),
1784 Vec::new(),
1785 content_rect,
1786 self.pbm.padding.to_physical(table_writing_mode),
1787 self.pbm.border.to_physical(table_writing_mode),
1788 PhysicalSides::zero(),
1789 self.specific_layout_info_for_grid(),
1790 );
1791 }
1792
1793 let mut table_fragments = Vec::new();
1794 let table_and_track_dimensions = TableAndTrackDimensions::new(self);
1795 self.make_fragments_for_columns_and_column_groups(
1796 &table_and_track_dimensions,
1797 &mut table_fragments,
1798 );
1799
1800 let mut baselines = Baselines::default();
1801 let mut row_group_fragment_layout = None;
1802 for row_index in 0..self.table.size.height {
1803 if row_index == 0 {
1813 let row_end = table_and_track_dimensions
1814 .get_row_rect(0)
1815 .max_block_position();
1816 baselines.first = Some(row_end);
1817 baselines.last = Some(row_end);
1818 }
1819
1820 let row_is_collapsed = self.is_row_collapsed(row_index);
1821 let table_row = self.table.rows[row_index].borrow();
1822 let mut row_fragment_layout = RowFragmentLayout::new(
1823 &table_row,
1824 row_index,
1825 &table_and_track_dimensions,
1826 &self.table.style,
1827 );
1828
1829 let old_row_group_index = row_group_fragment_layout
1830 .as_ref()
1831 .map(|layout: &RowGroupFragmentLayout| layout.index);
1832 if table_row.group_index != old_row_group_index {
1833 if let Some(old_row_group_layout) = row_group_fragment_layout.take() {
1835 table_fragments.push(old_row_group_layout.finish(
1836 layout_context,
1837 positioning_context,
1838 containing_block_for_logical_conversion,
1839 containing_block_for_children,
1840 ));
1841 }
1842
1843 if let Some(new_group_index) = table_row.group_index {
1845 row_group_fragment_layout = Some(RowGroupFragmentLayout::new(
1846 self.table.row_groups[new_group_index].clone(),
1847 new_group_index,
1848 &table_and_track_dimensions,
1849 ));
1850 }
1851 }
1852
1853 let column_indices = 0..self.table.size.width;
1854 row_fragment_layout.fragments.reserve(self.table.size.width);
1855 for column_index in column_indices {
1856 self.do_final_cell_layout(
1857 row_index,
1858 column_index,
1859 &table_and_track_dimensions,
1860 &mut baselines,
1861 &mut row_fragment_layout,
1862 row_group_fragment_layout.as_mut(),
1863 positioning_context,
1864 self.is_column_collapsed(column_index) || row_is_collapsed,
1865 );
1866 }
1867
1868 let row_fragment = row_fragment_layout.finish(
1869 layout_context,
1870 positioning_context,
1871 containing_block_for_logical_conversion,
1872 containing_block_for_children,
1873 &mut row_group_fragment_layout,
1874 );
1875
1876 match row_group_fragment_layout.as_mut() {
1877 Some(layout) => layout.fragments.push(row_fragment),
1878 None => table_fragments.push(row_fragment),
1879 }
1880 }
1881
1882 if let Some(row_group_layout) = row_group_fragment_layout.take() {
1883 table_fragments.push(row_group_layout.finish(
1884 layout_context,
1885 positioning_context,
1886 containing_block_for_logical_conversion,
1887 containing_block_for_children,
1888 ));
1889 }
1890
1891 let content_rect = LogicalRect {
1892 start_corner: LogicalVec2::zero(),
1893 size: LogicalVec2 {
1894 inline: table_and_track_dimensions.table_rect.max_inline_position(),
1895 block: table_and_track_dimensions.table_rect.max_block_position(),
1896 },
1897 }
1898 .as_physical(Some(containing_block_for_logical_conversion));
1899 BoxFragment::new(
1900 self.table.grid_base_fragment_info,
1901 self.table.grid_style.clone(),
1902 table_fragments,
1903 content_rect,
1904 self.pbm.padding.to_physical(table_writing_mode),
1905 self.pbm.border.to_physical(table_writing_mode),
1906 PhysicalSides::zero(),
1907 self.specific_layout_info_for_grid(),
1908 )
1909 .with_baselines(baselines)
1910 }
1911
1912 fn specific_layout_info_for_grid(&mut self) -> Option<SpecificLayoutInfo> {
1913 mem::take(&mut self.collapsed_borders).map(|mut collapsed_borders| {
1914 let mut track_sizes = LogicalVec2 {
1917 inline: mem::take(&mut self.distributed_column_widths),
1918 block: mem::take(&mut self.row_sizes),
1919 };
1920 for (column_index, column_size) in track_sizes.inline.iter_mut().enumerate() {
1921 if self.is_column_collapsed(column_index) {
1922 mem::take(column_size);
1923 }
1924 }
1925 for (row_index, row_size) in track_sizes.block.iter_mut().enumerate() {
1926 if self.is_row_collapsed(row_index) {
1927 mem::take(row_size);
1928 }
1929 }
1930 let writing_mode = self.table.style.writing_mode;
1931 if !writing_mode.is_bidi_ltr() {
1932 track_sizes.inline.reverse();
1933 collapsed_borders.inline.reverse();
1934 for border_line in &mut collapsed_borders.block {
1935 border_line.reverse();
1936 }
1937 }
1938 SpecificLayoutInfo::TableGridWithCollapsedBorders(Box::new(SpecificTableGridInfo {
1939 collapsed_borders: if writing_mode.is_horizontal() {
1940 PhysicalVec::new(collapsed_borders.inline, collapsed_borders.block)
1941 } else {
1942 PhysicalVec::new(collapsed_borders.block, collapsed_borders.inline)
1943 },
1944 track_sizes: if writing_mode.is_horizontal() {
1945 PhysicalVec::new(track_sizes.inline, track_sizes.block)
1946 } else {
1947 PhysicalVec::new(track_sizes.block, track_sizes.inline)
1948 },
1949 }))
1950 })
1951 }
1952
1953 fn is_row_collapsed(&self, row_index: usize) -> bool {
1954 let Some(row) = &self.table.rows.get(row_index) else {
1955 return false;
1956 };
1957
1958 let row = row.borrow();
1959 if row.base.style.get_inherited_box().visibility == Visibility::Collapse {
1960 return true;
1961 }
1962 let row_group = match row.group_index {
1963 Some(group_index) => self.table.row_groups[group_index].borrow(),
1964 None => return false,
1965 };
1966 row_group.base.style.get_inherited_box().visibility == Visibility::Collapse
1967 }
1968
1969 fn is_column_collapsed(&self, column_index: usize) -> bool {
1970 let Some(column) = &self.table.columns.get(column_index) else {
1971 return false;
1972 };
1973 let column = column.borrow();
1974 if column.base.style.get_inherited_box().visibility == Visibility::Collapse {
1975 return true;
1976 }
1977 let col_group = match column.group_index {
1978 Some(group_index) => self.table.column_groups[group_index].borrow(),
1979 None => return false,
1980 };
1981 col_group.base.style.get_inherited_box().visibility == Visibility::Collapse
1982 }
1983
1984 #[allow(clippy::too_many_arguments)]
1985 fn do_final_cell_layout(
1986 &mut self,
1987 row_index: usize,
1988 column_index: usize,
1989 dimensions: &TableAndTrackDimensions,
1990 baselines: &mut Baselines,
1991 row_fragment_layout: &mut RowFragmentLayout,
1992 row_group_fragment_layout: Option<&mut RowGroupFragmentLayout>,
1993 positioning_context_for_table: &mut PositioningContext,
1994 is_collapsed: bool,
1995 ) {
1996 let row_group_positioning_context =
1999 row_group_fragment_layout.and_then(|layout| layout.positioning_context.as_mut());
2000 let positioning_context = row_fragment_layout
2001 .positioning_context
2002 .as_mut()
2003 .or(row_group_positioning_context)
2004 .unwrap_or(positioning_context_for_table);
2005
2006 let layout = match self.cells_laid_out[row_index][column_index].take() {
2007 Some(layout) => layout,
2008 None => {
2009 return;
2010 },
2011 };
2012 let cell = match self.table.slots[row_index][column_index] {
2013 TableSlot::Cell(ref cell) => cell,
2014 _ => {
2015 warn!("Did not find a non-spanned cell at index with layout.");
2016 return;
2017 },
2018 }
2019 .borrow();
2020
2021 let row_block_offset = row_fragment_layout.rect.start_corner.block;
2023 let row_baseline = self.row_baselines[row_index];
2024 if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline && !layout.is_empty() {
2025 let baseline = row_block_offset + row_baseline;
2026 if row_index == 0 {
2027 baselines.first = Some(baseline);
2028 }
2029 baselines.last = Some(baseline);
2030 }
2031 let mut row_relative_cell_rect = dimensions.get_cell_rect(
2032 TableSlotCoordinates::new(column_index, row_index),
2033 cell.rowspan,
2034 cell.colspan,
2035 );
2036 row_relative_cell_rect.start_corner -= row_fragment_layout.rect.start_corner;
2037 let mut fragment = cell.create_fragment(
2038 layout,
2039 row_relative_cell_rect,
2040 row_baseline,
2041 positioning_context,
2042 &self.table.style,
2043 &row_fragment_layout.containing_block,
2044 is_collapsed,
2045 );
2046
2047 let make_relative_to_row_start = |mut rect: LogicalRect<Au>| {
2056 rect.start_corner -= row_fragment_layout.rect.start_corner;
2057 let writing_mode = self.table.style.writing_mode;
2058 PhysicalRect::new(
2059 if writing_mode.is_horizontal() {
2060 PhysicalPoint::new(rect.start_corner.inline, rect.start_corner.block)
2061 } else {
2062 PhysicalPoint::new(rect.start_corner.block, rect.start_corner.inline)
2063 },
2064 rect.size.to_physical_size(writing_mode),
2065 )
2066 };
2067
2068 let column = self.table.columns.get(column_index);
2069 let column_group = column
2070 .and_then(|column| column.borrow().group_index)
2071 .and_then(|index| self.table.column_groups.get(index));
2072 if let Some(column_group) = column_group {
2073 let column_group = column_group.borrow();
2074 let rect = make_relative_to_row_start(dimensions.get_column_group_rect(&column_group));
2075 fragment.add_extra_background(ExtraBackground {
2076 style: column_group.shared_background_style.clone(),
2077 rect,
2078 })
2079 }
2080 if let Some(column) = column {
2081 let column = column.borrow();
2082 if !column.is_anonymous {
2083 let rect = make_relative_to_row_start(dimensions.get_column_rect(column_index));
2084 fragment.add_extra_background(ExtraBackground {
2085 style: column.shared_background_style.clone(),
2086 rect,
2087 })
2088 }
2089 }
2090 let row = self.table.rows.get(row_index);
2091 let row_group = row
2092 .and_then(|row| row.borrow().group_index)
2093 .and_then(|index| self.table.row_groups.get(index));
2094 if let Some(row_group) = row_group {
2095 let rect =
2096 make_relative_to_row_start(dimensions.get_row_group_rect(&row_group.borrow()));
2097 fragment.add_extra_background(ExtraBackground {
2098 style: row_group.borrow().shared_background_style.clone(),
2099 rect,
2100 })
2101 }
2102 if let Some(row) = row {
2103 let row = row.borrow();
2104 let rect = make_relative_to_row_start(row_fragment_layout.rect);
2105 fragment.add_extra_background(ExtraBackground {
2106 style: row.shared_background_style.clone(),
2107 rect,
2108 })
2109 }
2110
2111 let fragment = Fragment::Box(ArcRefCell::new(fragment));
2112 cell.base.set_fragment(fragment.clone());
2113 row_fragment_layout.fragments.push(fragment);
2114 }
2115
2116 fn make_fragments_for_columns_and_column_groups(
2117 &self,
2118 dimensions: &TableAndTrackDimensions,
2119 fragments: &mut Vec<Fragment>,
2120 ) {
2121 for column_group in self.table.column_groups.iter() {
2122 let column_group = column_group.borrow();
2123 if !column_group.is_empty() {
2124 let fragment = Fragment::Positioning(PositioningFragment::new_empty(
2125 column_group.base.base_fragment_info,
2126 dimensions
2127 .get_column_group_rect(&column_group)
2128 .as_physical(None),
2129 column_group.base.style.clone(),
2130 ));
2131 column_group.base.set_fragment(fragment.clone());
2132 fragments.push(fragment);
2133 }
2134 }
2135
2136 for (column_index, column) in self.table.columns.iter().enumerate() {
2137 let column = column.borrow();
2138 let fragment = Fragment::Positioning(PositioningFragment::new_empty(
2139 column.base.base_fragment_info,
2140 dimensions.get_column_rect(column_index).as_physical(None),
2141 column.base.style.clone(),
2142 ));
2143 column.base.set_fragment(fragment.clone());
2144 fragments.push(fragment);
2145 }
2146 }
2147
2148 fn compute_border_collapse(&mut self, writing_mode: WritingMode) {
2149 if self.table.style.get_inherited_table().border_collapse != BorderCollapse::Collapse {
2150 self.collapsed_borders = None;
2151 return;
2152 }
2153
2154 let mut collapsed_borders = LogicalVec2 {
2155 block: vec![
2156 vec![Default::default(); self.table.size.width];
2157 self.table.size.height + 1
2158 ],
2159 inline: vec![
2160 vec![Default::default(); self.table.size.height];
2161 self.table.size.width + 1
2162 ],
2163 };
2164
2165 let apply_border = |collapsed_borders: &mut CollapsedBorders,
2166 layout_style: &LayoutStyle,
2167 block: &Range<usize>,
2168 inline: &Range<usize>| {
2169 let border = CollapsedBorder::from_layout_style(layout_style, writing_mode);
2170 border
2171 .block_start
2172 .max_assign_to_slice(&mut collapsed_borders.block[block.start][inline.clone()]);
2173 border
2174 .block_end
2175 .max_assign_to_slice(&mut collapsed_borders.block[block.end][inline.clone()]);
2176 border
2177 .inline_start
2178 .max_assign_to_slice(&mut collapsed_borders.inline[inline.start][block.clone()]);
2179 border
2180 .inline_end
2181 .max_assign_to_slice(&mut collapsed_borders.inline[inline.end][block.clone()]);
2182 };
2183 let hide_inner_borders = |collapsed_borders: &mut CollapsedBorders,
2184 block: &Range<usize>,
2185 inline: &Range<usize>| {
2186 for x in inline.clone() {
2187 for y in block.clone() {
2188 if x != inline.start {
2189 collapsed_borders.inline[x][y].hide();
2190 }
2191 if y != block.start {
2192 collapsed_borders.block[y][x].hide();
2193 }
2194 }
2195 }
2196 };
2197 let all_rows = 0..self.table.size.height;
2198 let all_columns = 0..self.table.size.width;
2199 for row_index in all_rows.clone() {
2200 for column_index in all_columns.clone() {
2201 let cell = match self.table.slots[row_index][column_index] {
2202 TableSlot::Cell(ref cell) => cell,
2203 _ => continue,
2204 }
2205 .borrow();
2206 let block_range = row_index..row_index + cell.rowspan;
2207 let inline_range = column_index..column_index + cell.colspan;
2208 hide_inner_borders(&mut collapsed_borders, &block_range, &inline_range);
2209 apply_border(
2210 &mut collapsed_borders,
2211 &cell.layout_style(),
2212 &block_range,
2213 &inline_range,
2214 );
2215 }
2216 }
2217 for (row_index, row) in self.table.rows.iter().enumerate() {
2218 let row = row.borrow();
2219 apply_border(
2220 &mut collapsed_borders,
2221 &row.layout_style(),
2222 &(row_index..row_index + 1),
2223 &all_columns,
2224 );
2225 }
2226 for row_group in &self.table.row_groups {
2227 let row_group = row_group.borrow();
2228 apply_border(
2229 &mut collapsed_borders,
2230 &row_group.layout_style(),
2231 &row_group.track_range,
2232 &all_columns,
2233 );
2234 }
2235 for (column_index, column) in self.table.columns.iter().enumerate() {
2236 let column = column.borrow();
2237 apply_border(
2238 &mut collapsed_borders,
2239 &column.layout_style(),
2240 &all_rows,
2241 &(column_index..column_index + 1),
2242 );
2243 }
2244 for column_group in &self.table.column_groups {
2245 let column_group = column_group.borrow();
2246 apply_border(
2247 &mut collapsed_borders,
2248 &column_group.layout_style(),
2249 &all_rows,
2250 &column_group.track_range,
2251 );
2252 }
2253 apply_border(
2254 &mut collapsed_borders,
2255 &self.table.layout_style_for_grid(),
2256 &all_rows,
2257 &all_columns,
2258 );
2259
2260 self.collapsed_borders = Some(collapsed_borders);
2261 }
2262
2263 fn get_collapsed_border_widths_for_area(
2264 &self,
2265 area: LogicalSides<usize>,
2266 ) -> Option<LogicalSides<Au>> {
2267 let collapsed_borders = self.collapsed_borders.as_ref()?;
2268 let columns = || area.inline_start..area.inline_end;
2269 let rows = || area.block_start..area.block_end;
2270 let max_width = |slice: &[CollapsedBorder]| {
2271 let slice_widths = slice.iter().map(|collapsed_border| collapsed_border.width);
2272 slice_widths.max().unwrap_or_default()
2273 };
2274 Some(area.map_inline_and_block_axes(
2275 |column| max_width(&collapsed_borders.inline[*column][rows()]) / 2,
2276 |row| max_width(&collapsed_borders.block[*row][columns()]) / 2,
2277 ))
2278 }
2279}
2280
2281struct RowFragmentLayout<'a> {
2282 row: &'a TableTrack,
2283 rect: LogicalRect<Au>,
2284 containing_block: ContainingBlock<'a>,
2285 positioning_context: Option<PositioningContext>,
2286 fragments: Vec<Fragment>,
2287}
2288
2289impl<'a> RowFragmentLayout<'a> {
2290 fn new(
2291 table_row: &'a TableTrack,
2292 index: usize,
2293 dimensions: &TableAndTrackDimensions,
2294 table_style: &'a ComputedValues,
2295 ) -> Self {
2296 let rect = dimensions.get_row_rect(index);
2297 let containing_block = ContainingBlock {
2298 size: ContainingBlockSize {
2299 inline: rect.size.inline,
2300 block: SizeConstraint::Definite(rect.size.block),
2301 },
2302 style: table_style,
2303 };
2304 Self {
2305 row: table_row,
2306 rect,
2307 positioning_context: PositioningContext::new_for_layout_box_base(&table_row.base),
2308 containing_block,
2309 fragments: Vec::new(),
2310 }
2311 }
2312 fn finish(
2313 mut self,
2314 layout_context: &LayoutContext,
2315 table_positioning_context: &mut PositioningContext,
2316 containing_block_for_logical_conversion: &ContainingBlock,
2317 containing_block_for_children: &ContainingBlock,
2318 row_group_fragment_layout: &mut Option<RowGroupFragmentLayout>,
2319 ) -> Fragment {
2320 if self.positioning_context.is_some() {
2321 self.rect.start_corner +=
2322 relative_adjustement(&self.row.base.style, containing_block_for_children);
2323 }
2324
2325 let (inline_size, block_size) = if let Some(row_group_layout) = row_group_fragment_layout {
2326 self.rect.start_corner -= row_group_layout.rect.start_corner;
2327 (
2328 row_group_layout.rect.size.inline,
2329 SizeConstraint::Definite(row_group_layout.rect.size.block),
2330 )
2331 } else {
2332 (
2333 containing_block_for_logical_conversion.size.inline,
2334 containing_block_for_logical_conversion.size.block,
2335 )
2336 };
2337
2338 let row_group_containing_block = ContainingBlock {
2339 size: ContainingBlockSize {
2340 inline: inline_size,
2341 block: block_size,
2342 },
2343 style: containing_block_for_logical_conversion.style,
2344 };
2345
2346 let mut row_fragment = BoxFragment::new(
2347 self.row.base.base_fragment_info,
2348 self.row.base.style.clone(),
2349 self.fragments,
2350 self.rect.as_physical(Some(&row_group_containing_block)),
2351 PhysicalSides::zero(), PhysicalSides::zero(), PhysicalSides::zero(), None, );
2356 row_fragment.set_does_not_paint_background();
2357
2358 if let Some(mut row_positioning_context) = self.positioning_context.take() {
2359 row_positioning_context.layout_collected_children(layout_context, &mut row_fragment);
2360
2361 let parent_positioning_context = row_group_fragment_layout
2362 .as_mut()
2363 .and_then(|layout| layout.positioning_context.as_mut())
2364 .unwrap_or(table_positioning_context);
2365 parent_positioning_context.append(row_positioning_context);
2366 }
2367
2368 let fragment = Fragment::Box(ArcRefCell::new(row_fragment));
2369 self.row.base.set_fragment(fragment.clone());
2370 fragment
2371 }
2372}
2373
2374struct RowGroupFragmentLayout {
2375 row_group: ArcRefCell<TableTrackGroup>,
2376 rect: LogicalRect<Au>,
2377 positioning_context: Option<PositioningContext>,
2378 index: usize,
2379 fragments: Vec<Fragment>,
2380}
2381
2382impl RowGroupFragmentLayout {
2383 fn new(
2384 row_group: ArcRefCell<TableTrackGroup>,
2385 index: usize,
2386 dimensions: &TableAndTrackDimensions,
2387 ) -> Self {
2388 let (rect, positioning_context) = {
2389 let row_group = row_group.borrow();
2390 (
2391 dimensions.get_row_group_rect(&row_group),
2392 PositioningContext::new_for_layout_box_base(&row_group.base),
2393 )
2394 };
2395 Self {
2396 row_group,
2397 rect,
2398 positioning_context,
2399 index,
2400 fragments: Vec::new(),
2401 }
2402 }
2403
2404 fn finish(
2405 mut self,
2406 layout_context: &LayoutContext,
2407 table_positioning_context: &mut PositioningContext,
2408 containing_block_for_logical_conversion: &ContainingBlock,
2409 containing_block_for_children: &ContainingBlock,
2410 ) -> Fragment {
2411 let row_group = self.row_group.borrow();
2412 if self.positioning_context.is_some() {
2413 self.rect.start_corner +=
2414 relative_adjustement(&row_group.base.style, containing_block_for_children);
2415 }
2416
2417 let mut row_group_fragment = BoxFragment::new(
2418 row_group.base.base_fragment_info,
2419 row_group.base.style.clone(),
2420 self.fragments,
2421 self.rect
2422 .as_physical(Some(containing_block_for_logical_conversion)),
2423 PhysicalSides::zero(), PhysicalSides::zero(), PhysicalSides::zero(), None, );
2428 row_group_fragment.set_does_not_paint_background();
2429
2430 if let Some(mut row_positioning_context) = self.positioning_context.take() {
2431 row_positioning_context
2432 .layout_collected_children(layout_context, &mut row_group_fragment);
2433 table_positioning_context.append(row_positioning_context);
2434 }
2435
2436 let fragment = Fragment::Box(ArcRefCell::new(row_group_fragment));
2437 row_group.base.set_fragment(fragment.clone());
2438 fragment
2439 }
2440}
2441
2442struct TableAndTrackDimensions {
2443 table_rect: LogicalRect<Au>,
2445 table_cells_rect: LogicalRect<Au>,
2448 row_dimensions: Vec<(Au, Au)>,
2450 column_dimensions: Vec<(Au, Au)>,
2452}
2453
2454impl TableAndTrackDimensions {
2455 fn new(table_layout: &TableLayout) -> Self {
2456 let border_spacing = table_layout.table.border_spacing();
2457
2458 let fallback_inline_size = table_layout.assignable_width;
2460 let fallback_block_size = table_layout.final_table_height;
2461
2462 let mut column_dimensions = Vec::new();
2463 let mut column_offset = Au::zero();
2464 for column_index in 0..table_layout.table.size.width {
2465 if table_layout.is_column_collapsed(column_index) {
2466 column_dimensions.push((column_offset, column_offset));
2467 continue;
2468 }
2469 let start_offset = column_offset + border_spacing.inline;
2470 let end_offset = start_offset + table_layout.distributed_column_widths[column_index];
2471 column_dimensions.push((start_offset, end_offset));
2472 column_offset = end_offset;
2473 }
2474 column_offset += if table_layout.table.size.width == 0 {
2475 fallback_inline_size
2476 } else {
2477 border_spacing.inline
2478 };
2479
2480 let mut row_dimensions = Vec::new();
2481 let mut row_offset = Au::zero();
2482 for row_index in 0..table_layout.table.size.height {
2483 if table_layout.is_row_collapsed(row_index) {
2484 row_dimensions.push((row_offset, row_offset));
2485 continue;
2486 }
2487 let start_offset = row_offset + border_spacing.block;
2488 let end_offset = start_offset + table_layout.row_sizes[row_index];
2489 row_dimensions.push((start_offset, end_offset));
2490 row_offset = end_offset;
2491 }
2492 row_offset += if table_layout.table.size.height == 0 {
2493 fallback_block_size
2494 } else {
2495 border_spacing.block
2496 };
2497
2498 let table_start_corner = LogicalVec2 {
2499 inline: column_dimensions.first().map_or_else(Au::zero, |v| v.0),
2500 block: row_dimensions.first().map_or_else(Au::zero, |v| v.0),
2501 };
2502 let table_size = LogicalVec2 {
2503 inline: column_dimensions
2504 .last()
2505 .map_or(fallback_inline_size, |v| v.1),
2506 block: row_dimensions.last().map_or(fallback_block_size, |v| v.1),
2507 } - table_start_corner;
2508 let table_cells_rect = LogicalRect {
2509 start_corner: table_start_corner,
2510 size: table_size,
2511 };
2512
2513 let table_rect = LogicalRect {
2514 start_corner: LogicalVec2::zero(),
2515 size: LogicalVec2 {
2516 inline: column_offset,
2517 block: row_offset,
2518 },
2519 };
2520
2521 Self {
2522 table_rect,
2523 table_cells_rect,
2524 row_dimensions,
2525 column_dimensions,
2526 }
2527 }
2528
2529 fn get_row_rect(&self, row_index: usize) -> LogicalRect<Au> {
2530 let mut row_rect = self.table_cells_rect;
2531 let row_dimensions = self.row_dimensions[row_index];
2532 row_rect.start_corner.block = row_dimensions.0;
2533 row_rect.size.block = row_dimensions.1 - row_dimensions.0;
2534 row_rect
2535 }
2536
2537 fn get_column_rect(&self, column_index: usize) -> LogicalRect<Au> {
2538 let mut row_rect = self.table_cells_rect;
2539 let column_dimensions = self.column_dimensions[column_index];
2540 row_rect.start_corner.inline = column_dimensions.0;
2541 row_rect.size.inline = column_dimensions.1 - column_dimensions.0;
2542 row_rect
2543 }
2544
2545 fn get_row_group_rect(&self, row_group: &TableTrackGroup) -> LogicalRect<Au> {
2546 if row_group.is_empty() {
2547 return LogicalRect::zero();
2548 }
2549
2550 let mut row_group_rect = self.table_cells_rect;
2551 let block_start = self.row_dimensions[row_group.track_range.start].0;
2552 let block_end = self.row_dimensions[row_group.track_range.end - 1].1;
2553 row_group_rect.start_corner.block = block_start;
2554 row_group_rect.size.block = block_end - block_start;
2555 row_group_rect
2556 }
2557
2558 fn get_column_group_rect(&self, column_group: &TableTrackGroup) -> LogicalRect<Au> {
2559 if column_group.is_empty() {
2560 return LogicalRect::zero();
2561 }
2562
2563 let mut column_group_rect = self.table_cells_rect;
2564 let inline_start = self.column_dimensions[column_group.track_range.start].0;
2565 let inline_end = self.column_dimensions[column_group.track_range.end - 1].1;
2566 column_group_rect.start_corner.inline = inline_start;
2567 column_group_rect.size.inline = inline_end - inline_start;
2568 column_group_rect
2569 }
2570
2571 fn get_cell_rect(
2572 &self,
2573 coordinates: TableSlotCoordinates,
2574 rowspan: usize,
2575 colspan: usize,
2576 ) -> LogicalRect<Au> {
2577 let start_corner = LogicalVec2 {
2578 inline: self.column_dimensions[coordinates.x].0,
2579 block: self.row_dimensions[coordinates.y].0,
2580 };
2581 let size = LogicalVec2 {
2582 inline: self.column_dimensions[coordinates.x + colspan - 1].1,
2583 block: self.row_dimensions[coordinates.y + rowspan - 1].1,
2584 } - start_corner;
2585 LogicalRect { start_corner, size }
2586 }
2587}
2588
2589impl Table {
2590 fn border_spacing(&self) -> LogicalVec2<Au> {
2591 if self.style.clone_border_collapse() == BorderCollapse::Collapse {
2592 LogicalVec2::zero()
2593 } else {
2594 let border_spacing = self.style.clone_border_spacing();
2595 LogicalVec2 {
2596 inline: border_spacing.horizontal(),
2597 block: border_spacing.vertical(),
2598 }
2599 }
2600 }
2601
2602 fn total_border_spacing(&self) -> LogicalVec2<Au> {
2603 let border_spacing = self.border_spacing();
2604 LogicalVec2 {
2605 inline: if self.size.width > 0 {
2606 border_spacing.inline * (self.size.width as i32 + 1)
2607 } else {
2608 Au::zero()
2609 },
2610 block: if self.size.height > 0 {
2611 border_spacing.block * (self.size.height as i32 + 1)
2612 } else {
2613 Au::zero()
2614 },
2615 }
2616 }
2617
2618 fn get_column_measure_for_column_at_index(
2619 &self,
2620 writing_mode: WritingMode,
2621 column_index: usize,
2622 is_in_fixed_mode: bool,
2623 ) -> CellOrTrackMeasure {
2624 let column = match self.columns.get(column_index) {
2625 Some(column) => column,
2626 None => return CellOrTrackMeasure::zero(),
2627 }
2628 .borrow();
2629
2630 let CellOrColumnOuterSizes {
2631 preferred: preferred_size,
2632 min: min_size,
2633 max: max_size,
2634 percentage: percentage_size,
2635 } = CellOrColumnOuterSizes::new(
2636 &column.base.style,
2637 writing_mode,
2638 &Default::default(),
2639 is_in_fixed_mode,
2640 );
2641
2642 CellOrTrackMeasure {
2643 content_sizes: ContentSizes {
2644 min_content: min_size.inline,
2649 max_content: preferred_size
2653 .inline
2654 .clamp_between_extremums(min_size.inline, max_size.inline),
2655 },
2656 percentage: percentage_size.inline,
2657 }
2658 }
2659
2660 fn get_row_measure_for_row_at_index(
2661 &self,
2662 writing_mode: WritingMode,
2663 row_index: usize,
2664 ) -> CellOrTrackMeasure {
2665 let row = match self.rows.get(row_index) {
2666 Some(row) => row,
2667 None => return CellOrTrackMeasure::zero(),
2668 };
2669
2670 let row = row.borrow();
2674 let size = row.base.style.box_size(writing_mode);
2675 let max_size = row.base.style.max_box_size(writing_mode);
2676 let percentage_contribution = get_size_percentage_contribution(&size, &max_size);
2677
2678 CellOrTrackMeasure {
2679 content_sizes: size
2680 .block
2681 .to_numeric()
2682 .and_then(|size| size.to_length())
2683 .map_or_else(Au::zero, Au::from)
2684 .into(),
2685 percentage: percentage_contribution.block,
2686 }
2687 }
2688
2689 pub(crate) fn layout(
2690 &self,
2691 layout_context: &LayoutContext,
2692 positioning_context: &mut PositioningContext,
2693 containing_block_for_children: &ContainingBlock,
2694 containing_block_for_table: &ContainingBlock,
2695 ) -> CacheableLayoutResult {
2696 TableLayout::new(self).layout(
2697 layout_context,
2698 positioning_context,
2699 containing_block_for_children,
2700 containing_block_for_table,
2701 )
2702 }
2703}
2704
2705impl ComputeInlineContentSizes for Table {
2706 #[servo_tracing::instrument(name = "Table::compute_inline_content_sizes", skip_all)]
2707 fn compute_inline_content_sizes(
2708 &self,
2709 layout_context: &LayoutContext,
2710 constraint_space: &ConstraintSpace,
2711 ) -> InlineContentSizesResult {
2712 let writing_mode = constraint_space.writing_mode;
2713 let mut layout = TableLayout::new(self);
2714 layout.compute_border_collapse(writing_mode);
2715 layout.pbm = self
2716 .layout_style(Some(&layout))
2717 .padding_border_margin_with_writing_mode_and_containing_block_inline_size(
2718 writing_mode,
2719 Au::zero(),
2720 );
2721 layout.compute_measures(layout_context, writing_mode);
2722
2723 let grid_content_sizes = layout.compute_grid_min_max();
2724
2725 let caption_content_sizes = ContentSizes::from(
2729 layout.compute_caption_minimum_inline_size(layout_context) -
2730 layout.pbm.padding_border_sums.inline,
2731 );
2732
2733 InlineContentSizesResult {
2734 sizes: grid_content_sizes.max(caption_content_sizes),
2735 depends_on_block_constraints: false,
2736 }
2737 }
2738}
2739
2740impl Table {
2741 #[inline]
2742 pub(crate) fn layout_style<'a>(
2743 &'a self,
2744 layout: Option<&'a TableLayout<'a>>,
2745 ) -> LayoutStyle<'a> {
2746 LayoutStyle::Table(TableLayoutStyle {
2747 table: self,
2748 layout,
2749 })
2750 }
2751
2752 #[inline]
2753 pub(crate) fn layout_style_for_grid(&self) -> LayoutStyle<'_> {
2754 LayoutStyle::Default(&self.grid_style)
2755 }
2756}
2757
2758impl TableTrack {
2759 #[inline]
2760 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
2761 LayoutStyle::Default(&self.base.style)
2762 }
2763}
2764
2765impl TableTrackGroup {
2766 #[inline]
2767 pub(crate) fn layout_style(&self) -> LayoutStyle<'_> {
2768 LayoutStyle::Default(&self.base.style)
2769 }
2770}
2771
2772impl TableLayoutStyle<'_> {
2773 #[inline]
2774 pub(crate) fn style(&self) -> &ComputedValues {
2775 &self.table.style
2776 }
2777
2778 #[inline]
2779 pub(crate) fn collapses_borders(&self) -> bool {
2780 self.style().get_inherited_table().border_collapse == BorderCollapse::Collapse
2781 }
2782
2783 pub(crate) fn halved_collapsed_border_widths(&self) -> LogicalSides<Au> {
2784 debug_assert!(self.collapses_borders());
2785 let area = LogicalSides {
2786 inline_start: 0,
2787 inline_end: self.table.size.width,
2788 block_start: 0,
2789 block_end: self.table.size.height,
2790 };
2791 if let Some(layout) = self.layout {
2792 layout.get_collapsed_border_widths_for_area(area)
2793 } else {
2794 let mut layout = TableLayout::new(self.table);
2796 layout.compute_border_collapse(self.style().writing_mode);
2797 layout.get_collapsed_border_widths_for_area(area)
2798 }
2799 .expect("Collapsed borders should be computed")
2800 }
2801}
2802
2803impl TableSlotCell {
2804 #[inline]
2805 fn layout_style(&self) -> LayoutStyle<'_> {
2806 self.contents.layout_style(&self.base)
2807 }
2808
2809 fn effective_vertical_align(&self) -> VerticalAlignKeyword {
2810 match self.base.style.clone_vertical_align() {
2811 VerticalAlign::Keyword(VerticalAlignKeyword::Top) => VerticalAlignKeyword::Top,
2812 VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => VerticalAlignKeyword::Bottom,
2813 VerticalAlign::Keyword(VerticalAlignKeyword::Middle) => VerticalAlignKeyword::Middle,
2814 _ => VerticalAlignKeyword::Baseline,
2815 }
2816 }
2817
2818 fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes {
2819 let constraint_space = ConstraintSpace::new_for_style_and_ratio(
2820 &self.base.style,
2821 None, );
2823 self.base
2824 .inline_content_sizes(layout_context, &constraint_space, &self.contents.contents)
2825 .sizes
2826 }
2827
2828 #[allow(clippy::too_many_arguments)]
2829 fn create_fragment(
2830 &self,
2831 mut layout: CellLayout,
2832 cell_rect: LogicalRect<Au>,
2833 cell_baseline: Au,
2834 positioning_context: &mut PositioningContext,
2835 table_style: &ComputedValues,
2836 containing_block: &ContainingBlock,
2837 is_collapsed: bool,
2838 ) -> BoxFragment {
2839 use style::Zero as StyleZero;
2841
2842 let cell_content_rect = cell_rect.deflate(&(layout.padding + layout.border));
2843 let content_block_size = layout.layout.content_block_size;
2844 let vertical_align_offset = match self.effective_vertical_align() {
2845 VerticalAlignKeyword::Top => Au::zero(),
2846 VerticalAlignKeyword::Bottom => cell_content_rect.size.block - content_block_size,
2847 VerticalAlignKeyword::Middle => {
2848 (cell_content_rect.size.block - content_block_size).scale_by(0.5)
2849 },
2850 _ => {
2851 cell_baseline -
2852 (layout.padding.block_start + layout.border.block_start) -
2853 layout.ascent()
2854 },
2855 };
2856
2857 let mut base_fragment_info = self.base.base_fragment_info;
2858 if self.base.style.get_inherited_table().empty_cells == EmptyCells::Hide &&
2859 table_style.get_inherited_table().border_collapse != BorderCollapse::Collapse &&
2860 layout.is_empty_for_empty_cells()
2861 {
2862 base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
2863 }
2864
2865 if is_collapsed {
2866 base_fragment_info.flags.insert(FragmentFlags::IS_COLLAPSED);
2867 }
2868
2869 let mut vertical_align_fragment_rect = cell_content_rect;
2871 vertical_align_fragment_rect.start_corner = LogicalVec2 {
2872 inline: Au::zero(),
2873 block: vertical_align_offset,
2874 };
2875 let vertical_align_fragment = PositioningFragment::new_anonymous(
2876 self.base.style.clone(),
2877 vertical_align_fragment_rect.as_physical(None),
2878 layout.layout.fragments,
2879 );
2880
2881 let physical_cell_rect = cell_content_rect.as_physical(Some(containing_block));
2889 layout
2890 .positioning_context
2891 .adjust_static_position_of_hoisted_fragments_with_offset(
2892 &physical_cell_rect.origin.to_vector(),
2893 PositioningContextLength::zero(),
2894 );
2895 positioning_context.append(layout.positioning_context);
2896
2897 let specific_layout_info = (table_style.get_inherited_table().border_collapse ==
2898 BorderCollapse::Collapse)
2899 .then_some(SpecificLayoutInfo::TableCellWithCollapsedBorders);
2900
2901 BoxFragment::new(
2902 base_fragment_info,
2903 self.base.style.clone(),
2904 vec![Fragment::Positioning(vertical_align_fragment)],
2905 physical_cell_rect,
2906 layout.padding.to_physical(table_style.writing_mode),
2907 layout.border.to_physical(table_style.writing_mode),
2908 PhysicalSides::zero(), specific_layout_info,
2910 )
2911 .with_baselines(layout.layout.baselines)
2912 }
2913}
2914
2915fn get_size_percentage_contribution(
2916 size: &LogicalVec2<Size<ComputedLengthPercentage>>,
2917 max_size: &LogicalVec2<Size<ComputedLengthPercentage>>,
2918) -> LogicalVec2<Option<Percentage>> {
2919 LogicalVec2 {
2927 inline: max_two_optional_percentages(
2928 size.inline.to_percentage(),
2929 max_size.inline.to_percentage(),
2930 ),
2931 block: max_two_optional_percentages(
2932 size.block.to_percentage(),
2933 max_size.block.to_percentage(),
2934 ),
2935 }
2936}
2937
2938struct CellOrColumnOuterSizes {
2939 min: LogicalVec2<Au>,
2940 preferred: LogicalVec2<Au>,
2941 max: LogicalVec2<Option<Au>>,
2942 percentage: LogicalVec2<Option<Percentage>>,
2943}
2944
2945impl CellOrColumnOuterSizes {
2946 fn new(
2947 style: &Arc<ComputedValues>,
2948 writing_mode: WritingMode,
2949 padding_border_sums: &LogicalVec2<Au>,
2950 is_in_fixed_mode: bool,
2951 ) -> Self {
2952 let box_sizing = style.get_position().box_sizing;
2953 let outer_size = |size: LogicalVec2<Au>| match box_sizing {
2954 BoxSizing::ContentBox => size + *padding_border_sums,
2955 BoxSizing::BorderBox => LogicalVec2 {
2956 inline: size.inline.max(padding_border_sums.inline),
2957 block: size.block.max(padding_border_sums.block),
2958 },
2959 };
2960
2961 let outer_option_size = |size: LogicalVec2<Option<Au>>| match box_sizing {
2962 BoxSizing::ContentBox => size.map_inline_and_block_axes(
2963 |inline| inline.map(|inline| inline + padding_border_sums.inline),
2964 |block| block.map(|block| block + padding_border_sums.block),
2965 ),
2966 BoxSizing::BorderBox => size.map_inline_and_block_axes(
2967 |inline| inline.map(|inline| inline.max(padding_border_sums.inline)),
2968 |block| block.map(|block| block.max(padding_border_sums.block)),
2969 ),
2970 };
2971
2972 let get_size_for_axis = |size: &Size<ComputedLengthPercentage>| {
2973 size.to_numeric()
2976 .and_then(|length_percentage| length_percentage.to_length())
2977 .map(Au::from)
2978 };
2979
2980 let size = style.box_size(writing_mode);
2981 if is_in_fixed_mode {
2982 return Self {
2983 percentage: size.map(|v| v.to_percentage()),
2984 preferred: outer_option_size(size.map(get_size_for_axis))
2985 .map(|v| v.unwrap_or_default()),
2986 min: LogicalVec2::default(),
2987 max: LogicalVec2::default(),
2988 };
2989 }
2990
2991 let min_size = style.min_box_size(writing_mode);
2992 let max_size = style.max_box_size(writing_mode);
2993
2994 Self {
2995 min: outer_size(min_size.map(|v| get_size_for_axis(v).unwrap_or_default())),
2996 preferred: outer_size(size.map(|v| get_size_for_axis(v).unwrap_or_default())),
2997 max: outer_option_size(max_size.map(get_size_for_axis)),
2998 percentage: get_size_percentage_contribution(&size, &max_size),
2999 }
3000 }
3001}
3002
3003struct RowspanToDistribute<'a> {
3004 coordinates: TableSlotCoordinates,
3005 cell: AtomicRef<'a, TableSlotCell>,
3006 measure: &'a CellOrTrackMeasure,
3007}
3008
3009impl RowspanToDistribute<'_> {
3010 fn range(&self) -> Range<usize> {
3011 self.coordinates.y..self.coordinates.y + self.cell.rowspan
3012 }
3013
3014 fn fully_encloses(&self, other: &RowspanToDistribute) -> bool {
3015 other.coordinates.y > self.coordinates.y && other.range().end < self.range().end
3016 }
3017}
3018
3019#[derive(Debug)]
3022struct ColspanToDistribute {
3023 starting_column: usize,
3024 span: usize,
3025 content_sizes: ContentSizes,
3026 percentage: Option<Percentage>,
3027}
3028
3029impl ColspanToDistribute {
3030 fn comparison_for_sort(a: &Self, b: &Self) -> Ordering {
3034 a.span
3035 .cmp(&b.span)
3036 .then_with(|| b.starting_column.cmp(&b.starting_column))
3037 }
3038
3039 fn range(&self) -> Range<usize> {
3040 self.starting_column..self.starting_column + self.span
3041 }
3042}