aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOriol Brufau <obrufau@igalia.com>2024-09-16 10:20:22 +0200
committerGitHub <noreply@github.com>2024-09-16 08:20:22 +0000
commitb12cebd1ac3b2bc809e5ac69f708cf61b515590d (patch)
tree6fc8bae0f36ce8f267608cf76244689a280fe7a2
parent679afe519591c3c36036154afb1e9b6d73ffa1ac (diff)
downloadservo-b12cebd1ac3b2bc809e5ac69f708cf61b515590d.tar.gz
servo-b12cebd1ac3b2bc809e5ac69f708cf61b515590d.zip
Small improvements for table border collapse (#33452)
We were only collapsing the borders from adjacent cells. This patch also handles the borders from rows, row groups, columns, and column groups. Additionally, it takes the border style into account in order to decide which border wins. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
-rw-r--r--components/layout_2020/style_ext.rs22
-rw-r--r--components/layout_2020/table/layout.rs121
-rw-r--r--tests/wpt/meta/css/CSS2/borders/border-conflict-style-101.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/borders/border-conflict-style-102.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/borders/border-conflict-style-103.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/borders/border-conflict-style-104.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/borders/border-conflict-style-105.xht.ini2
-rw-r--r--tests/wpt/meta/css/CSS2/borders/border-conflict-style-106.xht.ini2
-rw-r--r--tests/wpt/meta/css/css-tables/border-writing-mode-dynamic-001.html.ini3
-rw-r--r--tests/wpt/meta/css/css-tables/col_removal.html.ini3
-rw-r--r--tests/wpt/meta/css/css-tables/rules-groups.html.ini2
11 files changed, 130 insertions, 33 deletions
diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs
index 53bf9e6df33..21283395789 100644
--- a/components/layout_2020/style_ext.rs
+++ b/components/layout_2020/style_ext.rs
@@ -16,7 +16,9 @@ use style::properties::ComputedValues;
use style::servo::selector_parser::PseudoElement;
use style::values::computed::basic_shape::ClipPath;
use style::values::computed::image::Image as ComputedImageLayer;
-use style::values::computed::{AlignItems, LengthPercentage, NonNegativeLengthPercentage, Size};
+use style::values::computed::{
+ AlignItems, BorderStyle, LengthPercentage, NonNegativeLengthPercentage, Size,
+};
use style::values::generics::box_::Perspective;
use style::values::generics::length::MaxSize;
use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
@@ -250,6 +252,8 @@ pub(crate) trait ComputedValuesExt {
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<&LengthPercentage>;
+ fn border_style(&self, containing_block_writing_mode: WritingMode)
+ -> LogicalSides<BorderStyle>;
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Au>;
fn margin(
&self,
@@ -553,6 +557,22 @@ impl ComputedValuesExt for ComputedValues {
)
}
+ fn border_style(
+ &self,
+ containing_block_writing_mode: WritingMode,
+ ) -> LogicalSides<BorderStyle> {
+ let border = self.get_border();
+ LogicalSides::from_physical(
+ &PhysicalSides::new(
+ border.border_top_style,
+ border.border_right_style,
+ border.border_bottom_style,
+ border.border_left_style,
+ ),
+ containing_block_writing_mode,
+ )
+ }
+
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Au> {
let border = self.get_border();
LogicalSides::from_physical(
diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs
index e52bab12400..ed4aef17bf4 100644
--- a/components/layout_2020/table/layout.rs
+++ b/components/layout_2020/table/layout.rs
@@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+use core::cmp::Ordering;
use std::ops::Range;
use app_units::{Au, MAX_AU};
@@ -16,7 +17,9 @@ use style::computed_values::table_layout::T as TableLayoutMode;
use style::computed_values::visibility::T as Visibility;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
-use style::values::computed::{LengthPercentage as ComputedLengthPercentage, Percentage};
+use style::values::computed::{
+ BorderStyle, LengthPercentage as ComputedLengthPercentage, Percentage,
+};
use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, VerticalAlignKeyword};
use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage};
use style::Zero;
@@ -92,11 +95,79 @@ struct ColumnLayout {
has_originating_cells: bool,
}
+/// A calculated collapsed border.
+#[derive(Clone, Debug, Eq, PartialEq)]
+struct CollapsedBorder {
+ style: BorderStyle,
+ width: Au,
+}
+
+impl Default for CollapsedBorder {
+ fn default() -> Self {
+ Self::new(BorderStyle::None, Au::zero())
+ }
+}
+
+impl CollapsedBorder {
+ fn new(style: BorderStyle, width: Au) -> Self {
+ Self { style, width }
+ }
+
+ fn from_style(style: &ComputedValues, writing_mode: WritingMode) -> LogicalSides<Self> {
+ let border_style = style.border_style(writing_mode);
+ let border_width = style.border_width(writing_mode);
+ LogicalSides {
+ inline_start: Self::new(border_style.inline_start, border_width.inline_start),
+ inline_end: Self::new(border_style.inline_end, border_width.inline_end),
+ block_start: Self::new(border_style.block_start, border_width.block_start),
+ block_end: Self::new(border_style.block_end, border_width.block_end),
+ }
+ }
+
+ fn max_assign(&mut self, other: Self) {
+ if *self < other {
+ *self = other;
+ }
+ }
+}
+
+/// <https://drafts.csswg.org/css-tables/#border-specificity>
+/// > Given two borders styles, the border style having the most specificity is the border style which…
+/// > 1. … has the value "hidden" as border-style, if only one does
+/// > 2. … has the biggest border-width, once converted into css pixels
+/// > 3. … has the border-style which comes first in the following list:
+/// > double, solid, dashed, dotted, ridge, outset, groove, inset, none
+impl Ord for CollapsedBorder {
+ fn cmp(&self, other: &Self) -> Ordering {
+ let style_specificity = |border: &Self| match border.style {
+ BorderStyle::None => 0,
+ BorderStyle::Inset => 1,
+ BorderStyle::Groove => 2,
+ BorderStyle::Outset => 3,
+ BorderStyle::Ridge => 4,
+ BorderStyle::Dotted => 5,
+ BorderStyle::Dashed => 6,
+ BorderStyle::Solid => 7,
+ BorderStyle::Double => 8,
+ BorderStyle::Hidden => 9,
+ };
+ ((self.style == BorderStyle::Hidden).cmp(&(other.style == BorderStyle::Hidden)))
+ .then_with(|| self.width.cmp(&other.width))
+ .then_with(|| style_specificity(self).cmp(&style_specificity(other)))
+ }
+}
+
+impl PartialOrd for CollapsedBorder {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
/// The calculated collapsed borders.
#[derive(Clone, Debug, Default)]
struct CollapsedBorders {
- block: Vec<Au>,
- inline: Vec<Au>,
+ block: Vec<CollapsedBorder>,
+ inline: Vec<CollapsedBorder>,
}
/// A helper struct that performs the layout of the box tree version
@@ -2149,10 +2220,32 @@ impl<'a> TableLayout<'a> {
}
let mut collapsed_borders = CollapsedBorders {
- block: vec![Au::zero(); self.table.size.height + 1],
- inline: vec![Au::zero(); self.table.size.width + 1],
+ block: vec![Default::default(); self.table.size.height + 1],
+ inline: vec![Default::default(); self.table.size.width + 1],
};
+ let mut apply_border =
+ |style: &ComputedValues, block: &Range<usize>, inline: &Range<usize>| {
+ 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);
+ };
+ let all_rows = 0..self.table.size.height;
+ let all_columns = 0..self.table.size.width;
+ for column_group in &self.table.column_groups {
+ apply_border(&column_group.style, &all_rows, &column_group.track_range);
+ }
+ for (column_index, column) in self.table.columns.iter().enumerate() {
+ apply_border(&column.style, &all_rows, &(column_index..column_index + 1));
+ }
+ for row_group in &self.table.row_groups {
+ apply_border(&row_group.style, &row_group.track_range, &all_columns);
+ }
+ for (row_index, row) in self.table.rows.iter().enumerate() {
+ apply_border(&row.style, &(row_index..row_index + 1), &all_columns);
+ }
for row_index in 0..self.table.size.height {
for column_index in 0..self.table.size.width {
let cell = match self.table.slots[row_index][column_index] {
@@ -2160,11 +2253,11 @@ impl<'a> TableLayout<'a> {
_ => continue,
};
- let border = cell.style.border_width(writing_mode);
- collapsed_borders.block[row_index].max_assign(border.block_start);
- collapsed_borders.block[row_index + cell.rowspan].max_assign(border.block_end);
- collapsed_borders.inline[column_index].max_assign(border.inline_start);
- collapsed_borders.inline[column_index + cell.colspan].max_assign(border.inline_end);
+ apply_border(
+ &cell.style,
+ &(row_index..row_index + cell.rowspan),
+ &(column_index..column_index + cell.colspan),
+ );
}
}
@@ -2180,10 +2273,10 @@ impl<'a> TableLayout<'a> {
let end_x = coordinates.x + cell.colspan;
let end_y = coordinates.y + cell.rowspan;
let mut result = LogicalSides {
- inline_start: collapsed_borders.inline[coordinates.x],
- inline_end: collapsed_borders.inline[end_x],
- block_start: collapsed_borders.block[coordinates.y],
- block_end: collapsed_borders.block[end_y],
+ inline_start: collapsed_borders.inline[coordinates.x].width,
+ inline_end: collapsed_borders.inline[end_x].width,
+ block_start: collapsed_borders.block[coordinates.y].width,
+ block_end: collapsed_borders.block[end_y].width,
};
if coordinates.x != 0 {
diff --git a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-101.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-conflict-style-101.xht.ini
deleted file mode 100644
index 1f31519bf1a..00000000000
--- a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-101.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[border-conflict-style-101.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-102.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-conflict-style-102.xht.ini
deleted file mode 100644
index c5c4965f0e5..00000000000
--- a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-102.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[border-conflict-style-102.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-103.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-conflict-style-103.xht.ini
deleted file mode 100644
index 1bf47e6123d..00000000000
--- a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-103.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[border-conflict-style-103.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-104.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-conflict-style-104.xht.ini
deleted file mode 100644
index 4228a8e88c8..00000000000
--- a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-104.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[border-conflict-style-104.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-105.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-conflict-style-105.xht.ini
deleted file mode 100644
index b1af0734d8f..00000000000
--- a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-105.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[border-conflict-style-105.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-106.xht.ini b/tests/wpt/meta/css/CSS2/borders/border-conflict-style-106.xht.ini
deleted file mode 100644
index 5c16563b761..00000000000
--- a/tests/wpt/meta/css/CSS2/borders/border-conflict-style-106.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[border-conflict-style-106.xht]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-tables/border-writing-mode-dynamic-001.html.ini b/tests/wpt/meta/css/css-tables/border-writing-mode-dynamic-001.html.ini
deleted file mode 100644
index bb496cd52d5..00000000000
--- a/tests/wpt/meta/css/css-tables/border-writing-mode-dynamic-001.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[border-writing-mode-dynamic-001.html]
- [Table borders track writing mode changes]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-tables/col_removal.html.ini b/tests/wpt/meta/css/css-tables/col_removal.html.ini
deleted file mode 100644
index 2c3f82792b8..00000000000
--- a/tests/wpt/meta/css/css-tables/col_removal.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[col_removal.html]
- [Table grid syncs after COL removal]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-tables/rules-groups.html.ini b/tests/wpt/meta/css/css-tables/rules-groups.html.ini
new file mode 100644
index 00000000000..b8e00870260
--- /dev/null
+++ b/tests/wpt/meta/css/css-tables/rules-groups.html.ini
@@ -0,0 +1,2 @@
+[rules-groups.html]
+ expected: FAIL