layout: Start work on table row height and vertical-align (#31246)

This implements a very naive row height allocation approach. It has just
enough to implement `vertical-align` in table cells. Rowspanned cells
get enough space for their content, with the extra space necessary being
allocated to the last row. There's still a lot missing here, including
proper distribution of row height to rowspanned cells.

Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2024-02-10 09:03:01 +01:00 committed by GitHub
parent 39b3beda5d
commit 35fb95ca85
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 261 additions and 132 deletions

View file

@ -13,6 +13,7 @@ use style::computed_values::float::T as Float;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto}; use style::values::computed::{Length, LengthOrAuto};
use style::values::specified::Display;
use style::Zero; use style::Zero;
use crate::cell::ArcRefCell; use crate::cell::ArcRefCell;
@ -418,22 +419,33 @@ fn layout_block_level_children(
mut sequential_layout_state: Option<&mut SequentialLayoutState>, mut sequential_layout_state: Option<&mut SequentialLayoutState>,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
) -> FlowLayout { ) -> FlowLayout {
match sequential_layout_state { let mut placement_state =
PlacementState::new(collapsible_with_parent_start_margin, containing_block.style);
let fragments = match sequential_layout_state {
Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially( Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
layout_context, layout_context,
positioning_context, positioning_context,
child_boxes, child_boxes,
containing_block, containing_block,
sequential_layout_state, sequential_layout_state,
collapsible_with_parent_start_margin, &mut placement_state,
), ),
None => layout_block_level_children_in_parallel( None => layout_block_level_children_in_parallel(
layout_context, layout_context,
positioning_context, positioning_context,
child_boxes, child_boxes,
containing_block, containing_block,
collapsible_with_parent_start_margin, &mut placement_state,
), ),
};
let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
FlowLayout {
fragments,
content_block_size,
collapsible_margins_in_children,
baselines,
} }
} }
@ -442,8 +454,8 @@ fn layout_block_level_children_in_parallel(
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
child_boxes: &[ArcRefCell<BlockLevelBox>], child_boxes: &[ArcRefCell<BlockLevelBox>],
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, placement_state: &mut PlacementState,
) -> FlowLayout { ) -> Vec<Fragment> {
let collects_for_nearest_positioned_ancestor = let collects_for_nearest_positioned_ancestor =
positioning_context.collects_for_nearest_positioned_ancestor(); positioning_context.collects_for_nearest_positioned_ancestor();
let layout_results: Vec<(Fragment, PositioningContext)> = child_boxes let layout_results: Vec<(Fragment, PositioningContext)> = child_boxes
@ -462,8 +474,7 @@ fn layout_block_level_children_in_parallel(
}) })
.collect(); .collect();
let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin); layout_results
let fragments = layout_results
.into_iter() .into_iter()
.map(|(mut fragment, mut child_positioning_context)| { .map(|(mut fragment, mut child_positioning_context)| {
placement_state.place_fragment_and_update_baseline(&mut fragment, None); placement_state.place_fragment_and_update_baseline(&mut fragment, None);
@ -474,15 +485,7 @@ fn layout_block_level_children_in_parallel(
positioning_context.append(child_positioning_context); positioning_context.append(child_positioning_context);
fragment fragment
}) })
.collect(); .collect()
let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
FlowLayout {
fragments,
content_block_size,
collapsible_margins_in_children,
baselines,
}
} }
fn layout_block_level_children_sequentially( fn layout_block_level_children_sequentially(
@ -491,14 +494,12 @@ fn layout_block_level_children_sequentially(
child_boxes: &[ArcRefCell<BlockLevelBox>], child_boxes: &[ArcRefCell<BlockLevelBox>],
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
sequential_layout_state: &mut SequentialLayoutState, sequential_layout_state: &mut SequentialLayoutState,
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, placement_state: &mut PlacementState,
) -> FlowLayout { ) -> Vec<Fragment> {
let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin);
// Because floats are involved, we do layout for this block formatting context in tree // Because floats are involved, we do layout for this block formatting context in tree
// order without parallelism. This enables mutable access to a `SequentialLayoutState` that // order without parallelism. This enables mutable access to a `SequentialLayoutState` that
// tracks every float encountered so far (again in tree order). // tracks every float encountered so far (again in tree order).
let fragments = child_boxes child_boxes
.iter() .iter()
.map(|child_box| { .map(|child_box| {
let positioning_context_length_before_layout = positioning_context.len(); let positioning_context_length_before_layout = positioning_context.len();
@ -521,15 +522,7 @@ fn layout_block_level_children_sequentially(
fragment fragment
}) })
.collect(); .collect()
let (content_block_size, collapsible_margins_in_children, baselines) = placement_state.finish();
FlowLayout {
fragments,
content_block_size,
collapsible_margins_in_children,
baselines,
}
} }
impl BlockLevelBox { impl BlockLevelBox {
@ -1387,12 +1380,16 @@ struct PlacementState {
current_margin: CollapsedMargin, current_margin: CollapsedMargin,
current_block_direction_position: Length, current_block_direction_position: Length,
inflow_baselines: Baselines, inflow_baselines: Baselines,
is_inline_block_context: bool,
} }
impl PlacementState { impl PlacementState {
fn new( fn new(
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
containing_block_style: &ComputedValues,
) -> PlacementState { ) -> PlacementState {
let is_inline_block_context =
containing_block_style.get_box().clone_display() == Display::InlineBlock;
PlacementState { PlacementState {
next_in_flow_margin_collapses_with_parent_start_margin: next_in_flow_margin_collapses_with_parent_start_margin:
collapsible_with_parent_start_margin.0, collapsible_with_parent_start_margin.0,
@ -1401,6 +1398,7 @@ impl PlacementState {
current_margin: CollapsedMargin::zero(), current_margin: CollapsedMargin::zero(),
current_block_direction_position: Length::zero(), current_block_direction_position: Length::zero(),
inflow_baselines: Baselines::default(), inflow_baselines: Baselines::default(),
is_inline_block_context,
} }
} }
@ -1411,15 +1409,27 @@ impl PlacementState {
) { ) {
self.place_fragment(fragment, sequential_layout_state); self.place_fragment(fragment, sequential_layout_state);
if let Fragment::Box(box_fragment) = fragment { let box_fragment = match fragment {
let box_block_offset = box_fragment.content_rect.start_corner.block.into(); Fragment::Box(box_fragment) => box_fragment,
match (self.inflow_baselines.first, box_fragment.baselines.first) { _ => return,
(None, Some(first)) => self.inflow_baselines.first = Some(first + box_block_offset), };
_ => {},
} // From <https://drafts.csswg.org/css-align-3/#baseline-export>:
if let Some(last) = box_fragment.baselines.last { // > When finding the first/last baseline set of an inline-block, any baselines
self.inflow_baselines.last = Some(last + box_block_offset); // > contributed by table boxes must be skipped. (This quirk is a legacy behavior from
} // > [CSS2].)
let display = box_fragment.style.clone_display();
let is_table = display == Display::Table;
if self.is_inline_block_context && is_table {
return;
}
let box_block_offset = box_fragment.content_rect.start_corner.block.into();
if let (None, Some(first)) = (self.inflow_baselines.first, box_fragment.baselines.first) {
self.inflow_baselines.first = Some(first + box_block_offset);
}
if let Some(last) = box_fragment.baselines.last {
self.inflow_baselines.last = Some(last + box_block_offset);
} }
} }

View file

@ -61,7 +61,7 @@ pub(crate) enum NonReplacedFormattingContextContents {
/// The baselines of a layout or a [`BoxFragment`]. Some layout uses the first and some layout uses /// The baselines of a layout or a [`BoxFragment`]. Some layout uses the first and some layout uses
/// the last. /// the last.
#[derive(Default, Serialize)] #[derive(Debug, Default, Serialize)]
pub(crate) struct Baselines { pub(crate) struct Baselines {
pub first: Option<Au>, pub first: Option<Au>,
pub last: Option<Au>, pub last: Option<Au>,

View file

@ -202,6 +202,7 @@ impl BoxFragment {
\nmargin={:?}\ \nmargin={:?}\
\nclearance={:?}\ \nclearance={:?}\
\nscrollable_overflow={:?}\ \nscrollable_overflow={:?}\
\nbaselines={:?}\
\noverflow={:?} / {:?}", \noverflow={:?} / {:?}",
self.base, self.base,
self.content_rect, self.content_rect,
@ -210,6 +211,7 @@ impl BoxFragment {
self.margin, self.margin,
self.clearance, self.clearance,
self.scrollable_overflow(&PhysicalRect::zero()), self.scrollable_overflow(&PhysicalRect::zero()),
self.baselines,
self.style.get_box().overflow_x, self.style.get_box().overflow_x,
self.style.get_box().overflow_y, self.style.get_box().overflow_y,
)); ));

View file

@ -107,11 +107,14 @@ impl Table {
} }
} }
let mut table = table_builder.finish();
table.anonymous = true;
IndependentFormattingContext::NonReplaced(NonReplacedFormattingContext { IndependentFormattingContext::NonReplaced(NonReplacedFormattingContext {
base_fragment_info: (&anonymous_info).into(), base_fragment_info: (&anonymous_info).into(),
style: anonymous_style, style: anonymous_style,
content_sizes: None, content_sizes: None,
contents: NonReplacedFormattingContextContents::Table(table_builder.finish()), contents: NonReplacedFormattingContextContents::Table(table),
}) })
} }
@ -226,6 +229,24 @@ impl TableBuilder {
for row in self.table.slots.iter_mut() { for row in self.table.slots.iter_mut() {
row.resize_with(self.table.size.width, || TableSlot::Empty); row.resize_with(self.table.size.width, || TableSlot::Empty);
} }
// Turn all rowspan=0 rows into the real value to avoid having to
// make the calculation continually during layout. In addition, make
// sure that there are no rowspans that extend past the end of the
// table.
for row_index in 0..self.table.size.height {
for cell in self.table.slots[row_index].iter_mut() {
if let TableSlot::Cell(ref mut cell) = cell {
let rowspan_to_end_of_table = self.table.size.height - row_index;
if cell.rowspan == 0 {
cell.rowspan = rowspan_to_end_of_table;
} else {
cell.rowspan = cell.rowspan.min(rowspan_to_end_of_table);
}
}
}
}
self.table self.table
} }

View file

@ -2,18 +2,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::ops::Add;
use app_units::{Au, MAX_AU}; use app_units::{Au, MAX_AU};
use euclid::num::Zero; use euclid::num::Zero;
use log::warn; use log::warn;
use style::computed_values::border_collapse::T as BorderCollapse; use style::computed_values::border_collapse::T as BorderCollapse;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::values::computed::{CSSPixelLength, Length, LengthOrAuto, Percentage}; use style::values::computed::{CSSPixelLength, Length, LengthOrAuto, Percentage};
use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, VerticalAlignKeyword};
use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage}; use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage};
use super::{Table, TableSlot, TableSlotCell}; use super::{Table, TableSlot, TableSlotCell};
use crate::context::LayoutContext; use crate::context::LayoutContext;
use crate::formatting_contexts::{Baselines, IndependentLayout}; use crate::formatting_contexts::{Baselines, IndependentLayout};
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment}; use crate::fragment_tree::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::{LogicalRect, LogicalSides, LogicalVec2}; use crate::geom::{LogicalRect, LogicalSides, LogicalVec2};
use crate::positioned::{PositioningContext, PositioningContextLength}; use crate::positioned::{PositioningContext, PositioningContextLength};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
@ -29,7 +32,20 @@ struct CellLayout {
padding: LogicalSides<Length>, padding: LogicalSides<Length>,
border: LogicalSides<Length>, border: LogicalSides<Length>,
positioning_context: PositioningContext, positioning_context: PositioningContext,
rowspan: usize, }
impl CellLayout {
fn ascent(&self) -> Au {
self.layout
.baselines
.first
.unwrap_or(self.layout.content_block_size)
}
/// The block size of this laid out cell including its border and padding.
fn outer_block_size(&self) -> Au {
self.layout.content_block_size + (self.border.block_sum() + self.padding.block_sum()).into()
}
} }
/// A helper struct that performs the layout of the box tree version /// A helper struct that performs the layout of the box tree version
@ -45,6 +61,7 @@ struct TableLayout<'a> {
column_measures: Vec<CellOrColumnMeasure>, column_measures: Vec<CellOrColumnMeasure>,
distributed_column_widths: Vec<Au>, distributed_column_widths: Vec<Au>,
row_sizes: Vec<Au>, row_sizes: Vec<Au>,
row_baselines: Vec<Au>,
cells_laid_out: Vec<Vec<Option<CellLayout>>>, cells_laid_out: Vec<Vec<Option<CellLayout>>>,
} }
@ -75,6 +92,7 @@ impl<'a> TableLayout<'a> {
column_measures: Vec::new(), column_measures: Vec::new(),
distributed_column_widths: Vec::new(), distributed_column_widths: Vec::new(),
row_sizes: Vec::new(), row_sizes: Vec::new(),
row_baselines: Vec::new(),
cells_laid_out: Vec::new(), cells_laid_out: Vec::new(),
} }
} }
@ -883,6 +901,8 @@ 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 do_row_layout_first_pass(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
@ -936,7 +956,6 @@ impl<'a> TableLayout<'a> {
padding, padding,
border, border,
positioning_context, positioning_context,
rowspan: cell.rowspan,
})) }))
} }
self.cells_laid_out.push(cells_laid_out_row); self.cells_laid_out.push(cells_laid_out_row);
@ -945,52 +964,110 @@ impl<'a> TableLayout<'a> {
fn distribute_height_to_rows(&mut self) { fn distribute_height_to_rows(&mut self) {
for row_index in 0..self.table.size.height { for row_index in 0..self.table.size.height {
let mut max_row_height = Au::zero(); 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 { for column_index in 0..self.table.size.width {
let coords = TableSlotCoordinates::new(column_index, row_index); let coords = TableSlotCoordinates::new(column_index, row_index);
self.table
.resolve_first_cell_coords(coords) let cell = match self.table.slots[row_index][column_index] {
.map(|resolved_coords| { TableSlot::Cell(ref cell) => cell,
let cell = self.cells_laid_out[resolved_coords.y][resolved_coords.x] TableSlot::Spanned(ref spanned_cells) if spanned_cells[0].y != 0 => {
.as_ref() let offset = spanned_cells[0];
.unwrap(); let origin = coords - offset;
let total_height = cell.layout.content_block_size +
cell.border.block_sum().into() + // We only allocate the remaining space for the last row of the rowspanned cell.
cell.padding.block_sum().into(); if let Some(TableSlot::Cell(origin_cell)) = self.table.get_slot(origin) {
// TODO: We are accounting for rowspan=0 here, but perhaps this should be if origin_cell.rowspan != offset.y + 1 {
// translated into a real rowspan during table box tree construction. continue;
let effective_rowspan = match cell.rowspan { }
0 => (self.table.size.height - resolved_coords.y) as i32, }
rowspan => rowspan as i32,
}; // This is all of the rows that are spanned except this one.
max_row_height = (total_height / effective_rowspan).max(max_row_height) 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);
}
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;
},
};
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
// 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(),
);
}
}
} }
self.row_sizes.push(max_row_height);
self.row_baselines.push(max_ascent);
self.row_sizes
.push(max_row_height.max(max_ascent + max_descent));
} }
} }
/// Lay out the table of this [`TableLayout`] into fragments. This should only be be called /// Lay out the table of this [`TableLayout`] into fragments. This should only be be called
/// after calling [`TableLayout.compute_measures`]. /// after calling [`TableLayout.compute_measures`].
fn layout_into_box_fragments( fn layout(mut self, positioning_context: &mut PositioningContext) -> IndependentLayout {
mut self,
positioning_context: &mut PositioningContext,
) -> (Vec<Fragment>, Au) {
assert_eq!(self.table.size.height, self.row_sizes.len()); assert_eq!(self.table.size.height, self.row_sizes.len());
assert_eq!(self.table.size.width, self.distributed_column_widths.len()); assert_eq!(self.table.size.width, self.distributed_column_widths.len());
let mut baselines = Baselines::default();
let border_spacing = self.table.border_spacing(); let border_spacing = self.table.border_spacing();
let mut fragments = Vec::new(); let mut fragments = Vec::new();
let mut row_offset = border_spacing.block; let mut row_offset = border_spacing.block;
for row_index in 0..self.table.size.height { for row_index in 0..self.table.size.height {
let mut column_offset = border_spacing.inline; let mut column_offset = border_spacing.inline;
let row_size = self.row_sizes[row_index]; let row_size = self.row_sizes[row_index];
let row_baseline = self.row_baselines[row_index];
// From <https://drafts.csswg.org/css-align-3/#baseline-export>
// > If any cells in the row participate in first baseline/last baseline alignment along
// > the inline axis, the first/last baseline set of the row is generated from their
// > shared alignment baseline and the rows first available font, after alignment has
// > been performed. Otherwise, the first/last baseline set of the row is synthesized from
// > the lowest and highest content edges of the cells in the row. [CSS2]
//
// If any cell below has baseline alignment, these values will be overwritten,
// but they are initialized to the content edge of the first row.
if row_index == 0 {
baselines.first = Some(row_offset + row_size);
baselines.last = Some(row_offset + row_size);
}
for column_index in 0..self.table.size.width { for column_index in 0..self.table.size.width {
let column_size = self.distributed_column_widths[column_index];
let layout = match self.cells_laid_out[row_index][column_index].take() { let layout = match self.cells_laid_out[row_index][column_index].take() {
Some(layout) => layout, Some(layout) => layout,
None => continue, None => {
continue;
},
}; };
let cell = match self.table.slots[row_index][column_index] { let cell = match self.table.slots[row_index][column_index] {
@ -1001,30 +1078,58 @@ impl<'a> TableLayout<'a> {
}, },
}; };
// If this cell has baseline alignment, it can adjust the table's overall baseline.
if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline {
if row_index == 0 {
baselines.first = Some(row_offset + row_baseline);
}
baselines.last = Some(row_offset + row_baseline);
}
// Calculate the inline and block size of all rows and columns that this cell spans.
let inline_size: Au = (column_index..column_index + cell.colspan)
.map(|index| self.distributed_column_widths[index])
.fold(Au::zero(), Au::add) +
((cell.colspan - 1) as i32 * border_spacing.inline);
let block_size: Au = (row_index..row_index + cell.rowspan)
.map(|index| self.row_sizes[index])
.fold(Au::zero(), Au::add) +
((cell.rowspan - 1) as i32 * border_spacing.block);
let cell_rect: LogicalRect<Length> = LogicalRect { let cell_rect: LogicalRect<Length> = LogicalRect {
start_corner: LogicalVec2 { start_corner: LogicalVec2 {
inline: column_offset.into(), inline: column_offset.into(),
block: row_offset.into(), block: row_offset.into(),
}, },
size: LogicalVec2 { size: LogicalVec2 {
inline: column_size.into(), inline: inline_size.into(),
block: row_size.into(), block: block_size.into(),
}, },
}; };
fragments.push(Fragment::Box(cell.create_fragment( fragments.push(Fragment::Box(cell.create_fragment(
layout, layout,
cell_rect, cell_rect,
row_baseline,
positioning_context, positioning_context,
))); )));
column_offset += column_size + border_spacing.inline; column_offset += inline_size + border_spacing.inline;
} }
row_offset += row_size + border_spacing.block; row_offset += row_size + border_spacing.block;
} }
(fragments, row_offset) if self.table.anonymous {
baselines.first = None;
baselines.last = None;
}
IndependentLayout {
fragments,
content_block_size: row_offset,
baselines,
}
} }
} }
@ -1100,13 +1205,7 @@ impl Table {
) -> IndependentLayout { ) -> IndependentLayout {
let mut table_layout = TableLayout::new(self); let mut table_layout = TableLayout::new(self);
table_layout.compute_measures(layout_context, positioning_context, containing_block); table_layout.compute_measures(layout_context, positioning_context, containing_block);
let (fragments, content_block_size) = table_layout.layout(positioning_context)
table_layout.layout_into_box_fragments(positioning_context);
IndependentLayout {
fragments,
content_block_size,
baselines: Baselines::default(),
}
} }
} }
@ -1136,24 +1235,63 @@ impl TableSlotCell {
sizes sizes
} }
fn effective_vertical_align(&self) -> VerticalAlignKeyword {
match self.style.clone_vertical_align() {
VerticalAlign::Keyword(VerticalAlignKeyword::Top) => VerticalAlignKeyword::Top,
VerticalAlign::Keyword(VerticalAlignKeyword::Bottom) => VerticalAlignKeyword::Bottom,
VerticalAlign::Keyword(VerticalAlignKeyword::Middle) => VerticalAlignKeyword::Middle,
_ => VerticalAlignKeyword::Baseline,
}
}
fn create_fragment( fn create_fragment(
&self, &self,
mut layout: CellLayout, mut layout: CellLayout,
cell_rect: LogicalRect<Length>, cell_rect: LogicalRect<Length>,
cell_baseline: Au,
positioning_context: &mut PositioningContext, positioning_context: &mut PositioningContext,
) -> BoxFragment { ) -> BoxFragment {
// This must be scoped to this function because it conflicts with euclid's Zero. // This must be scoped to this function because it conflicts with euclid's Zero.
use style::Zero as StyleZero; use style::Zero as StyleZero;
let fragments = layout.layout.fragments; let cell_content_rect = cell_rect.deflate(&(&layout.padding + &layout.border));
let content_rect = cell_rect.deflate(&(&layout.padding + &layout.border)); let content_block_size = layout.layout.content_block_size.into();
let vertical_align_offset = match self.effective_vertical_align() {
VerticalAlignKeyword::Top => Length::new(0.),
VerticalAlignKeyword::Bottom => cell_content_rect.size.block - content_block_size,
VerticalAlignKeyword::Middle => {
(cell_content_rect.size.block - content_block_size).scale_by(0.5)
},
_ => {
Length::from(cell_baseline) -
(layout.padding.block_start + layout.border.block_start) -
Length::from(layout.ascent())
},
};
// Create an `AnonymousFragment` to move the cell contents to the cell baseline.
let mut vertical_align_fragment_rect = cell_content_rect.clone();
vertical_align_fragment_rect.start_corner = LogicalVec2 {
inline: Length::new(0.),
block: vertical_align_offset,
};
let vertical_align_fragment = AnonymousFragment::new(
vertical_align_fragment_rect,
layout.layout.fragments,
self.style.writing_mode,
);
// Adjust the static position of all absolute children based on the // Adjust the static position of all absolute children based on the
// final content rect of this fragment. // final content rect of this fragment. Note that we are not shifting by the position of the
// Anonymous fragment we use to shift content to the baseline.
//
// TODO(mrobinson): This is correct for absolutes that are direct children of the table
// cell, but wrong for absolute fragments that are more deeply nested in the hierarchy of
// fragments.
layout layout
.positioning_context .positioning_context
.adjust_static_position_of_hoisted_fragments_with_offset( .adjust_static_position_of_hoisted_fragments_with_offset(
&content_rect.start_corner, &cell_content_rect.start_corner,
PositioningContextLength::zero(), PositioningContextLength::zero(),
); );
positioning_context.append(layout.positioning_context); positioning_context.append(layout.positioning_context);
@ -1161,8 +1299,8 @@ impl TableSlotCell {
BoxFragment::new( BoxFragment::new(
self.base_fragment_info, self.base_fragment_info,
self.style.clone(), self.style.clone(),
fragments, vec![Fragment::Anonymous(vertical_align_fragment)],
content_rect, cell_content_rect,
layout.padding, layout.padding,
layout.border, layout.border,
LogicalSides::zero(), /* margin */ LogicalSides::zero(), /* margin */

View file

@ -37,6 +37,9 @@ pub struct Table {
/// The size of this table. /// The size of this table.
pub size: TableSize, pub size: TableSize,
/// Whether or not this Table is anonymous.
anonymous: bool,
} }
impl Table { impl Table {
@ -45,6 +48,7 @@ impl Table {
style, style,
slots: Vec::new(), slots: Vec::new(),
size: TableSize::zero(), size: TableSize::zero(),
anonymous: false,
} }
} }

View file

@ -1,2 +0,0 @@
[margin-collapse-clear-002.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-clear-003.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-clear-008.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapse-clear-009.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-001.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-002.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-003.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-004.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-005.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-006.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-vertical-align-baseline-007.xht]
expected: FAIL

View file

@ -5,9 +5,6 @@
[Anonymous consecutive columns spanned by the same set of cells are merged] [Anonymous consecutive columns spanned by the same set of cells are merged]
expected: FAIL expected: FAIL
[Anonymous consecutive rows spanned by the same set of cells are merged]
expected: FAIL
[Explicitely-defined consecutive columns spanned by the same set of cells are not merged] [Explicitely-defined consecutive columns spanned by the same set of cells are not merged]
expected: FAIL expected: FAIL

View file

@ -1,16 +1,4 @@
[baseline-table.html] [baseline-table.html]
[.container 3]
expected: FAIL
[.container 4]
expected: FAIL
[.container 5]
expected: FAIL
[.container 6]
expected: FAIL
[.container 11] [.container 11]
expected: FAIL expected: FAIL
@ -19,6 +7,3 @@
[.container 13] [.container 13]
expected: FAIL expected: FAIL
[.container 14]
expected: FAIL

View file

@ -17,9 +17,6 @@
[table 7] [table 7]
expected: FAIL expected: FAIL
[table 8]
expected: FAIL
[table 9] [table 9]
expected: FAIL expected: FAIL

View file

@ -29,9 +29,6 @@
[table 12] [table 12]
expected: FAIL expected: FAIL
[table 13]
expected: FAIL
[table 14] [table 14]
expected: FAIL expected: FAIL