From d58aa7fc0475e756d3b3cde13977a599339e495a Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Thu, 16 Jan 2025 03:24:00 -0800 Subject: [PATCH] layout: Allow different collapsed border style/color within a row/column (#35003) We were previously using the same style and color for two collapsed borders sharing a coordinate. Now such a line of collapsed borders can be piecewise and have different colors and styles. This still doesn't add support for piecewise border widths. Also, since we are currently painting borders as part of the table and cell boxes, and a box side can't have a piecewise border, this patch only really works when: - There aren't spanning cells - The table has no assigned border (only the cells and tracks have it) Signed-off-by: Oriol Brufau --- components/layout_2020/geom.rs | 2 +- components/layout_2020/table/layout.rs | 198 ++++++++++-------- .../border-bottom-applies-to-006.xht.ini | 2 - ...border-bottom-color-applies-to-006.xht.ini | 2 - ...border-bottom-width-applies-to-006.xht.ini | 2 - .../borders/border-top-applies-to-006.xht.ini | 2 - .../border-top-color-applies-to-006.xht.ini | 2 - .../border-top-width-applies-to-006.xht.ini | 2 - .../border-collapse-dynamic-cell-002.xht.ini | 2 - .../collapsed-border-remove-cell.html.ini | 2 - 10 files changed, 116 insertions(+), 100 deletions(-) delete mode 100644 tests/wpt/meta/css/CSS2/borders/border-bottom-applies-to-006.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/borders/border-bottom-color-applies-to-006.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/borders/border-bottom-width-applies-to-006.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/borders/border-top-applies-to-006.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/borders/border-top-color-applies-to-006.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/borders/border-top-width-applies-to-006.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/tables/border-collapse-dynamic-cell-002.xht.ini delete mode 100644 tests/wpt/meta/css/css-tables/collapsed-border-remove-cell.html.ini diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index ce6b969b1ef..d316f165609 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -40,7 +40,7 @@ pub struct LogicalRect { pub size: LogicalVec2, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct LogicalSides { pub inline_start: T, pub inline_end: T, diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 8b153d05b4b..91af3d59447 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -45,11 +45,12 @@ use crate::{ }; fn detailed_layout_info( - border_style_color: Option>, + border_style_color: Option>, + writing_mode: WritingMode, ) -> Option { Some(SpecificLayoutInfo::TableOrTableCell(Box::new( SpecificTableOrTableCellInfo { - border_style_color: border_style_color?, + border_style_color: border_style_color?.to_physical(writing_mode), }, ))) } @@ -131,9 +132,9 @@ impl CollapsedBorder { } } - fn max_assign(&mut self, other: Self) { - if *self < other { - *self = other; + fn max_assign(&mut self, other: &Self) { + if *self < *other { + *self = other.clone(); } } } @@ -172,13 +173,25 @@ impl PartialOrd for CollapsedBorder { impl Eq for CollapsedBorder {} -/// The calculated collapsed borders. +/// Represents a piecewise sequence of collapsed borders along a line. #[derive(Clone, Debug, Default)] -struct CollapsedBorders { - block: Vec, - inline: Vec, +struct CollapsedBorderLine { + max_width: Au, + list: Vec, } +impl CollapsedBorderLine { + fn max_assign(&mut self, collapsed_border: &CollapsedBorder, range: &Range) { + self.max_width.max_assign(collapsed_border.width); + for index in range.clone() { + self.list[index].max_assign(collapsed_border) + } + } +} + +/// The calculated collapsed borders. +type CollapsedBorders = LogicalVec2>; + /// A helper struct that performs the layout of the box tree version /// of a table into the fragment tree version. This implements /// @@ -279,14 +292,13 @@ impl<'a> TableLayout<'a> { .percentages_relative_to(Au::zero()); let border = self - .get_collapsed_borders_for_cell( - cell, - TableSlotCoordinates::new(column_index, row_index), - ) - .map_or_else( - || cell.base.style.border_width(writing_mode), - |(border, _)| border, - ); + .get_collapsed_border_widths_for_area(LogicalSides { + inline_start: column_index, + inline_end: column_index + cell.colspan, + block_start: row_index, + block_end: row_index + cell.rowspan, + }) + .unwrap_or_else(|| cell.base.style.border_width(writing_mode)); let padding_border_sums = LogicalVec2 { inline: padding.inline_sum() + border.inline_sum(), @@ -1195,22 +1207,23 @@ impl<'a> TableLayout<'a> { return None; }; - let coordinates = TableSlotCoordinates::new(column_index, row_index); - let (border, detailed_layout_info) = - match self.get_collapsed_borders_for_cell(cell, coordinates) { - Some((border_width, border_style_color)) => { - let border_style_color = border_style_color - .to_physical(self.table.style.writing_mode); - (border_width, detailed_layout_info(Some(border_style_color))) - }, - None => ( - cell.base.style.border_width( - containing_block_for_table.style.writing_mode, - ), - None, - ), - }; - + let area = LogicalSides { + inline_start: column_index, + inline_end: column_index + cell.colspan, + block_start: row_index, + block_end: row_index + cell.rowspan, + }; + let detailed_layout_info = detailed_layout_info( + self.get_collapsed_border_style_colors_for_area(area), + self.table.style.writing_mode, + ); + let border = self + .get_collapsed_border_widths_for_area(area) + .unwrap_or_else(|| { + cell.base + .style + .border_width(containing_block_for_table.style.writing_mode) + }); let padding: LogicalSides = cell .base .style @@ -1861,20 +1874,13 @@ impl<'a> TableLayout<'a> { assert_eq!(self.table.size.height, self.row_sizes.len()); assert_eq!(self.table.size.width, self.distributed_column_widths.len()); - let border_style_color = self.collapsed_borders.as_ref().map(|collapsed_borders| { - LogicalSides { - inline_start: collapsed_borders.inline[0].style_color.clone(), - inline_end: collapsed_borders.inline[self.table.size.width] - .style_color - .clone(), - block_start: collapsed_borders.block[0].style_color.clone(), - block_end: collapsed_borders.block[self.table.size.height] - .style_color - .clone(), - } - .to_physical(table_writing_mode) + let border_style_color = self.get_collapsed_border_style_colors_for_area(LogicalSides { + inline_start: 0, + inline_end: self.table.size.width, + block_start: 0, + block_end: self.table.size.height, }); - let detailed_layout_info = detailed_layout_info(border_style_color); + let detailed_layout_info = detailed_layout_info(border_style_color, table_writing_mode); if self.table.size.width == 0 && self.table.size.height == 0 { let content_rect = LogicalRect { @@ -2211,17 +2217,29 @@ impl<'a> TableLayout<'a> { } let mut collapsed_borders = CollapsedBorders { - block: vec![Default::default(); self.table.size.height + 1], - inline: vec![Default::default(); self.table.size.width + 1], + block: vec![ + CollapsedBorderLine { + max_width: Au::zero(), + list: vec![Default::default(); self.table.size.width], + }; + self.table.size.height + 1 + ], + inline: vec![ + CollapsedBorderLine { + max_width: Au::zero(), + list: vec![Default::default(); self.table.size.height], + }; + self.table.size.width + 1 + ], }; let mut apply_border = |style: &ComputedValues, block: &Range, inline: &Range| { let border = CollapsedBorder::from_style(style, writing_mode); - collapsed_borders.block[block.start].max_assign(border.block_start); - collapsed_borders.block[block.end].max_assign(border.block_end); - collapsed_borders.inline[inline.start].max_assign(border.inline_start); - collapsed_borders.inline[inline.end].max_assign(border.inline_end); + collapsed_borders.block[block.start].max_assign(&border.block_start, inline); + collapsed_borders.block[block.end].max_assign(&border.block_end, inline); + collapsed_borders.inline[inline.start].max_assign(&border.inline_start, block); + collapsed_borders.inline[inline.end].max_assign(&border.inline_end, block); }; let all_rows = 0..self.table.size.height; let all_columns = 0..self.table.size.width; @@ -2256,47 +2274,61 @@ impl<'a> TableLayout<'a> { self.collapsed_borders = Some(collapsed_borders); } - fn get_collapsed_borders_for_cell( + fn get_collapsed_border_widths_for_area( &self, - cell: &TableSlotCell, - coordinates: TableSlotCoordinates, - ) -> Option<(LogicalSides, LogicalSides)> { + area: LogicalSides, + ) -> Option> { let collapsed_borders = self.collapsed_borders.as_ref()?; - let end_x = coordinates.x + cell.colspan; - let end_y = coordinates.y + cell.rowspan; - let inline_start = &collapsed_borders.inline[coordinates.x]; - let inline_end = &collapsed_borders.inline[end_x]; - let block_start = &collapsed_borders.block[coordinates.y]; - let block_end = &collapsed_borders.block[end_y]; - let border_width = LogicalSides { - inline_start: if coordinates.x == 0 { - inline_start.width - self.pbm.border.inline_start + let inline_start = &collapsed_borders.inline[area.inline_start]; + let inline_end = &collapsed_borders.inline[area.inline_end]; + let block_start = &collapsed_borders.block[area.block_start]; + let block_end = &collapsed_borders.block[area.block_end]; + Some(LogicalSides { + inline_start: if area.inline_start == 0 { + inline_start.max_width - self.pbm.border.inline_start } else { - inline_start.width / 2 + inline_start.max_width / 2 }, - inline_end: if end_x == self.table.size.width { - inline_end.width - self.pbm.border.inline_end + inline_end: if area.inline_end == self.table.size.width { + inline_end.max_width - self.pbm.border.inline_end } else { - inline_end.width / 2 + inline_end.max_width / 2 }, - block_start: if coordinates.y == 0 { - block_start.width - self.pbm.border.block_start + block_start: if area.block_start == 0 { + block_start.max_width - self.pbm.border.block_start } else { - block_start.width / 2 + block_start.max_width / 2 }, - block_end: if end_y == self.table.size.height { - block_end.width - self.pbm.border.block_end + block_end: if area.block_end == self.table.size.height { + block_end.max_width - self.pbm.border.block_end } else { - block_end.width / 2 + block_end.max_width / 2 }, - }; - let border_style_color = LogicalSides { - inline_start: inline_start.style_color.clone(), - inline_end: inline_end.style_color.clone(), - block_start: block_start.style_color.clone(), - block_end: block_end.style_color.clone(), - }; - Some((border_width, border_style_color)) + }) + } + + fn get_collapsed_border_style_colors_for_area( + &self, + area: LogicalSides, + ) -> Option> { + let collapsed_borders = self.collapsed_borders.as_ref()?; + if self.table.size.width == 0 || self.table.size.height == 0 { + return Some(LogicalSides::default()); + } + let inline_start = &collapsed_borders.inline[area.inline_start]; + let inline_end = &collapsed_borders.inline[area.inline_end]; + let block_start = &collapsed_borders.block[area.block_start]; + let block_end = &collapsed_borders.block[area.block_end]; + + // This area may span multiple rows and columns, each of which can have different + // collapsed borders. However, we don't have support for one side of a box to have + // a piecewise border. Therefore, we just pick the first piece for the entire side. + Some(LogicalSides { + inline_start: inline_start.list[area.block_start].style_color.clone(), + inline_end: inline_end.list[area.block_start].style_color.clone(), + block_start: block_start.list[area.inline_start].style_color.clone(), + block_end: block_end.list[area.inline_start].style_color.clone(), + }) } } diff --git a/tests/wpt/meta/css/CSS2/borders/border-bottom-applies-to-006.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-bottom-applies-to-006.xht.ini deleted file mode 100644 index 35ea6d3fada..00000000000 --- a/tests/wpt/meta/css/CSS2/borders/border-bottom-applies-to-006.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-bottom-applies-to-006.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/borders/border-bottom-color-applies-to-006.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-bottom-color-applies-to-006.xht.ini deleted file mode 100644 index 40f5c78d7cb..00000000000 --- a/tests/wpt/meta/css/CSS2/borders/border-bottom-color-applies-to-006.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-bottom-color-applies-to-006.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/borders/border-bottom-width-applies-to-006.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-bottom-width-applies-to-006.xht.ini deleted file mode 100644 index 4d181d00be2..00000000000 --- a/tests/wpt/meta/css/CSS2/borders/border-bottom-width-applies-to-006.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-bottom-width-applies-to-006.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/borders/border-top-applies-to-006.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-top-applies-to-006.xht.ini deleted file mode 100644 index 27b2aaa4cbf..00000000000 --- a/tests/wpt/meta/css/CSS2/borders/border-top-applies-to-006.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-top-applies-to-006.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/borders/border-top-color-applies-to-006.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-top-color-applies-to-006.xht.ini deleted file mode 100644 index 099d358ca08..00000000000 --- a/tests/wpt/meta/css/CSS2/borders/border-top-color-applies-to-006.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-top-color-applies-to-006.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/borders/border-top-width-applies-to-006.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-top-width-applies-to-006.xht.ini deleted file mode 100644 index 050bbc2da77..00000000000 --- a/tests/wpt/meta/css/CSS2/borders/border-top-width-applies-to-006.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-top-width-applies-to-006.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/tables/border-collapse-dynamic-cell-002.xht.ini b/tests/wpt/meta/css/CSS2/tables/border-collapse-dynamic-cell-002.xht.ini deleted file mode 100644 index 4a0bc7a2846..00000000000 --- a/tests/wpt/meta/css/CSS2/tables/border-collapse-dynamic-cell-002.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-collapse-dynamic-cell-002.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/collapsed-border-remove-cell.html.ini b/tests/wpt/meta/css/css-tables/collapsed-border-remove-cell.html.ini deleted file mode 100644 index 6430bb5ee41..00000000000 --- a/tests/wpt/meta/css/css-tables/collapsed-border-remove-cell.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[collapsed-border-remove-cell.html] - expected: FAIL