mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
layout: Add initial support for row height distribution (#31421)
This change adds a version of row height distribution that follows the distribtuion algorithm used for tables in Blink's LayoutNG. This is just an intermediate step toward implementing a distribution algorithm for both rows and columns more similar to Layout NG. The CSS Table 3 specification is often wrong with regard to web compatability, which is why we have abandoned it in favor of the Layout NG algorithm for row height distribution. this work. Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
31cfaf290d
commit
127aa657c2
49 changed files with 591 additions and 302 deletions
|
@ -24,13 +24,6 @@ pub(crate) struct ContentSizes {
|
|||
|
||||
/// <https://drafts.csswg.org/css-sizing/#intrinsic-sizes>
|
||||
impl ContentSizes {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
min_content: Au::zero(),
|
||||
max_content: Au::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(&self, f: impl Fn(Au) -> Au) -> Self {
|
||||
Self {
|
||||
min_content: f(self.min_content),
|
||||
|
@ -57,6 +50,19 @@ impl ContentSizes {
|
|||
}
|
||||
}
|
||||
|
||||
impl Zero for ContentSizes {
|
||||
fn zero() -> Self {
|
||||
Self {
|
||||
min_content: Au::zero(),
|
||||
max_content: Au::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.min_content.is_zero() && self.max_content.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for ContentSizes {
|
||||
type Output = Self;
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* 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 std::ops::Range;
|
||||
|
||||
use app_units::{Au, MAX_AU};
|
||||
use log::warn;
|
||||
use servo_arc::Arc;
|
||||
|
@ -52,17 +54,32 @@ impl CellLayout {
|
|||
}
|
||||
}
|
||||
|
||||
/// Information stored during the layout of rows.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct RowLayout {
|
||||
constrained: bool,
|
||||
has_cell_with_span_greater_than_one: bool,
|
||||
percent: Percentage,
|
||||
}
|
||||
|
||||
/// Information stored during the layout of columns.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct ColumnLayout {
|
||||
constrained: bool,
|
||||
has_originating_cells: bool,
|
||||
}
|
||||
|
||||
/// A helper struct that performs the layout of the box tree version
|
||||
/// of a table into the fragment tree version. This implements
|
||||
/// <https://drafts.csswg.org/css-tables/#table-layout-algorithm>
|
||||
struct TableLayout<'a> {
|
||||
table: &'a Table,
|
||||
pbm: PaddingBorderMargin,
|
||||
column_constrainedness: Vec<bool>,
|
||||
column_has_originating_cell: Vec<bool>,
|
||||
cell_measures: Vec<Vec<CellOrColumnMeasure>>,
|
||||
rows: Vec<RowLayout>,
|
||||
columns: Vec<ColumnLayout>,
|
||||
cell_measures: Vec<Vec<LogicalVec2<CellOrTrackMeasure>>>,
|
||||
assignable_width: Au,
|
||||
column_measures: Vec<CellOrColumnMeasure>,
|
||||
column_measures: Vec<CellOrTrackMeasure>,
|
||||
distributed_column_widths: Vec<Au>,
|
||||
row_sizes: Vec<Au>,
|
||||
row_baselines: Vec<Au>,
|
||||
|
@ -71,18 +88,22 @@ struct TableLayout<'a> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct CellOrColumnMeasure {
|
||||
struct CellOrTrackMeasure {
|
||||
content_sizes: ContentSizes,
|
||||
percentage_width: Percentage,
|
||||
percentage: Percentage,
|
||||
}
|
||||
|
||||
impl CellOrColumnMeasure {
|
||||
impl Zero for CellOrTrackMeasure {
|
||||
fn zero() -> Self {
|
||||
Self {
|
||||
content_sizes: ContentSizes::zero(),
|
||||
percentage_width: Percentage(0.),
|
||||
percentage: Percentage(0.),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.content_sizes.is_zero() && self.percentage.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TableLayout<'a> {
|
||||
|
@ -90,8 +111,8 @@ impl<'a> TableLayout<'a> {
|
|||
Self {
|
||||
table,
|
||||
pbm: PaddingBorderMargin::zero(),
|
||||
column_constrainedness: Vec::new(),
|
||||
column_has_originating_cell: Vec::new(),
|
||||
rows: Vec::new(),
|
||||
columns: Vec::new(),
|
||||
cell_measures: Vec::new(),
|
||||
assignable_width: Au::zero(),
|
||||
column_measures: Vec::new(),
|
||||
|
@ -112,13 +133,15 @@ impl<'a> TableLayout<'a> {
|
|||
containing_block: &ContainingBlock,
|
||||
) {
|
||||
let writing_mode = containing_block.style.writing_mode;
|
||||
self.compute_column_constrainedness_and_has_originating_cells(writing_mode);
|
||||
self.compute_track_constrainedness_and_has_originating_cells(writing_mode);
|
||||
self.compute_cell_measures(layout_context, containing_block);
|
||||
self.compute_column_measures(containing_block.style.writing_mode);
|
||||
self.compute_table_width(containing_block);
|
||||
self.distributed_column_widths = self.distribute_width_to_columns();
|
||||
self.do_row_layout_first_pass(layout_context, containing_block, positioning_context);
|
||||
self.distribute_height_to_rows();
|
||||
|
||||
self.layout_cells_in_row(layout_context, containing_block, positioning_context);
|
||||
let first_layout_row_heights = self.do_first_row_layout(writing_mode);
|
||||
self.compute_table_height_and_final_row_heights(first_layout_row_heights, containing_block);
|
||||
}
|
||||
|
||||
/// This is an implementation of *Computing Cell Measures* from
|
||||
|
@ -128,10 +151,11 @@ impl<'a> TableLayout<'a> {
|
|||
layout_context: &LayoutContext,
|
||||
containing_block: &ContainingBlock,
|
||||
) {
|
||||
let row_measures = vec![LogicalVec2::zero(); self.table.size.width];
|
||||
self.cell_measures = vec![row_measures; self.table.size.height];
|
||||
|
||||
let writing_mode = containing_block.style.writing_mode;
|
||||
for row_index in 0..self.table.size.height {
|
||||
let mut row_measures = vec![CellOrColumnMeasure::zero(); self.table.size.width];
|
||||
|
||||
for column_index in 0..self.table.size.width {
|
||||
let cell = match self.table.slots[row_index][column_index] {
|
||||
TableSlot::Cell(ref cell) => cell,
|
||||
|
@ -139,7 +163,7 @@ impl<'a> TableLayout<'a> {
|
|||
};
|
||||
|
||||
let (size, min_size, max_size) = get_sizes_from_style(&cell.style, writing_mode);
|
||||
let content_sizes = cell
|
||||
let inline_content_sizes = cell
|
||||
.contents
|
||||
.contents
|
||||
.inline_content_sizes(layout_context, writing_mode);
|
||||
|
@ -148,16 +172,17 @@ impl<'a> TableLayout<'a> {
|
|||
|
||||
// > The outer min-content width of a table-cell is max(min-width, min-content width)
|
||||
// > adjusted by the cell intrinsic offsets.
|
||||
let mut outer_min_content_width = content_sizes.min_content.max(min_size.inline);
|
||||
let mut outer_max_content_width = if !self.column_constrainedness[column_index] {
|
||||
let mut outer_min_content_width =
|
||||
inline_content_sizes.min_content.max(min_size.inline);
|
||||
let mut outer_max_content_width = if !self.columns[column_index].constrained {
|
||||
// > The outer max-content width of a table-cell in a non-constrained column is
|
||||
// > max(min-width, width, min-content width, min(max-width, max-content width))
|
||||
// > adjusted by the cell intrinsic offsets.
|
||||
min_size
|
||||
.inline
|
||||
.max(size.inline)
|
||||
.max(content_sizes.min_content)
|
||||
.max(max_size.inline.min(content_sizes.max_content))
|
||||
.max(inline_content_sizes.min_content)
|
||||
.max(max_size.inline.min(inline_content_sizes.max_content))
|
||||
} else {
|
||||
// > The outer max-content width of a table-cell in a constrained column is
|
||||
// > max(min-width, width, min-content width, min(max-width, width)) adjusted by the
|
||||
|
@ -165,30 +190,63 @@ impl<'a> TableLayout<'a> {
|
|||
min_size
|
||||
.inline
|
||||
.max(size.inline)
|
||||
.max(content_sizes.min_content)
|
||||
.max(inline_content_sizes.min_content)
|
||||
.max(max_size.inline.min(size.inline))
|
||||
};
|
||||
|
||||
let inline_padding_border_sum = Au::from(
|
||||
cell.style
|
||||
.padding(writing_mode)
|
||||
.percentages_relative_to(Length::zero())
|
||||
.inline_sum() +
|
||||
cell.style.border_width(writing_mode).inline_sum(),
|
||||
);
|
||||
let padding = cell
|
||||
.style
|
||||
.padding(writing_mode)
|
||||
.percentages_relative_to(Length::zero());
|
||||
let border = cell.style.border_width(writing_mode);
|
||||
|
||||
let inline_padding_border_sum =
|
||||
Au::from(padding.inline_sum() + border.inline_sum());
|
||||
outer_min_content_width += inline_padding_border_sum;
|
||||
outer_max_content_width += inline_padding_border_sum;
|
||||
|
||||
row_measures[column_index] = CellOrColumnMeasure {
|
||||
let inline_measure = CellOrTrackMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
min_content: outer_min_content_width,
|
||||
max_content: outer_max_content_width,
|
||||
},
|
||||
percentage_width: percentage_contribution.inline,
|
||||
percentage: percentage_contribution.inline,
|
||||
};
|
||||
|
||||
// These calculations do not take into account the `min-content` and `max-content`
|
||||
// sizes. These sizes are incorporated after the first row layout pass, when the
|
||||
// block size of the layout is known.
|
||||
//
|
||||
// TODO: Is it correct to use the block size as the minimum of the `outer min
|
||||
// content height` here? The specification doesn't mention this, but it does cause
|
||||
// a test to pass.
|
||||
let mut outer_min_content_height = min_size.block.max(size.block);
|
||||
let mut outer_max_content_height = if !self.rows[row_index].constrained {
|
||||
min_size.block.max(size.block)
|
||||
} else {
|
||||
min_size
|
||||
.block
|
||||
.max(size.block)
|
||||
.max(max_size.block.min(size.block))
|
||||
};
|
||||
|
||||
let block_padding_border_sum = Au::from(padding.block_sum() + border.block_sum());
|
||||
outer_min_content_height += block_padding_border_sum;
|
||||
outer_max_content_height += block_padding_border_sum;
|
||||
|
||||
let block_measure = CellOrTrackMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
min_content: outer_min_content_height,
|
||||
max_content: outer_max_content_height,
|
||||
},
|
||||
percentage: percentage_contribution.block,
|
||||
};
|
||||
|
||||
self.cell_measures[row_index][column_index] = LogicalVec2 {
|
||||
inline: inline_measure,
|
||||
block: block_measure,
|
||||
};
|
||||
}
|
||||
|
||||
self.cell_measures.push(row_measures);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -197,14 +255,48 @@ impl<'a> TableLayout<'a> {
|
|||
/// > A column is constrained if its corresponding table-column-group (if any), its
|
||||
/// > corresponding table-column (if any), or any of the cells spanning only that
|
||||
/// > column has a computed width that is not "auto", and is not a percentage.
|
||||
fn compute_column_constrainedness_and_has_originating_cells(
|
||||
fn compute_track_constrainedness_and_has_originating_cells(
|
||||
&mut self,
|
||||
writing_mode: WritingMode,
|
||||
) {
|
||||
for column_index in 0..self.table.size.width {
|
||||
let mut column_constrained = false;
|
||||
let mut column_has_originating_cell = false;
|
||||
self.rows = vec![RowLayout::default(); self.table.size.height];
|
||||
self.columns = vec![ColumnLayout::default(); self.table.size.width];
|
||||
|
||||
for column_index in 0..self.table.size.width {
|
||||
if let Some(column) = self.table.columns.get(column_index) {
|
||||
if !column.style.box_size(writing_mode).inline.is_auto() {
|
||||
self.columns[column_index].constrained = true;
|
||||
continue;
|
||||
}
|
||||
if let Some(column_group_index) = column.group_index {
|
||||
let column_group = &self.table.column_groups[column_group_index];
|
||||
if !column_group.style.box_size(writing_mode).inline.is_auto() {
|
||||
self.columns[column_index].constrained = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
self.columns[column_index].constrained = false;
|
||||
}
|
||||
}
|
||||
|
||||
for row_index in 0..self.table.size.height {
|
||||
if let Some(row) = self.table.rows.get(row_index) {
|
||||
if !row.style.box_size(writing_mode).block.is_auto() {
|
||||
self.rows[row_index].constrained = true;
|
||||
continue;
|
||||
}
|
||||
if let Some(row_group_index) = row.group_index {
|
||||
let row_group = &self.table.row_groups[row_group_index];
|
||||
if !row_group.style.box_size(writing_mode).block.is_auto() {
|
||||
self.rows[row_index].constrained = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.rows[row_index].constrained = false;
|
||||
}
|
||||
|
||||
for column_index in 0..self.table.size.width {
|
||||
for row_index in 0..self.table.size.height {
|
||||
let coords = TableSlotCoordinates::new(column_index, row_index);
|
||||
let cell_constrained = match self.table.resolve_first_cell(coords) {
|
||||
|
@ -217,13 +309,20 @@ impl<'a> TableLayout<'a> {
|
|||
.unwrap_or(false),
|
||||
_ => false,
|
||||
};
|
||||
column_has_originating_cell = column_has_originating_cell ||
|
||||
|
||||
let rowspan_greater_than_1 = match self.table.slots[row_index][column_index] {
|
||||
TableSlot::Cell(ref cell) => cell.rowspan > 1,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
self.rows[row_index].has_cell_with_span_greater_than_one |= rowspan_greater_than_1;
|
||||
self.rows[row_index].constrained |= cell_constrained;
|
||||
|
||||
let has_originating_cell =
|
||||
matches!(self.table.get_slot(coords), Some(TableSlot::Cell(_)));
|
||||
column_constrained = column_constrained || cell_constrained;
|
||||
self.columns[column_index].has_originating_cells |= has_originating_cell;
|
||||
self.columns[column_index].constrained |= cell_constrained;
|
||||
}
|
||||
self.column_constrainedness.push(column_constrained);
|
||||
self.column_has_originating_cell
|
||||
.push(column_has_originating_cell);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,16 +380,12 @@ impl<'a> TableLayout<'a> {
|
|||
|
||||
// This takes the max of `min_content`, `max_content`, and
|
||||
// intrinsic percentage width as described above.
|
||||
let cell_measure = &self.cell_measures[row_index][column_index];
|
||||
let cell_measure = &self.cell_measures[row_index][column_index].inline;
|
||||
column_measure
|
||||
.content_sizes
|
||||
.max_assign(cell_measure.content_sizes);
|
||||
column_measure.percentage_width = Percentage(
|
||||
column_measure
|
||||
.percentage_width
|
||||
.0
|
||||
.max(cell_measure.percentage_width.0),
|
||||
);
|
||||
column_measure.percentage =
|
||||
Percentage(column_measure.percentage.0.max(cell_measure.percentage.0));
|
||||
}
|
||||
|
||||
column_measures.push(column_measure);
|
||||
|
@ -314,11 +409,11 @@ impl<'a> TableLayout<'a> {
|
|||
for column_index in 0..self.table.size.width {
|
||||
let column_measure = &mut column_measures[column_index];
|
||||
let final_intrinsic_percentage_width = column_measure
|
||||
.percentage_width
|
||||
.percentage
|
||||
.0
|
||||
.min(100. - total_intrinsic_percentage_width);
|
||||
total_intrinsic_percentage_width += final_intrinsic_percentage_width;
|
||||
column_measure.percentage_width = Percentage(final_intrinsic_percentage_width);
|
||||
column_measure.percentage = Percentage(final_intrinsic_percentage_width);
|
||||
}
|
||||
|
||||
self.column_measures = column_measures;
|
||||
|
@ -327,8 +422,8 @@ impl<'a> TableLayout<'a> {
|
|||
fn compute_content_sizes_for_columns_with_span_up_to_n(
|
||||
&self,
|
||||
n: usize,
|
||||
old_column_measures: &[CellOrColumnMeasure],
|
||||
) -> (usize, Vec<CellOrColumnMeasure>) {
|
||||
old_column_measures: &[CellOrTrackMeasure],
|
||||
) -> (usize, Vec<CellOrTrackMeasure>) {
|
||||
let mut next_span_n = usize::MAX;
|
||||
let mut new_content_sizes_for_columns = Vec::new();
|
||||
let border_spacing = self.table.border_spacing();
|
||||
|
@ -354,7 +449,8 @@ impl<'a> TableLayout<'a> {
|
|||
_ => continue,
|
||||
};
|
||||
|
||||
let cell_measures = &self.cell_measures[resolved_coords.y][resolved_coords.x];
|
||||
let cell_measures =
|
||||
&self.cell_measures[resolved_coords.y][resolved_coords.x].inline;
|
||||
let cell_inline_content_sizes = cell_measures.content_sizes;
|
||||
|
||||
let columns_spanned = resolved_coords.x..resolved_coords.x + cell.colspan;
|
||||
|
@ -485,9 +581,7 @@ impl<'a> TableLayout<'a> {
|
|||
// > Otherwise, it is the largest of the contributions of the cells in the column
|
||||
// > whose colSpan is N, where the contribution of a cell is the result of taking
|
||||
// > the following steps:
|
||||
if old_column_measure.percentage_width.0 <= 0. &&
|
||||
cell_measures.percentage_width.0 != 0.
|
||||
{
|
||||
if old_column_measure.percentage.0 <= 0. && cell_measures.percentage.0 != 0. {
|
||||
// > 1. Start with the percentage contribution of the cell.
|
||||
// > 2. Subtract the intrinsic percentage width of the column based on cells
|
||||
// > of span up to N-1 of all columns that the cell spans. If this gives a
|
||||
|
@ -496,13 +590,13 @@ impl<'a> TableLayout<'a> {
|
|||
let other_column_percentages_sum =
|
||||
(columns_spanned).fold(0., |sum, spanned_column_index| {
|
||||
let spanned_column_percentage =
|
||||
old_column_measures[spanned_column_index].percentage_width;
|
||||
old_column_measures[spanned_column_index].percentage;
|
||||
if spanned_column_percentage.0 == 0. {
|
||||
spanned_columns_with_zero += 1;
|
||||
}
|
||||
sum + spanned_column_percentage.0
|
||||
});
|
||||
let step_2 = (cell_measures.percentage_width -
|
||||
let step_2 = (cell_measures.percentage -
|
||||
Percentage(other_column_percentages_sum))
|
||||
.clamp_to_non_negative();
|
||||
|
||||
|
@ -521,9 +615,9 @@ impl<'a> TableLayout<'a> {
|
|||
Percentage(new_column_intrinsic_percentage_width.0.max(step_3));
|
||||
}
|
||||
}
|
||||
new_content_sizes_for_columns.push(CellOrColumnMeasure {
|
||||
new_content_sizes_for_columns.push(CellOrTrackMeasure {
|
||||
content_sizes: new_column_content_sizes,
|
||||
percentage_width: new_column_intrinsic_percentage_width,
|
||||
percentage: new_column_intrinsic_percentage_width,
|
||||
});
|
||||
}
|
||||
(next_span_n, new_content_sizes_for_columns)
|
||||
|
@ -636,16 +730,14 @@ impl<'a> TableLayout<'a> {
|
|||
let column_measure = &self.column_measures[column_idx];
|
||||
let min_content_width = column_measure.content_sizes.min_content;
|
||||
let max_content_width = column_measure.content_sizes.max_content;
|
||||
let constrained = self.column_constrainedness[column_idx];
|
||||
let constrained = self.columns[column_idx].constrained;
|
||||
|
||||
let (
|
||||
min_content_percentage_sizing_guess,
|
||||
min_content_specified_sizing_guess,
|
||||
max_content_sizing_guess,
|
||||
) = if !column_measure.percentage_width.is_zero() {
|
||||
let resolved = self
|
||||
.assignable_width
|
||||
.scale_by(column_measure.percentage_width.0);
|
||||
) = if !column_measure.percentage.is_zero() {
|
||||
let resolved = self.assignable_width.scale_by(column_measure.percentage.0);
|
||||
let percent_guess = min_content_width.max(resolved);
|
||||
(percent_guess, percent_guess, percent_guess)
|
||||
} else if constrained {
|
||||
|
@ -746,11 +838,11 @@ impl<'a> TableLayout<'a> {
|
|||
let extra_inline_size = self.assignable_width - column_sizes_sum;
|
||||
|
||||
let has_originating_cells =
|
||||
|column_index: &usize| self.column_has_originating_cell[*column_index];
|
||||
let is_constrained = |column_index: &usize| self.column_constrainedness[*column_index];
|
||||
|column_index: &usize| self.columns[*column_index].has_originating_cells;
|
||||
let is_constrained = |column_index: &usize| self.columns[*column_index].constrained;
|
||||
let is_unconstrained = |column_index: &usize| !is_constrained(column_index);
|
||||
let has_percent_greater_than_zero =
|
||||
|column_index: &usize| self.column_measures[*column_index].percentage_width.0 > 0.;
|
||||
|column_index: &usize| self.column_measures[*column_index].percentage.0 > 0.;
|
||||
let has_percent_zero = |column_index: &usize| !has_percent_greater_than_zero(column_index);
|
||||
let has_max_content = |column_index: &usize| {
|
||||
self.column_measures[*column_index]
|
||||
|
@ -846,13 +938,12 @@ impl<'a> TableLayout<'a> {
|
|||
let columns_with_percentage = all_columns.clone().filter(has_percent_greater_than_zero);
|
||||
let total_percent = columns_with_percentage
|
||||
.clone()
|
||||
.map(|column_index| self.column_measures[column_index].percentage_width.0)
|
||||
.map(|column_index| self.column_measures[column_index].percentage.0)
|
||||
.sum::<f32>();
|
||||
if total_percent > 0. {
|
||||
for column_index in columns_with_percentage {
|
||||
column_sizes[column_index] += extra_inline_size.scale_by(
|
||||
self.column_measures[column_index].percentage_width.0 / total_percent,
|
||||
);
|
||||
column_sizes[column_index] += extra_inline_size
|
||||
.scale_by(self.column_measures[column_index].percentage.0 / total_percent);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -882,7 +973,7 @@ impl<'a> TableLayout<'a> {
|
|||
|
||||
/// This is an implementation of *Row layout (first pass)* from
|
||||
/// <https://drafts.csswg.org/css-tables/#row-layout>.
|
||||
fn do_row_layout_first_pass(
|
||||
fn layout_cells_in_row(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
containing_block: &ContainingBlock,
|
||||
|
@ -930,86 +1021,349 @@ impl<'a> TableLayout<'a> {
|
|||
&mut positioning_context,
|
||||
&containing_block_for_children,
|
||||
);
|
||||
|
||||
let content_size_from_layout = ContentSizes {
|
||||
min_content: layout.content_block_size,
|
||||
max_content: layout.content_block_size,
|
||||
};
|
||||
self.cell_measures[row_index][column_index]
|
||||
.block
|
||||
.content_sizes
|
||||
.max_assign(content_size_from_layout);
|
||||
|
||||
cells_laid_out_row.push(Some(CellLayout {
|
||||
layout,
|
||||
padding,
|
||||
border,
|
||||
positioning_context,
|
||||
}))
|
||||
}));
|
||||
}
|
||||
self.cells_laid_out.push(cells_laid_out_row);
|
||||
}
|
||||
}
|
||||
|
||||
fn distribute_height_to_rows(&mut self) {
|
||||
/// Do the first layout of a table row, after laying out the cells themselves. This is
|
||||
/// more or less and implementation of <https://drafts.csswg.org/css-tables/#row-layout>.
|
||||
fn do_first_row_layout(&mut self, writing_mode: WritingMode) -> Vec<Au> {
|
||||
let mut row_sizes = (0..self.table.size.height)
|
||||
.map(|row_index| {
|
||||
let (mut max_ascent, mut max_descent, mut max_row_height) =
|
||||
(Au::zero(), Au::zero(), Au::zero());
|
||||
|
||||
for column_index in 0..self.table.size.width {
|
||||
let cell = match self.table.slots[row_index][column_index] {
|
||||
TableSlot::Cell(ref cell) => cell,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let layout = match self.cells_laid_out[row_index][column_index] {
|
||||
Some(ref layout) => layout,
|
||||
None => {
|
||||
warn!(
|
||||
"Did not find a layout at a slot index with an originating cell."
|
||||
);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
let outer_block_size = layout.outer_block_size();
|
||||
if cell.rowspan == 1 {
|
||||
max_row_height = max_row_height.max(outer_block_size);
|
||||
}
|
||||
|
||||
if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline {
|
||||
let ascent = layout.ascent();
|
||||
let border_padding_start =
|
||||
layout.border.block_start + layout.padding.block_start;
|
||||
let border_padding_end = layout.border.block_end + layout.padding.block_end;
|
||||
max_ascent = max_ascent.max(ascent + border_padding_start.into());
|
||||
|
||||
// Only take into account the descent of this cell if doesn't span
|
||||
// rows. The descent portion of the cell in cells that do span rows
|
||||
// may extend into other rows.
|
||||
if cell.rowspan == 1 {
|
||||
max_descent = max_descent.max(
|
||||
layout.layout.content_block_size - ascent +
|
||||
border_padding_end.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.row_baselines.push(max_ascent);
|
||||
max_row_height.max(max_ascent + max_descent)
|
||||
})
|
||||
.collect();
|
||||
self.calculate_row_sizes_after_first_layout(&mut row_sizes, writing_mode);
|
||||
row_sizes
|
||||
}
|
||||
|
||||
/// After doing layout of table rows, calculate final row size and distribute space across
|
||||
/// rowspanned cells. This follows the implementation of LayoutNG and the priority
|
||||
/// agorithm described at <https://github.com/w3c/csswg-drafts/issues/4418>.
|
||||
fn calculate_row_sizes_after_first_layout(
|
||||
&mut self,
|
||||
row_sizes: &mut Vec<Au>,
|
||||
writing_mode: WritingMode,
|
||||
) {
|
||||
let mut cells_to_distribute = Vec::new();
|
||||
let mut total_percentage = 0.;
|
||||
for row_index in 0..self.table.size.height {
|
||||
let (mut max_ascent, mut max_descent, mut max_row_height) =
|
||||
(Au::zero(), Au::zero(), Au::zero());
|
||||
let row_measure = self
|
||||
.table
|
||||
.get_row_measure_for_row_at_index(writing_mode, row_index);
|
||||
row_sizes[row_index] = row_sizes[row_index].max(row_measure.content_sizes.min_content);
|
||||
|
||||
let mut percentage = match self.table.rows.get(row_index) {
|
||||
Some(row) => {
|
||||
get_size_percentage_contribution_from_style(&row.style, writing_mode)
|
||||
.block
|
||||
.0
|
||||
},
|
||||
None => 0.,
|
||||
};
|
||||
for column_index in 0..self.table.size.width {
|
||||
let coords = TableSlotCoordinates::new(column_index, row_index);
|
||||
let cell_percentage = self.cell_measures[row_index][column_index]
|
||||
.block
|
||||
.percentage
|
||||
.0;
|
||||
percentage = percentage.max(cell_percentage);
|
||||
|
||||
let cell_measure = &self.cell_measures[row_index][column_index].block;
|
||||
let cell = match self.table.slots[row_index][column_index] {
|
||||
TableSlot::Cell(ref cell) => cell,
|
||||
TableSlot::Spanned(ref spanned_cells) if spanned_cells[0].y != 0 => {
|
||||
let offset = spanned_cells[0];
|
||||
let origin = coords - offset;
|
||||
|
||||
// We only allocate the remaining space for the last row of the rowspanned cell.
|
||||
if let Some(TableSlot::Cell(origin_cell)) = self.table.get_slot(origin) {
|
||||
if origin_cell.rowspan != offset.y + 1 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// This is all of the rows that are spanned except this one.
|
||||
let used_block_size = (origin.y..coords.y)
|
||||
.map(|row_index| self.row_sizes[row_index])
|
||||
.fold(Au::zero(), |sum, size| sum + size);
|
||||
if let Some(layout) = &self.cells_laid_out[origin.y][origin.x] {
|
||||
max_row_height =
|
||||
max_row_height.max(layout.outer_block_size() - used_block_size);
|
||||
}
|
||||
TableSlot::Cell(ref cell) if cell.rowspan > 1 => cell,
|
||||
TableSlot::Cell(_) => {
|
||||
// If this is an originating cell, that isn't spanning, then we make sure the row is
|
||||
// at least big enough to hold the cell.
|
||||
row_sizes[row_index] =
|
||||
row_sizes[row_index].max(cell_measure.content_sizes.max_content);
|
||||
continue;
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
let layout = match self.cells_laid_out[row_index][column_index] {
|
||||
Some(ref layout) => layout,
|
||||
None => {
|
||||
warn!("Did not find a layout at a slot index with an originating cell.");
|
||||
continue;
|
||||
},
|
||||
};
|
||||
cells_to_distribute.push(RowspanToDistribute {
|
||||
coordinates: TableSlotCoordinates::new(column_index, row_index),
|
||||
cell,
|
||||
measure: cell_measure,
|
||||
});
|
||||
}
|
||||
|
||||
let outer_block_size = layout.outer_block_size();
|
||||
if cell.rowspan == 1 {
|
||||
max_row_height = max_row_height.max(outer_block_size);
|
||||
}
|
||||
self.rows[row_index].percent = Percentage(percentage.min(100. - total_percentage));
|
||||
total_percentage += self.rows[row_index].percent.0;
|
||||
}
|
||||
|
||||
if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline {
|
||||
let ascent = layout.ascent();
|
||||
let border_padding_start =
|
||||
layout.border.block_start + layout.padding.block_start;
|
||||
let border_padding_end = layout.border.block_end + layout.padding.block_end;
|
||||
max_ascent = max_ascent.max(ascent + border_padding_start.into());
|
||||
cells_to_distribute.sort_by(|a, b| {
|
||||
if a.range() == b.range() {
|
||||
return a
|
||||
.measure
|
||||
.content_sizes
|
||||
.min_content
|
||||
.cmp(&b.measure.content_sizes.min_content);
|
||||
}
|
||||
if a.fully_encloses(b) {
|
||||
return std::cmp::Ordering::Greater;
|
||||
}
|
||||
if b.fully_encloses(a) {
|
||||
return std::cmp::Ordering::Less;
|
||||
}
|
||||
a.coordinates.y.cmp(&b.coordinates.y)
|
||||
});
|
||||
|
||||
// Only take into account the descent of this cell if doesn't span
|
||||
// rows. The descent portion of the cell in cells that do span rows
|
||||
// will be allocated to the other rows that it spans.
|
||||
if cell.rowspan == 1 {
|
||||
max_descent = max_descent.max(
|
||||
layout.layout.content_block_size - ascent + border_padding_end.into(),
|
||||
);
|
||||
for rowspan_to_distribute in cells_to_distribute {
|
||||
let rows_spanned = rowspan_to_distribute.range();
|
||||
let current_rows_size: Au = rows_spanned.clone().map(|index| row_sizes[index]).sum();
|
||||
let border_spacing_spanned =
|
||||
self.table.border_spacing().block * (rows_spanned.len() - 1) as i32;
|
||||
let excess_size = (rowspan_to_distribute.measure.content_sizes.min_content -
|
||||
current_rows_size -
|
||||
border_spacing_spanned)
|
||||
.max(Au::zero());
|
||||
|
||||
self.distribute_extra_size_to_rows(
|
||||
excess_size,
|
||||
rows_spanned,
|
||||
row_sizes,
|
||||
None,
|
||||
true, /* rowspan_distribution */
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// An implementation of the same extra block size distribution algorithm used in
|
||||
/// LayoutNG and described at <https://github.com/w3c/csswg-drafts/issues/4418>.
|
||||
fn distribute_extra_size_to_rows(
|
||||
&self,
|
||||
mut excess_size: Au,
|
||||
track_range: Range<usize>,
|
||||
track_sizes: &mut Vec<Au>,
|
||||
percentage_resolution_size: Option<Au>,
|
||||
rowspan_distribution: bool,
|
||||
) {
|
||||
if excess_size.is_zero() {
|
||||
return;
|
||||
}
|
||||
|
||||
let is_constrained = |track_index: &usize| self.rows[*track_index].constrained;
|
||||
let is_unconstrained = |track_index: &usize| !is_constrained(track_index);
|
||||
let is_empty: Vec<bool> = track_sizes.iter().map(|size| size.is_zero()).collect();
|
||||
let is_not_empty = |track_index: &usize| !is_empty[*track_index];
|
||||
let other_row_that_starts_a_rowspan = |track_index: &usize| {
|
||||
*track_index != track_range.start &&
|
||||
self.rows[*track_index].has_cell_with_span_greater_than_one
|
||||
};
|
||||
|
||||
// If we have a table height (not during rowspan distribution), first distribute to rows
|
||||
// that have percentage sizes proportionally to the size missing to reach the percentage
|
||||
// of table height required.
|
||||
if let Some(percentage_resolution_size) = percentage_resolution_size {
|
||||
let get_percent_block_size_deficit = |row_index: usize, track_size: Au| {
|
||||
let size_needed_for_percent =
|
||||
percentage_resolution_size.scale_by(self.rows[row_index].percent.0 / 100.);
|
||||
(size_needed_for_percent - track_size).max(Au::zero())
|
||||
};
|
||||
let percent_block_size_deficit: Au = track_range
|
||||
.clone()
|
||||
.map(|index| get_percent_block_size_deficit(index, track_sizes[index]))
|
||||
.sum();
|
||||
let percent_distributable_block_size = percent_block_size_deficit.min(excess_size);
|
||||
if percent_distributable_block_size > Au::zero() {
|
||||
for track_index in track_range.clone() {
|
||||
let row_deficit =
|
||||
get_percent_block_size_deficit(track_index, track_sizes[track_index]);
|
||||
if row_deficit > Au::zero() {
|
||||
let ratio =
|
||||
row_deficit.to_f32_px() / percent_block_size_deficit.to_f32_px();
|
||||
let size = percent_distributable_block_size.scale_by(ratio);
|
||||
track_sizes[track_index] += size;
|
||||
excess_size -= size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.row_baselines.push(max_ascent);
|
||||
self.row_sizes
|
||||
.push(max_row_height.max(max_ascent + max_descent));
|
||||
}
|
||||
|
||||
// If this is rowspan distribution and there are rows other than the first row that have a
|
||||
// cell with rowspan > 1, distribute the extra space equally to those rows.
|
||||
if rowspan_distribution {
|
||||
let rows_that_start_rowspan: Vec<usize> = track_range
|
||||
.clone()
|
||||
.filter(other_row_that_starts_a_rowspan)
|
||||
.collect();
|
||||
if !rows_that_start_rowspan.is_empty() {
|
||||
let scale = 1.0 / rows_that_start_rowspan.len() as f32;
|
||||
for track_index in rows_that_start_rowspan.iter() {
|
||||
track_sizes[*track_index] += excess_size.scale_by(scale);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are unconstrained non-empty rows, grow them all proportionally to their current size.
|
||||
let unconstrained_non_empty_rows: Vec<usize> = track_range
|
||||
.clone()
|
||||
.filter(is_unconstrained)
|
||||
.filter(is_not_empty)
|
||||
.collect();
|
||||
if !unconstrained_non_empty_rows.is_empty() {
|
||||
let total_size: Au = unconstrained_non_empty_rows
|
||||
.iter()
|
||||
.map(|index| track_sizes[*index])
|
||||
.sum();
|
||||
for track_index in unconstrained_non_empty_rows.iter() {
|
||||
let scale = track_sizes[*track_index].to_f32_px() / total_size.to_f32_px();
|
||||
track_sizes[*track_index] += excess_size.scale_by(scale);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let (non_empty_rows, empty_rows): (Vec<usize>, Vec<usize>) =
|
||||
track_range.clone().partition(is_not_empty);
|
||||
let only_have_empty_rows = empty_rows.len() == track_range.len();
|
||||
if !empty_rows.is_empty() {
|
||||
// If this is rowspan distribution and there are only empty rows, just grow the
|
||||
// last one.
|
||||
if rowspan_distribution && only_have_empty_rows {
|
||||
track_sizes[*empty_rows.last().unwrap()] += excess_size;
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, if we only have empty rows or if all the non-empty rows are constrained,
|
||||
// then grow the empty rows.
|
||||
let non_empty_rows_all_constrained = !non_empty_rows.iter().any(is_unconstrained);
|
||||
if only_have_empty_rows || non_empty_rows_all_constrained {
|
||||
// If there are both unconstrained and constrained empty rows, only increase the
|
||||
// size of the unconstrained ones, otherwise increase the size of all empty rows.
|
||||
let mut rows_to_grow = &empty_rows;
|
||||
let unconstrained_empty_rows: Vec<usize> = rows_to_grow
|
||||
.iter()
|
||||
.copied()
|
||||
.filter(is_unconstrained)
|
||||
.collect();
|
||||
if !unconstrained_empty_rows.is_empty() {
|
||||
rows_to_grow = &unconstrained_empty_rows;
|
||||
}
|
||||
|
||||
// All empty rows that will grow equally.
|
||||
let scale = 1.0 / rows_to_grow.len() as f32;
|
||||
for track_index in rows_to_grow.iter() {
|
||||
track_sizes[*track_index] += excess_size.scale_by(scale);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are non-empty rows, they all grow in proportion to their current size,
|
||||
// whether or not they are constrained.
|
||||
if !non_empty_rows.is_empty() {
|
||||
let total_size: Au = non_empty_rows.iter().map(|index| track_sizes[*index]).sum();
|
||||
for track_index in non_empty_rows.iter() {
|
||||
let scale = track_sizes[*track_index].to_f32_px() / total_size.to_f32_px();
|
||||
track_sizes[*track_index] += excess_size.scale_by(scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Given computed row sizes, compute the final block size of the table and distribute extra
|
||||
/// block size to table rows.
|
||||
fn compute_table_height_and_final_row_heights(
|
||||
&mut self,
|
||||
mut row_sizes: Vec<Au>,
|
||||
containing_block: &ContainingBlock,
|
||||
) {
|
||||
// The table content height is the maximum of the computed table height from style and the
|
||||
// sum of computed row heights from row layout plus size from borders and spacing.
|
||||
let table_height_from_style = self
|
||||
.table
|
||||
.style
|
||||
.content_box_size(containing_block, &self.pbm)
|
||||
.block
|
||||
.auto_is(Length::zero)
|
||||
.into();
|
||||
|
||||
let border_spacing = self.table.border_spacing();
|
||||
let border_and_spacing = self.pbm.border.block_sum() +
|
||||
border_spacing.block * (self.table.size.height as i32 + 1);
|
||||
let table_height_from_rows = row_sizes.iter().sum::<Au>() + border_and_spacing;
|
||||
let final_table_height = table_height_from_rows.max(table_height_from_style);
|
||||
|
||||
// If the table height is defined by the rows sizes, there is no extra space to distribute
|
||||
// to rows.
|
||||
if final_table_height == table_height_from_rows {
|
||||
self.row_sizes = row_sizes;
|
||||
return;
|
||||
}
|
||||
|
||||
// There was extra block size added to the table from the table style, so distribute this
|
||||
// extra space to rows using the same distribution algorithm used for distributing rowspan
|
||||
// space.
|
||||
// TODO: This should first distribute space to row groups and then to rows.
|
||||
self.distribute_extra_size_to_rows(
|
||||
final_table_height - table_height_from_rows,
|
||||
0..self.table.size.height,
|
||||
&mut row_sizes,
|
||||
Some(final_table_height),
|
||||
false, /* rowspan_distribution */
|
||||
);
|
||||
self.row_sizes = row_sizes;
|
||||
}
|
||||
|
||||
/// Lay out the table of this [`TableLayout`] into fragments. This should only be be called
|
||||
|
@ -1372,17 +1726,17 @@ impl Table {
|
|||
&self,
|
||||
writing_mode: WritingMode,
|
||||
column_index: usize,
|
||||
) -> CellOrColumnMeasure {
|
||||
) -> CellOrTrackMeasure {
|
||||
let column = match self.columns.get(column_index) {
|
||||
Some(column) => column,
|
||||
None => return CellOrColumnMeasure::zero(),
|
||||
None => return CellOrTrackMeasure::zero(),
|
||||
};
|
||||
|
||||
let (size, min_size, max_size) = get_sizes_from_style(&column.style, writing_mode);
|
||||
let percentage_contribution =
|
||||
get_size_percentage_contribution_from_style(&column.style, writing_mode);
|
||||
|
||||
CellOrColumnMeasure {
|
||||
CellOrTrackMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
// > The outer min-content width of a table-column or table-column-group is
|
||||
// > max(min-width, width).
|
||||
|
@ -1391,7 +1745,34 @@ impl Table {
|
|||
// > max(min-width, min(max-width, width)).
|
||||
max_content: min_size.inline.max(max_size.inline.min(size.inline)),
|
||||
},
|
||||
percentage_width: percentage_contribution.inline,
|
||||
percentage: percentage_contribution.inline,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_row_measure_for_row_at_index(
|
||||
&self,
|
||||
writing_mode: WritingMode,
|
||||
row_index: usize,
|
||||
) -> CellOrTrackMeasure {
|
||||
let row = match self.rows.get(row_index) {
|
||||
Some(row) => row,
|
||||
None => return CellOrTrackMeasure::zero(),
|
||||
};
|
||||
|
||||
let (size, min_size, max_size) = get_sizes_from_style(&row.style, writing_mode);
|
||||
let percentage_contribution =
|
||||
get_size_percentage_contribution_from_style(&row.style, writing_mode);
|
||||
|
||||
CellOrTrackMeasure {
|
||||
content_sizes: ContentSizes {
|
||||
// > The outer min-content width of a table-column or table-column-group is
|
||||
// > max(min-width, width).
|
||||
min_content: min_size.inline.max(size.block),
|
||||
// > The outer max-content width of a table-column or table-column-group is
|
||||
// > max(min-width, min(max-width, width)).
|
||||
max_content: min_size.inline.max(max_size.block.min(size.block)),
|
||||
},
|
||||
percentage: percentage_contribution.block,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1579,3 +1960,19 @@ fn get_sizes_from_style(
|
|||
|
||||
(size, min_size, max_size)
|
||||
}
|
||||
|
||||
struct RowspanToDistribute<'a> {
|
||||
coordinates: TableSlotCoordinates,
|
||||
cell: &'a TableSlotCell,
|
||||
measure: &'a CellOrTrackMeasure,
|
||||
}
|
||||
|
||||
impl<'a> RowspanToDistribute<'a> {
|
||||
fn range(&self) -> Range<usize> {
|
||||
self.coordinates.y..self.coordinates.y + self.cell.rowspan
|
||||
}
|
||||
|
||||
fn fully_encloses(&self, other: &RowspanToDistribute) -> bool {
|
||||
other.coordinates.y > self.coordinates.y && other.range().end < self.range().end
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-001.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-002.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-003.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-004.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-005.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-006.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-007.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-013.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[clear-applies-to-014.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[float-applies-to-013.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[float-applies-to-014.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-table-cell-001.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[padding-applies-to-013a.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[row-visibility-002.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[separated-border-model-004e.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-cell-001.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-height-algorithm-008a.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-height-algorithm-008b.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-height-algorithm-008c.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-visual-layout-026a.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-visual-layout-026b.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-visual-layout-026c.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-visual-layout-026d.xht]
|
||||
expected: FAIL
|
|
@ -1,12 +0,0 @@
|
|||
[border-spacing-included-in-sizes-001.html]
|
||||
[tbody 1]
|
||||
expected: FAIL
|
||||
|
||||
[tbody 2]
|
||||
expected: FAIL
|
||||
|
||||
[tbody 3]
|
||||
expected: FAIL
|
||||
|
||||
[tfoot tr 4]
|
||||
expected: FAIL
|
|
@ -1,15 +0,0 @@
|
|||
[bounding-box-computation-1.html]
|
||||
[Table-cell is 100px tall]
|
||||
expected: FAIL
|
||||
|
||||
[Table-row is 100px tall]
|
||||
expected: FAIL
|
||||
|
||||
[Table-row-group is 100px tall]
|
||||
expected: FAIL
|
||||
|
||||
[Table-column is 100px tall]
|
||||
expected: FAIL
|
||||
|
||||
[Table-column-group is 100px tall]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[chrome-rowspan-bug.html]
|
||||
[table tracks correct use of rowspan]
|
||||
expected: FAIL
|
|
@ -2,9 +2,6 @@
|
|||
[main table 1]
|
||||
expected: FAIL
|
||||
|
||||
[main table 2]
|
||||
expected: FAIL
|
||||
|
||||
[main table 4]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -31,3 +28,6 @@
|
|||
|
||||
[main table 3]
|
||||
expected: FAIL
|
||||
|
||||
[main table 2]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[fixup-dynamic-anonymous-table-001.html]
|
||||
expected: FAIL
|
|
@ -1,10 +1,4 @@
|
|||
[computing-row-measure-0.html]
|
||||
[Checking intermediate min-content height for span 1 (1)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 1 (3)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 1 (2)]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
[computing-row-measure-1.html]
|
||||
[Checking intermediate min-content width for span 2 (4)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content width for span 2 (1)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content width for span 2 (2)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content width for span 2 (4)]
|
||||
expected: FAIL
|
||||
|
|
|
@ -11,26 +11,14 @@
|
|||
[Border-spacing is added between any two unmerged columns (2)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged rows (2)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged columns (3)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged rows (3)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged columns (4)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged rows (4)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged columns (5)]
|
||||
expected: FAIL
|
||||
|
||||
[Border-spacing is added between any two unmerged rows (5)]
|
||||
expected: FAIL
|
||||
|
||||
[Explicitely defined rows are not merged]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[col-change-span-bg-invalidation-002.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[subpixel-table-cell-height-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[subpixel-table-cell-width-002.html]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[table-cell-scroll-height.html]
|
||||
[scrollHeight on scrollable table cell]
|
||||
expected: FAIL
|
|
@ -2,9 +2,6 @@
|
|||
[2.1. An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes. (1/2)]
|
||||
expected: FAIL
|
||||
|
||||
[2.1. An anonymous table-row box must be generated around each sequence of consecutive children of a table-root box which are not proper table child boxes. (2/2)]
|
||||
expected: FAIL
|
||||
|
||||
[2.2. An anonymous table-row box must be generated around each sequence of consecutive children of a table-row-grouping box which are not table-row boxes. (1/3)]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -2,14 +2,5 @@
|
|||
[table, .display-table 1]
|
||||
expected: FAIL
|
||||
|
||||
[table, .display-table 2]
|
||||
expected: FAIL
|
||||
|
||||
[table, .display-table 3]
|
||||
expected: FAIL
|
||||
|
||||
[table, .display-table 4]
|
||||
expected: FAIL
|
||||
|
||||
[table, .display-table 5]
|
||||
expected: FAIL
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
[table 6]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
[table 9]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -85,3 +82,6 @@
|
|||
|
||||
[table 8]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
[rowspan-height-redistribution.html]
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
[table 8]
|
||||
expected: FAIL
|
||||
|
||||
[table 22]
|
||||
expected: FAIL
|
||||
|
||||
[table 23]
|
||||
expected: FAIL
|
||||
|
||||
[table 2]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -14,38 +26,41 @@
|
|||
[table 6]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
[table 8]
|
||||
expected: FAIL
|
||||
|
||||
[table 9]
|
||||
expected: FAIL
|
||||
|
||||
[table 10]
|
||||
expected: FAIL
|
||||
|
||||
[table 11]
|
||||
expected: FAIL
|
||||
|
||||
[table 12]
|
||||
expected: FAIL
|
||||
|
||||
[table 13]
|
||||
expected: FAIL
|
||||
|
||||
[table 14]
|
||||
expected: FAIL
|
||||
|
||||
[table 15]
|
||||
expected: FAIL
|
||||
|
||||
[table 16]
|
||||
expected: FAIL
|
||||
|
||||
[table 17]
|
||||
expected: FAIL
|
||||
|
||||
[table 18]
|
||||
expected: FAIL
|
||||
|
||||
[table 19]
|
||||
[table 20]
|
||||
expected: FAIL
|
||||
|
||||
[table 21]
|
||||
expected: FAIL
|
||||
|
||||
[table 22]
|
||||
expected: FAIL
|
||||
|
||||
[table 23]
|
||||
expected: FAIL
|
||||
|
||||
[table 20]
|
||||
[table 24]
|
||||
expected: FAIL
|
||||
|
|
|
@ -56,9 +56,6 @@
|
|||
[table 23]
|
||||
expected: FAIL
|
||||
|
||||
[table 24]
|
||||
expected: FAIL
|
||||
|
||||
[table 25]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
[table 6]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
[table 9]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -37,3 +34,6 @@
|
|||
|
||||
[table 15]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
|
|
@ -26,9 +26,6 @@
|
|||
[table 11]
|
||||
expected: FAIL
|
||||
|
||||
[table 12]
|
||||
expected: FAIL
|
||||
|
||||
[table 14]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -58,3 +55,6 @@
|
|||
|
||||
[table 26]
|
||||
expected: FAIL
|
||||
|
||||
[table 12]
|
||||
expected: FAIL
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
[table 5]
|
||||
expected: FAIL
|
||||
|
||||
[table 6]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
[table 6]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
[table 8]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
[table 9]
|
||||
expected: FAIL
|
||||
|
||||
[table 11]
|
||||
expected: FAIL
|
||||
|
||||
[table 5]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
[visibility-collapse-row-005.html]
|
||||
[collapsed row should not contribute to overflow]
|
||||
expected: FAIL
|
||||
|
||||
[collapsed section should not contribute to overflow]
|
||||
expected: FAIL
|
|
@ -1,12 +1,6 @@
|
|||
[computing-column-measure-1.html]
|
||||
[Checking intermediate min-content height for span 2 (1)]
|
||||
[Checking intermediate min-content height for span 2 (4)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 2 (2)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 2 (3)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 2 (4)]
|
||||
expected: FAIL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue