mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Improve fixed table layout (#35170)
If `width` is indefinite, treat the outer size as zero, instead of treating the content size as zero and then adding padding and borders. Also, we don't want a default minimum of zero to get added padding and borders, and then defeat the point baove. So just ignore minimums and maximums. That seems to roughly match what other browsers do, but as usual, the details are not interoperable, e.g. some browsers may obey min or max sizing properties in some cases. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
859cc6ab9b
commit
d5fcc5a5d5
16 changed files with 69 additions and 96 deletions
|
@ -10,7 +10,7 @@ use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
|||
use app_units::Au;
|
||||
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
|
||||
use style::values::computed::{
|
||||
CSSPixelLength, LengthPercentage, MaxSize as StyleMaxSize, Size as StyleSize,
|
||||
CSSPixelLength, LengthPercentage, MaxSize as StyleMaxSize, Percentage, Size as StyleSize,
|
||||
};
|
||||
use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
|
||||
use style::Zero;
|
||||
|
@ -672,11 +672,6 @@ impl<T> Default for Size<T> {
|
|||
}
|
||||
|
||||
impl<T> Size<T> {
|
||||
#[inline]
|
||||
pub(crate) fn is_numeric(&self) -> bool {
|
||||
matches!(self, Self::Numeric(_))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn is_initial(&self) -> bool {
|
||||
matches!(self, Self::Initial)
|
||||
|
@ -747,6 +742,14 @@ impl From<StyleMaxSize> for Size<LengthPercentage> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Size<LengthPercentage> {
|
||||
#[inline]
|
||||
pub(crate) fn to_percentage(&self) -> Option<Percentage> {
|
||||
self.to_numeric()
|
||||
.and_then(|length_percentage| length_percentage.to_percentage())
|
||||
}
|
||||
}
|
||||
|
||||
impl LogicalVec2<Size<LengthPercentage>> {
|
||||
pub(crate) fn maybe_percentages_relative_to_basis(
|
||||
&self,
|
||||
|
|
|
@ -216,6 +216,7 @@ pub(crate) struct TableLayout<'a> {
|
|||
basis_for_cell_padding_percentage: Au,
|
||||
/// Information about collapsed borders.
|
||||
collapsed_borders: Option<CollapsedBorders>,
|
||||
is_in_fixed_mode: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -239,6 +240,15 @@ impl Zero for CellOrTrackMeasure {
|
|||
|
||||
impl<'a> TableLayout<'a> {
|
||||
fn new(table: &'a Table) -> TableLayout<'a> {
|
||||
// It's not clear whether `inline-size: stretch` allows fixed table mode or not,
|
||||
// we align with Gecko and Blink.
|
||||
// <https://github.com/w3c/csswg-drafts/issues/10937>.
|
||||
let is_in_fixed_mode = table.style.get_table().clone_table_layout() ==
|
||||
TableLayoutMode::Fixed &&
|
||||
!matches!(
|
||||
table.style.box_size(table.style.writing_mode).inline,
|
||||
Size::Initial | Size::MaxContent
|
||||
);
|
||||
Self {
|
||||
table,
|
||||
pbm: PaddingBorderMargin::zero(),
|
||||
|
@ -254,6 +264,7 @@ impl<'a> TableLayout<'a> {
|
|||
cells_laid_out: Vec::new(),
|
||||
basis_for_cell_padding_percentage: Au::zero(),
|
||||
collapsed_borders: None,
|
||||
is_in_fixed_mode,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,15 +275,6 @@ impl<'a> TableLayout<'a> {
|
|||
layout_context: &LayoutContext,
|
||||
writing_mode: WritingMode,
|
||||
) {
|
||||
// It's not clear whether `inline-size: stretch` allows fixed table mode or not,
|
||||
// we align with Gecko and Blink.
|
||||
// <https://github.com/w3c/csswg-drafts/issues/10937>.
|
||||
let is_in_fixed_mode = self.table.style.get_table().clone_table_layout() ==
|
||||
TableLayoutMode::Fixed &&
|
||||
!matches!(
|
||||
self.table.style.box_size(writing_mode).inline,
|
||||
Size::Initial | Size::MaxContent
|
||||
);
|
||||
let row_measures = vec![LogicalVec2::zero(); self.table.size.width];
|
||||
self.cell_measures = vec![row_measures; self.table.size.height];
|
||||
|
||||
|
@ -305,46 +307,39 @@ impl<'a> TableLayout<'a> {
|
|||
preferred: preferred_size,
|
||||
min: min_size,
|
||||
max: max_size,
|
||||
inline_preferred_size_is_auto,
|
||||
percentage: percentage_size,
|
||||
} = CellOrColumnOuterSizes::new(
|
||||
&cell.base.style,
|
||||
writing_mode,
|
||||
&padding_border_sums,
|
||||
self.is_in_fixed_mode,
|
||||
);
|
||||
|
||||
// <https://drafts.csswg.org/css-tables/#in-fixed-mode>
|
||||
// > When a table-root is laid out in fixed mode, the content of its table-cells is ignored
|
||||
// > for the purpose of width computation, the aggregation algorithm for column sizing considers
|
||||
// > only table-cells belonging to the first row track
|
||||
let inline_measure = if is_in_fixed_mode && row_index > 0 {
|
||||
CellOrTrackMeasure::zero()
|
||||
let inline_measure = if self.is_in_fixed_mode {
|
||||
if row_index > 0 {
|
||||
CellOrTrackMeasure::zero()
|
||||
} else {
|
||||
CellOrTrackMeasure {
|
||||
content_sizes: preferred_size.inline.into(),
|
||||
percentage: percentage_size.inline,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut inline_content_sizes =
|
||||
cell.inline_content_sizes(layout_context, is_in_fixed_mode);
|
||||
inline_content_sizes.min_content += padding_border_sums.inline;
|
||||
inline_content_sizes.max_content += padding_border_sums.inline;
|
||||
let inline_content_sizes = cell.inline_content_sizes(layout_context) +
|
||||
padding_border_sums.inline.into();
|
||||
assert!(
|
||||
inline_content_sizes.max_content >= inline_content_sizes.min_content,
|
||||
"the max-content size should never be smaller than the min-content size"
|
||||
);
|
||||
|
||||
// These formulas differ from the spec, but seem to match Gecko and Blink.
|
||||
let outer_min_content_width = if is_in_fixed_mode {
|
||||
if inline_preferred_size_is_auto {
|
||||
// This is an outer size, but we deliberately ignore borders and padding.
|
||||
// This is like allowing the content-box width to be negative.
|
||||
Au::zero()
|
||||
} else {
|
||||
preferred_size
|
||||
.inline
|
||||
.clamp_between_extremums(min_size.inline, max_size.inline)
|
||||
}
|
||||
} else {
|
||||
inline_content_sizes
|
||||
.min_content
|
||||
.clamp_between_extremums(min_size.inline, max_size.inline)
|
||||
};
|
||||
let outer_min_content_width = inline_content_sizes
|
||||
.min_content
|
||||
.clamp_between_extremums(min_size.inline, max_size.inline);
|
||||
let outer_max_content_width = if self.columns[column_index].constrained {
|
||||
inline_content_sizes
|
||||
.min_content
|
||||
|
@ -494,9 +489,11 @@ impl<'a> TableLayout<'a> {
|
|||
for column_index in 0..self.table.size.width {
|
||||
let column = &mut self.columns[column_index];
|
||||
|
||||
let column_measure = self
|
||||
.table
|
||||
.get_column_measure_for_column_at_index(writing_mode, column_index);
|
||||
let column_measure = self.table.get_column_measure_for_column_at_index(
|
||||
writing_mode,
|
||||
column_index,
|
||||
self.is_in_fixed_mode,
|
||||
);
|
||||
column.content_sizes = column_measure.content_sizes;
|
||||
column.percentage = column_measure.percentage;
|
||||
|
||||
|
@ -2563,6 +2560,7 @@ impl Table {
|
|||
&self,
|
||||
writing_mode: WritingMode,
|
||||
column_index: usize,
|
||||
is_in_fixed_mode: bool,
|
||||
) -> CellOrTrackMeasure {
|
||||
let column = match self.columns.get(column_index) {
|
||||
Some(column) => column,
|
||||
|
@ -2574,8 +2572,12 @@ impl Table {
|
|||
min: min_size,
|
||||
max: max_size,
|
||||
percentage: percentage_size,
|
||||
..
|
||||
} = CellOrColumnOuterSizes::new(&column.style, writing_mode, &Default::default());
|
||||
} = CellOrColumnOuterSizes::new(
|
||||
&column.style,
|
||||
writing_mode,
|
||||
&Default::default(),
|
||||
is_in_fixed_mode,
|
||||
);
|
||||
|
||||
CellOrTrackMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
|
@ -2772,15 +2774,7 @@ impl TableSlotCell {
|
|||
}
|
||||
}
|
||||
|
||||
fn inline_content_sizes(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
is_in_fixed_mode: bool,
|
||||
) -> ContentSizes {
|
||||
if is_in_fixed_mode {
|
||||
return ContentSizes::zero();
|
||||
};
|
||||
|
||||
fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes {
|
||||
let constraint_space = ConstraintSpace::new_for_style_and_ratio(
|
||||
&self.base.style,
|
||||
None, /* TODO: support preferred aspect ratios on non-replaced boxes */
|
||||
|
@ -2883,20 +2877,15 @@ fn get_size_percentage_contribution(
|
|||
// > min(percentage width, percentage max-width).
|
||||
// > If the computed values are not percentages, then 0% is used for width, and an
|
||||
// > infinite percentage is used for max-width.
|
||||
let get_contribution_for_axis =
|
||||
|size: &Size<ComputedLengthPercentage>, max_size: &Size<ComputedLengthPercentage>| {
|
||||
let size_percentage = size
|
||||
.to_numeric()
|
||||
.and_then(|length_percentage| length_percentage.to_percentage());
|
||||
let max_size_percentage = max_size
|
||||
.to_numeric()
|
||||
.and_then(|length_percentage| length_percentage.to_percentage());
|
||||
max_two_optional_percentages(size_percentage, max_size_percentage)
|
||||
};
|
||||
|
||||
LogicalVec2 {
|
||||
inline: get_contribution_for_axis(&size.inline, &max_size.inline),
|
||||
block: get_contribution_for_axis(&size.block, &max_size.block),
|
||||
inline: max_two_optional_percentages(
|
||||
size.inline.to_percentage(),
|
||||
max_size.inline.to_percentage(),
|
||||
),
|
||||
block: max_two_optional_percentages(
|
||||
size.block.to_percentage(),
|
||||
max_size.block.to_percentage(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2905,7 +2894,6 @@ struct CellOrColumnOuterSizes {
|
|||
preferred: LogicalVec2<Au>,
|
||||
max: LogicalVec2<Option<Au>>,
|
||||
percentage: LogicalVec2<Option<Percentage>>,
|
||||
inline_preferred_size_is_auto: bool,
|
||||
}
|
||||
|
||||
impl CellOrColumnOuterSizes {
|
||||
|
@ -2913,6 +2901,7 @@ impl CellOrColumnOuterSizes {
|
|||
style: &Arc<ComputedValues>,
|
||||
writing_mode: WritingMode,
|
||||
padding_border_sums: &LogicalVec2<Au>,
|
||||
is_in_fixed_mode: bool,
|
||||
) -> Self {
|
||||
let box_sizing = style.get_position().box_sizing;
|
||||
let outer_size = |size: LogicalVec2<Au>| match box_sizing {
|
||||
|
@ -2923,7 +2912,7 @@ impl CellOrColumnOuterSizes {
|
|||
},
|
||||
};
|
||||
|
||||
let outer_size_for_max = |size: LogicalVec2<Option<Au>>| match box_sizing {
|
||||
let outer_option_size = |size: LogicalVec2<Option<Au>>| match box_sizing {
|
||||
BoxSizing::ContentBox => size.map_inline_and_block_axes(
|
||||
|inline| inline.map(|inline| inline + padding_border_sums.inline),
|
||||
|block| block.map(|block| block + padding_border_sums.block),
|
||||
|
@ -2943,14 +2932,23 @@ impl CellOrColumnOuterSizes {
|
|||
};
|
||||
|
||||
let size = style.box_size(writing_mode);
|
||||
if is_in_fixed_mode {
|
||||
return Self {
|
||||
percentage: size.map(|v| v.to_percentage()),
|
||||
preferred: outer_option_size(size.map(get_size_for_axis))
|
||||
.map(|v| v.unwrap_or_default()),
|
||||
min: LogicalVec2::default(),
|
||||
max: LogicalVec2::default(),
|
||||
};
|
||||
}
|
||||
|
||||
let min_size = style.min_box_size(writing_mode);
|
||||
let max_size = style.max_box_size(writing_mode);
|
||||
|
||||
Self {
|
||||
min: outer_size(min_size.map(|v| get_size_for_axis(v).unwrap_or_default())),
|
||||
preferred: outer_size(size.map(|v| get_size_for_axis(v).unwrap_or_default())),
|
||||
max: outer_size_for_max(max_size.map(get_size_for_axis)),
|
||||
inline_preferred_size_is_auto: !size.inline.is_numeric(),
|
||||
max: outer_option_size(max_size.map(get_size_for_axis)),
|
||||
percentage: get_size_percentage_contribution(&size, &max_size),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[border-applies-to-006.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[border-color-applies-to-006.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003e07.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003e08.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003e09.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003e10.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003e11.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003e12.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003f03.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003f04.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003f05.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003f06.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003f07.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixed-table-layout-003f08.xht]
|
||||
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue