mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
layout: Add *very* basic support for table layout (#31121)
* layout: Add *very* basic support for table layout This is the first step to proper table layout. It implements a naive layout algorithm, notably only taking into account the preferred widths of the first table row. Still, it causes some float tests to start passing, so turn on the `layout.tables.enabled` preference for those directories. Co-authored-by: Oriol Brufau <obrufau@igalia.com> * Address review comments * Fix a crash with rowspan=0 * Turn on pref and update results for `/css/css-tables` and `/css/CSS2/tables` --------- Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
3d520f2668
commit
fc31e69f79
115 changed files with 842 additions and 315 deletions
|
@ -419,29 +419,29 @@ impl FlexContainer {
|
|||
impl<'a> FlexItem<'a> {
|
||||
fn new(flex_context: &FlexContext, box_: &'a mut IndependentFormattingContext) -> Self {
|
||||
let containing_block = flex_context.containing_block;
|
||||
let box_style = box_.style();
|
||||
|
||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
||||
assert_eq!(
|
||||
containing_block.style.writing_mode, box_style.writing_mode,
|
||||
containing_block.style.writing_mode,
|
||||
box_.style().writing_mode,
|
||||
"Mixed writing modes are not supported yet"
|
||||
);
|
||||
|
||||
let container_is_horizontal = containing_block.style.writing_mode.is_horizontal();
|
||||
let item_is_horizontal = box_style.writing_mode.is_horizontal();
|
||||
let item_is_horizontal = box_.style().writing_mode.is_horizontal();
|
||||
let item_is_orthogonal = item_is_horizontal != container_is_horizontal;
|
||||
let container_is_row = flex_context.flex_axis == FlexAxis::Row;
|
||||
let cross_axis_is_item_block_axis = container_is_row ^ item_is_orthogonal;
|
||||
|
||||
let pbm = box_style.padding_border_margin(containing_block);
|
||||
let content_box_size = box_style.content_box_size(containing_block, &pbm);
|
||||
let max_size = box_style.content_max_box_size(containing_block, &pbm);
|
||||
let min_size = box_style.content_min_box_size(containing_block, &pbm);
|
||||
let pbm = box_.style().padding_border_margin(containing_block);
|
||||
let content_box_size = box_.style().content_box_size(containing_block, &pbm);
|
||||
let max_size = box_.style().content_max_box_size(containing_block, &pbm);
|
||||
let min_size = box_.style().content_min_box_size(containing_block, &pbm);
|
||||
|
||||
// https://drafts.csswg.org/css-flexbox/#min-size-auto
|
||||
let automatic_min_size = || {
|
||||
// FIXME(stshine): Consider more situations when auto min size is not needed.
|
||||
if box_style.get_box().overflow_x.is_scrollable() {
|
||||
if box_.style().get_box().overflow_x.is_scrollable() {
|
||||
return Length::zero();
|
||||
}
|
||||
|
||||
|
@ -453,7 +453,7 @@ impl<'a> FlexItem<'a> {
|
|||
IndependentFormattingContext::Replaced(ref bfc) => {
|
||||
match (
|
||||
bfc.contents
|
||||
.inline_size_over_block_size_intrinsic_ratio(box_style),
|
||||
.inline_size_over_block_size_intrinsic_ratio(box_.style()),
|
||||
content_box_size.block,
|
||||
) {
|
||||
(Some(ratio), LengthOrAuto::LengthPercentage(block_size)) => {
|
||||
|
@ -476,7 +476,7 @@ impl<'a> FlexItem<'a> {
|
|||
IndependentFormattingContext::Replaced(ref replaced) => {
|
||||
if let Some(ratio) = replaced
|
||||
.contents
|
||||
.inline_size_over_block_size_intrinsic_ratio(box_style)
|
||||
.inline_size_over_block_size_intrinsic_ratio(box_.style())
|
||||
{
|
||||
inline_content_size.clamp_between_extremums(
|
||||
min_size.block.auto_is(|| Length::zero()) * ratio,
|
||||
|
@ -520,7 +520,7 @@ impl<'a> FlexItem<'a> {
|
|||
let padding_border = padding.sum_by_axis() + border.sum_by_axis();
|
||||
let pbm_auto_is_zero = padding_border + margin_auto_is_zero.sum_by_axis();
|
||||
|
||||
let align_self = flex_context.align_for(&box_style.clone_align_self());
|
||||
let align_self = flex_context.align_for(&box_.style().clone_align_self());
|
||||
|
||||
let flex_base_size = flex_base_size(
|
||||
flex_context,
|
||||
|
|
|
@ -155,7 +155,7 @@ impl IndependentFormattingContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes {
|
||||
pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes {
|
||||
match self {
|
||||
Self::NonReplaced(inner) => inner
|
||||
.contents
|
||||
|
@ -173,7 +173,7 @@ impl IndependentFormattingContext {
|
|||
Self::NonReplaced(non_replaced) => {
|
||||
let style = &non_replaced.style;
|
||||
let content_sizes = &mut non_replaced.content_sizes;
|
||||
let contents = &non_replaced.contents;
|
||||
let contents = &mut non_replaced.contents;
|
||||
sizing::outer_inline(&style, containing_block_writing_mode, || {
|
||||
content_sizes
|
||||
.get_or_insert_with(|| {
|
||||
|
@ -213,7 +213,7 @@ impl NonReplacedFormattingContext {
|
|||
|
||||
pub fn inline_content_sizes(&mut self, layout_context: &LayoutContext) -> ContentSizes {
|
||||
let writing_mode = self.style.writing_mode;
|
||||
let contents = &self.contents;
|
||||
let contents = &mut self.contents;
|
||||
self.content_sizes
|
||||
.get_or_insert_with(|| contents.inline_content_sizes(layout_context, writing_mode))
|
||||
.clone()
|
||||
|
@ -222,7 +222,7 @@ impl NonReplacedFormattingContext {
|
|||
|
||||
impl NonReplacedFormattingContextContents {
|
||||
pub fn inline_content_sizes(
|
||||
&self,
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
writing_mode: WritingMode,
|
||||
) -> ContentSizes {
|
||||
|
@ -231,7 +231,7 @@ impl NonReplacedFormattingContextContents {
|
|||
.contents
|
||||
.inline_content_sizes(layout_context, writing_mode),
|
||||
Self::Flex(inner) => inner.inline_content_sizes(),
|
||||
Self::Table(table) => table.inline_content_sizes(),
|
||||
Self::Table(table) => table.inline_content_sizes(layout_context, writing_mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -394,6 +394,23 @@ impl<T> LogicalRect<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn deflate(&self, sides: &LogicalSides<T>) -> Self
|
||||
where
|
||||
T: Add<Output = T> + Copy,
|
||||
T: Sub<Output = T> + Copy,
|
||||
{
|
||||
LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
inline: self.start_corner.inline + sides.inline_start,
|
||||
block: self.start_corner.block + sides.block_start,
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: self.size.inline - sides.inline_sum(),
|
||||
block: self.size.block - sides.block_sum(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical(
|
||||
&self,
|
||||
mode: WritingMode,
|
||||
|
|
|
@ -7,6 +7,8 @@ use std::convert::{TryFrom, TryInto};
|
|||
|
||||
use log::warn;
|
||||
use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
|
||||
use servo_arc::Arc;
|
||||
use style::properties::ComputedValues;
|
||||
use style::selector_parser::PseudoElement;
|
||||
use style::str::char_is_whitespace;
|
||||
use style::values::specified::TextDecorationLine;
|
||||
|
@ -20,13 +22,14 @@ use crate::formatting_contexts::{
|
|||
IndependentFormattingContext, NonReplacedFormattingContext,
|
||||
NonReplacedFormattingContextContents,
|
||||
};
|
||||
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag};
|
||||
use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal};
|
||||
|
||||
/// A reference to a slot and its coordinates in the table
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct ResolvedSlotAndLocation<'a> {
|
||||
cell: &'a TableSlotCell,
|
||||
coords: TableSlotCoordinates,
|
||||
pub(super) struct ResolvedSlotAndLocation<'a> {
|
||||
pub cell: &'a TableSlotCell,
|
||||
pub coords: TableSlotCoordinates,
|
||||
}
|
||||
|
||||
impl<'a> ResolvedSlotAndLocation<'a> {
|
||||
|
@ -114,12 +117,15 @@ impl Table {
|
|||
|
||||
/// Push a new slot into the last row of this table.
|
||||
fn push_new_slot_to_last_row(&mut self, slot: TableSlot) {
|
||||
self.slots.last_mut().expect("Should have rows").push(slot)
|
||||
}
|
||||
let last_row = match self.slots.last_mut() {
|
||||
Some(row) => row,
|
||||
None => {
|
||||
unreachable!("Should have some rows before calling `push_new_slot_to_last_row`")
|
||||
},
|
||||
};
|
||||
|
||||
/// Convenience method for get() that returns a SlotAndLocation
|
||||
fn get_slot<'a>(&'a self, coords: TableSlotCoordinates) -> Option<&'a TableSlot> {
|
||||
self.slots.get(coords.y)?.get(coords.x)
|
||||
self.size.width = self.size.width.max(last_row.len() + 1);
|
||||
last_row.push(slot);
|
||||
}
|
||||
|
||||
/// Find [`ResolvedSlotAndLocation`] of all the slots that cover the slot at the given
|
||||
|
@ -127,7 +133,7 @@ impl Table {
|
|||
/// the target and returns a [`ResolvedSlotAndLocation`] for each of them. If there is
|
||||
/// no slot at the given coordinates or that slot is an empty space, an empty vector
|
||||
/// is returned.
|
||||
fn resolve_slot_at<'a>(
|
||||
pub(super) fn resolve_slot_at<'a>(
|
||||
&'a self,
|
||||
coords: TableSlotCoordinates,
|
||||
) -> Vec<ResolvedSlotAndLocation<'a>> {
|
||||
|
@ -191,7 +197,6 @@ impl TableSlot {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TableBuilder {
|
||||
/// The table that we are building.
|
||||
table: Table,
|
||||
|
@ -209,7 +214,22 @@ pub struct TableBuilder {
|
|||
}
|
||||
|
||||
impl TableBuilder {
|
||||
pub fn finish(self) -> Table {
|
||||
pub(super) fn new(style: Arc<ComputedValues>) -> Self {
|
||||
Self {
|
||||
table: Table::new(style),
|
||||
incoming_rowspans: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_for_tests() -> Self {
|
||||
Self::new(ComputedValues::initial_values().to_arc())
|
||||
}
|
||||
|
||||
pub fn finish(mut self) -> Table {
|
||||
// Make sure that every row has the same number of cells.
|
||||
for row in self.table.slots.iter_mut() {
|
||||
row.resize_with(self.table.size.width, || TableSlot::Empty);
|
||||
}
|
||||
self.table
|
||||
}
|
||||
|
||||
|
@ -227,6 +247,7 @@ impl TableBuilder {
|
|||
|
||||
pub fn start_row<'builder>(&'builder mut self) {
|
||||
self.table.slots.push(Vec::new());
|
||||
self.table.size.height += 1;
|
||||
self.create_slots_for_cells_above_with_rowspan(true);
|
||||
}
|
||||
|
||||
|
@ -385,7 +406,7 @@ where
|
|||
context,
|
||||
info,
|
||||
propagated_text_decoration_line,
|
||||
builder: Default::default(),
|
||||
builder: TableBuilder::new(info.style.clone()),
|
||||
current_anonymous_row_content: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -592,12 +613,22 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let tag = Tag::new_pseudo(
|
||||
self.info.node.opaque(),
|
||||
Some(PseudoElement::ServoAnonymousTableCell),
|
||||
);
|
||||
let base_fragment_info = BaseFragmentInfo {
|
||||
tag,
|
||||
flags: FragmentFlags::empty(),
|
||||
};
|
||||
|
||||
let block_container = builder.finish();
|
||||
self.table_traversal.builder.add_cell(TableSlotCell {
|
||||
contents: BlockFormattingContext::from_block_container(block_container),
|
||||
colspan: 1,
|
||||
rowspan: 1,
|
||||
id: 0, // This is just an id used for testing purposes.
|
||||
style: anonymous_info.style,
|
||||
base_fragment_info,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -657,7 +688,8 @@ where
|
|||
contents,
|
||||
colspan,
|
||||
rowspan,
|
||||
id: 0, // This is just an id used for testing purposes.
|
||||
style: info.style.clone(),
|
||||
base_fragment_info: info.into(),
|
||||
});
|
||||
|
||||
// We are doing this until we have actually set a Box for this `BoxSlot`.
|
||||
|
|
566
components/layout_2020/table/layout.rs
Normal file
566
components/layout_2020/table/layout.rs
Normal file
|
@ -0,0 +1,566 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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 app_units::Au;
|
||||
use euclid::num::Zero;
|
||||
use log::warn;
|
||||
use style::logical_geometry::WritingMode;
|
||||
use style::values::computed::{CSSPixelLength, Length, LengthOrAuto};
|
||||
use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage};
|
||||
|
||||
use super::{Table, TableSlot, TableSlotCell};
|
||||
use crate::context::LayoutContext;
|
||||
use crate::formatting_contexts::IndependentLayout;
|
||||
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
|
||||
use crate::geom::{LogicalRect, LogicalSides, LogicalVec2};
|
||||
use crate::positioned::{PositioningContext, PositioningContextLength};
|
||||
use crate::sizing::ContentSizes;
|
||||
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
|
||||
use crate::table::TableSlotCoordinates;
|
||||
use crate::ContainingBlock;
|
||||
|
||||
/// A result of a final or speculative layout of a single cell in
|
||||
/// the table. Note that this is only done for slots that are not
|
||||
/// covered by spans or empty.
|
||||
struct CellLayout {
|
||||
layout: IndependentLayout,
|
||||
padding: LogicalSides<Length>,
|
||||
border: LogicalSides<Length>,
|
||||
positioning_context: PositioningContext,
|
||||
rowspan: usize,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
assignable_width: Au,
|
||||
column_sizes: Vec<Au>,
|
||||
row_sizes: Vec<Au>,
|
||||
cells_laid_out: Vec<Vec<Option<CellLayout>>>,
|
||||
}
|
||||
|
||||
impl<'a> TableLayout<'a> {
|
||||
fn new(table: &'a Table) -> TableLayout {
|
||||
Self {
|
||||
table,
|
||||
pbm: PaddingBorderMargin::zero(),
|
||||
assignable_width: Au::zero(),
|
||||
column_sizes: Vec::new(),
|
||||
row_sizes: Vec::new(),
|
||||
cells_laid_out: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Do the preparatory steps to table layout, measuring cells and distributing sizes
|
||||
/// to all columns and rows.
|
||||
fn compute_measures(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
positioning_context: &mut PositioningContext,
|
||||
containing_block: &ContainingBlock,
|
||||
) {
|
||||
let (inline_content_sizes, column_content_sizes) = self
|
||||
.table
|
||||
.compute_inline_content_sizes(layout_context, containing_block.style.writing_mode);
|
||||
|
||||
self.calculate_assignable_table_width(containing_block, inline_content_sizes);
|
||||
self.column_sizes =
|
||||
self.distribute_width_to_columns(column_content_sizes, containing_block);
|
||||
self.do_row_layout_first_pass(layout_context, containing_block, positioning_context);
|
||||
self.distribute_height_to_rows();
|
||||
}
|
||||
|
||||
fn calculate_assignable_table_width(
|
||||
&mut self,
|
||||
containing_block: &ContainingBlock,
|
||||
inline_content_sizes: ContentSizes,
|
||||
) {
|
||||
let grid_min_inline_size = inline_content_sizes.min_content;
|
||||
let grid_max_inline_size = inline_content_sizes.max_content;
|
||||
|
||||
self.pbm = self.table.style.padding_border_margin(containing_block);
|
||||
let content_box_size = self
|
||||
.table
|
||||
.style
|
||||
.content_box_size(containing_block, &self.pbm);
|
||||
let min_content_sizes = self
|
||||
.table
|
||||
.style
|
||||
.content_min_box_size(containing_block, &self.pbm)
|
||||
.auto_is(Length::zero);
|
||||
|
||||
// https://drafts.csswg.org/css-tables/#used-min-width-of-table
|
||||
// > The used min-width of a table is the greater of the resolved min-width, CAPMIN, and GRIDMIN.
|
||||
let used_min_width_of_table = grid_min_inline_size.max(min_content_sizes.inline);
|
||||
|
||||
// https://drafts.csswg.org/css-tables/#used-width-of-table
|
||||
// > The used width of a table depends on the columns and captions widths as follows:
|
||||
// > * If the table-root’s width property has a computed value (resolving to
|
||||
// > resolved-table-width) other than auto, the used width is the greater of
|
||||
// > resolved-table-width, and the used min-width of the table.
|
||||
// > * If the table-root has 'width: auto', the used width is the greater of min(GRIDMAX,
|
||||
// > the table’s containing block width), the used min-width of the table.
|
||||
let used_width_of_table = match content_box_size.inline {
|
||||
LengthPercentage(length_percentage) => length_percentage.max(used_min_width_of_table),
|
||||
Auto => grid_max_inline_size
|
||||
.min(containing_block.inline_size)
|
||||
.max(used_min_width_of_table),
|
||||
};
|
||||
|
||||
self.assignable_width = used_width_of_table.into();
|
||||
}
|
||||
|
||||
/// Distribute width to columns, performing step 2.4 of table layout from
|
||||
/// <https://drafts.csswg.org/css-tables/#table-layout-algorithm>.
|
||||
fn distribute_width_to_columns(
|
||||
&self,
|
||||
column_content_sizes: Vec<ContentSizes>,
|
||||
containing_block: &ContainingBlock,
|
||||
) -> Vec<Au> {
|
||||
if self.table.slots.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// > First, each column of the table is assigned a sizing type:
|
||||
// > * percent-column: a column whose any constraint is defined to use a percentage only
|
||||
// > (with a value different from 0%)
|
||||
// > * pixel-column: column whose any constraint is defined to use a defined length only
|
||||
// > (and is not a percent-column)
|
||||
// > * auto-column: any other column
|
||||
// >
|
||||
// > Then, valid sizing methods are to be assigned to the columns by sizing type, yielding
|
||||
// > the following sizing-guesses:
|
||||
// >
|
||||
// > * The min-content sizing-guess is the set of column width assignments where
|
||||
// > each column is assigned its min-content width.
|
||||
// > * The min-content-percentage sizing-guess is the set of column width assignments where:
|
||||
// > * each percent-column is assigned the larger of:
|
||||
// > * its intrinsic percentage width times the assignable width and
|
||||
// > * its min-content width.
|
||||
// > * all other columns are assigned their min-content width.
|
||||
// > * The min-content-specified sizing-guess is the set of column width assignments where:
|
||||
// > * each percent-column is assigned the larger of:
|
||||
// > * its intrinsic percentage width times the assignable width and
|
||||
// > * its min-content width
|
||||
// > * any other column that is constrained is assigned its max-content width
|
||||
// > * all other columns are assigned their min-content width.
|
||||
// > * The max-content sizing-guess is the set of column width assignments where:
|
||||
// > * each percent-column is assigned the larger of:
|
||||
// > * its intrinsic percentage width times the assignable width and
|
||||
// > * its min-content width
|
||||
// > * all other columns are assigned their max-content width.
|
||||
let mut min_content_sizing_guesses = Vec::new();
|
||||
let mut min_content_percentage_sizing_guesses = Vec::new();
|
||||
let mut min_content_specified_sizing_guesses = Vec::new();
|
||||
let mut max_content_sizing_guesses = Vec::new();
|
||||
|
||||
for column_idx in 0..self.table.size.width {
|
||||
let coords = TableSlotCoordinates::new(column_idx, 0);
|
||||
let cell = match self.table.resolve_first_cell(coords) {
|
||||
Some(cell) => cell,
|
||||
None => {
|
||||
min_content_sizing_guesses.push(Au::zero());
|
||||
min_content_percentage_sizing_guesses.push(Au::zero());
|
||||
min_content_specified_sizing_guesses.push(Au::zero());
|
||||
max_content_sizing_guesses.push(Au::zero());
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
let inline_size = cell
|
||||
.style
|
||||
.box_size(containing_block.style.writing_mode)
|
||||
.inline;
|
||||
let min_and_max_content = &column_content_sizes[column_idx];
|
||||
let min_content_width = min_and_max_content.min_content;
|
||||
let max_content_width = min_and_max_content.max_content;
|
||||
|
||||
let (
|
||||
min_content_percentage_sizing_guess,
|
||||
min_content_specified_sizing_guess,
|
||||
max_content_sizing_guess,
|
||||
) = match inline_size {
|
||||
LengthPercentage(length_percentage) if length_percentage.has_percentage() => {
|
||||
let percent_guess = min_content_width
|
||||
.max(length_percentage.resolve(self.assignable_width.into()));
|
||||
(percent_guess, percent_guess, percent_guess)
|
||||
},
|
||||
LengthPercentage(_) => (min_content_width, max_content_width, max_content_width),
|
||||
Auto => (min_content_width, min_content_width, max_content_width),
|
||||
};
|
||||
|
||||
min_content_sizing_guesses.push(min_content_width.into());
|
||||
min_content_percentage_sizing_guesses.push(min_content_percentage_sizing_guess.into());
|
||||
min_content_specified_sizing_guesses.push(min_content_specified_sizing_guess.into());
|
||||
max_content_sizing_guesses.push(max_content_sizing_guess.into());
|
||||
}
|
||||
|
||||
// > If the assignable table width is less than or equal to the max-content sizing-guess, the
|
||||
// > used widths of the columns must be the linear combination (with weights adding to 1) of
|
||||
// > the two consecutive sizing-guesses whose width sums bound the available width.
|
||||
//
|
||||
// > Otherwise, the used widths of the columns are the result of starting from the max-content
|
||||
// > sizing-guess and distributing the excess width to the columns of the table according to
|
||||
// > the rules for distributing excess width to columns (for used width).
|
||||
fn sum(guesses: &[Au]) -> Au {
|
||||
guesses.iter().fold(Au::zero(), |sum, guess| sum + *guess)
|
||||
}
|
||||
|
||||
let max_content_sizing_sum = sum(&max_content_sizing_guesses);
|
||||
if self.assignable_width >= max_content_sizing_sum {
|
||||
self.distribute_extra_width_to_columns(
|
||||
&mut max_content_sizing_guesses,
|
||||
max_content_sizing_sum,
|
||||
);
|
||||
return max_content_sizing_guesses;
|
||||
}
|
||||
let min_content_specified_sizing_sum = sum(&min_content_specified_sizing_guesses);
|
||||
if self.assignable_width == min_content_specified_sizing_sum {
|
||||
return min_content_specified_sizing_guesses;
|
||||
}
|
||||
let min_content_percentage_sizing_sum = sum(&min_content_percentage_sizing_guesses);
|
||||
if self.assignable_width == min_content_percentage_sizing_sum {
|
||||
return min_content_percentage_sizing_guesses;
|
||||
}
|
||||
let min_content_sizes_sum = sum(&min_content_sizing_guesses);
|
||||
if self.assignable_width <= min_content_sizes_sum {
|
||||
return min_content_sizing_guesses;
|
||||
}
|
||||
|
||||
let bounds = |sum_a, sum_b| self.assignable_width > sum_a && self.assignable_width < sum_b;
|
||||
let blend = |a: &[Au], sum_a: Au, b: &[Au], sum_b: Au| {
|
||||
// First convert the Au units to f32 in order to do floating point division.
|
||||
let weight_a =
|
||||
(self.assignable_width - sum_b).to_f32_px() / (sum_a - sum_b).to_f32_px();
|
||||
let weight_b = 1.0 - weight_a;
|
||||
a.iter()
|
||||
.zip(b.iter())
|
||||
.map(|(guess_a, guess_b)| {
|
||||
(guess_a.scale_by(weight_a)) + (guess_b.scale_by(weight_b))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
if bounds(min_content_sizes_sum, min_content_percentage_sizing_sum) {
|
||||
return blend(
|
||||
&min_content_sizing_guesses,
|
||||
min_content_sizes_sum,
|
||||
&min_content_percentage_sizing_guesses,
|
||||
min_content_percentage_sizing_sum,
|
||||
);
|
||||
}
|
||||
|
||||
if bounds(
|
||||
min_content_percentage_sizing_sum,
|
||||
min_content_specified_sizing_sum,
|
||||
) {
|
||||
return blend(
|
||||
&min_content_percentage_sizing_guesses,
|
||||
min_content_percentage_sizing_sum,
|
||||
&min_content_specified_sizing_guesses,
|
||||
min_content_specified_sizing_sum,
|
||||
);
|
||||
}
|
||||
|
||||
assert!(bounds(
|
||||
min_content_specified_sizing_sum,
|
||||
max_content_sizing_sum
|
||||
));
|
||||
blend(
|
||||
&min_content_specified_sizing_guesses,
|
||||
min_content_specified_sizing_sum,
|
||||
&max_content_sizing_guesses,
|
||||
max_content_sizing_sum,
|
||||
)
|
||||
}
|
||||
|
||||
fn distribute_extra_width_to_columns(
|
||||
&self,
|
||||
max_content_sizing_guesses: &mut Vec<Au>,
|
||||
max_content_sum: Au,
|
||||
) {
|
||||
// The simplest distribution algorithm, until we have support for proper extra space
|
||||
// distribution is to equally distribute the extra space.
|
||||
let ratio_factor = 1.0 / max_content_sizing_guesses.len() as f32;
|
||||
let extra_space_for_all_columns =
|
||||
(self.assignable_width - max_content_sum).scale_by(ratio_factor);
|
||||
for guess in max_content_sizing_guesses.iter_mut() {
|
||||
*guess += extra_space_for_all_columns;
|
||||
}
|
||||
}
|
||||
|
||||
fn do_row_layout_first_pass(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
containing_block: &ContainingBlock,
|
||||
parent_positioning_context: &mut PositioningContext,
|
||||
) {
|
||||
for row_index in 0..self.table.slots.len() {
|
||||
let row = &self.table.slots[row_index];
|
||||
let mut cells_laid_out_row = Vec::new();
|
||||
for column_index in 0..row.len() {
|
||||
let cell = match &row[column_index] {
|
||||
TableSlot::Cell(cell) => cell,
|
||||
_ => {
|
||||
cells_laid_out_row.push(None);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
let mut total_width = Au::zero();
|
||||
for width_index in column_index..column_index + cell.colspan {
|
||||
total_width += self.column_sizes[width_index];
|
||||
}
|
||||
|
||||
let border = cell.style.border_width(containing_block.style.writing_mode);
|
||||
let padding = cell
|
||||
.style
|
||||
.padding(containing_block.style.writing_mode)
|
||||
.percentages_relative_to(Length::zero());
|
||||
let inline_border_padding_sum = border.inline_sum() + padding.inline_sum();
|
||||
let mut total_width: CSSPixelLength =
|
||||
Length::from(total_width) - inline_border_padding_sum;
|
||||
total_width = total_width.max(Length::zero());
|
||||
|
||||
let containing_block_for_children = ContainingBlock {
|
||||
inline_size: total_width,
|
||||
block_size: LengthOrAuto::Auto,
|
||||
style: &cell.style,
|
||||
};
|
||||
let collect_for_nearest_positioned_ancestor =
|
||||
parent_positioning_context.collects_for_nearest_positioned_ancestor();
|
||||
let mut positioning_context =
|
||||
PositioningContext::new_for_subtree(collect_for_nearest_positioned_ancestor);
|
||||
|
||||
let layout = cell.contents.layout(
|
||||
layout_context,
|
||||
&mut positioning_context,
|
||||
&containing_block_for_children,
|
||||
);
|
||||
cells_laid_out_row.push(Some(CellLayout {
|
||||
layout,
|
||||
padding,
|
||||
border,
|
||||
positioning_context,
|
||||
rowspan: cell.rowspan,
|
||||
}))
|
||||
}
|
||||
self.cells_laid_out.push(cells_laid_out_row);
|
||||
}
|
||||
}
|
||||
|
||||
fn distribute_height_to_rows(&mut self) {
|
||||
for row_index in 0..self.table.size.height {
|
||||
let mut max_row_height = Au::zero();
|
||||
for column_index in 0..self.table.size.width {
|
||||
let coords = TableSlotCoordinates::new(column_index, row_index);
|
||||
self.table
|
||||
.resolve_first_cell_coords(coords)
|
||||
.map(|resolved_coords| {
|
||||
let cell = self.cells_laid_out[resolved_coords.y][resolved_coords.x]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let total_height = cell.layout.content_block_size +
|
||||
cell.border.block_sum().into() +
|
||||
cell.padding.block_sum().into();
|
||||
// TODO: We are accounting for rowspan=0 here, but perhaps this should be
|
||||
// translated into a real rowspan during table box tree construction.
|
||||
let effective_rowspan = match cell.rowspan {
|
||||
0 => (self.table.size.height - resolved_coords.y) as i32,
|
||||
rowspan => rowspan as i32,
|
||||
};
|
||||
max_row_height = (total_height / effective_rowspan).max(max_row_height)
|
||||
});
|
||||
}
|
||||
self.row_sizes.push(max_row_height);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lay out the table of this [`TableLayout`] into fragments. This should only be be called
|
||||
/// after calling [`TableLayout.compute_measures`].
|
||||
fn layout_into_box_fragments(
|
||||
mut self,
|
||||
positioning_context: &mut PositioningContext,
|
||||
) -> (Vec<Fragment>, Au) {
|
||||
assert_eq!(self.table.size.height, self.row_sizes.len());
|
||||
assert_eq!(self.table.size.width, self.column_sizes.len());
|
||||
|
||||
let mut fragments = Vec::new();
|
||||
let mut row_offset = Au::zero();
|
||||
for row_index in 0..self.table.size.height {
|
||||
let mut column_offset = Au::zero();
|
||||
let row_size = self.row_sizes[row_index];
|
||||
|
||||
for column_index in 0..self.table.size.width {
|
||||
let column_size = self.column_sizes[column_index];
|
||||
let layout = match self.cells_laid_out[row_index][column_index].take() {
|
||||
Some(layout) => layout,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let cell = match self.table.slots[row_index][column_index] {
|
||||
TableSlot::Cell(ref cell) => cell,
|
||||
_ => {
|
||||
warn!("Did not find a non-spanned cell at index with layout.");
|
||||
continue;
|
||||
},
|
||||
};
|
||||
|
||||
let cell_rect: LogicalRect<Length> = LogicalRect {
|
||||
start_corner: LogicalVec2 {
|
||||
inline: column_offset.into(),
|
||||
block: row_offset.into(),
|
||||
},
|
||||
size: LogicalVec2 {
|
||||
inline: column_size.into(),
|
||||
block: row_size.into(),
|
||||
},
|
||||
};
|
||||
|
||||
fragments.push(Fragment::Box(cell.create_fragment(
|
||||
layout,
|
||||
cell_rect,
|
||||
positioning_context,
|
||||
)));
|
||||
|
||||
column_offset += column_size;
|
||||
}
|
||||
|
||||
row_offset += row_size;
|
||||
}
|
||||
|
||||
(fragments, row_offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl Table {
|
||||
fn inline_content_sizes_for_cell_at(
|
||||
&self,
|
||||
coords: TableSlotCoordinates,
|
||||
layout_context: &LayoutContext,
|
||||
writing_mode: WritingMode,
|
||||
) -> ContentSizes {
|
||||
let cell = match self.resolve_first_cell(coords) {
|
||||
Some(cell) => cell,
|
||||
None => return ContentSizes::zero(),
|
||||
};
|
||||
|
||||
let sizes = cell.inline_content_sizes(layout_context, writing_mode);
|
||||
sizes.map(|size| size / cell.colspan as f32)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_inline_content_sizes(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
writing_mode: WritingMode,
|
||||
) -> (ContentSizes, Vec<ContentSizes>) {
|
||||
let mut final_size = ContentSizes::zero();
|
||||
let column_content_sizes = (0..self.size.width)
|
||||
.map(|column_idx| {
|
||||
let coords = TableSlotCoordinates::new(column_idx, 0);
|
||||
let content_sizes =
|
||||
self.inline_content_sizes_for_cell_at(coords, layout_context, writing_mode);
|
||||
final_size.min_content += content_sizes.min_content;
|
||||
final_size.max_content += content_sizes.max_content;
|
||||
content_sizes
|
||||
})
|
||||
.collect();
|
||||
(final_size, column_content_sizes)
|
||||
}
|
||||
|
||||
pub(crate) fn inline_content_sizes(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
writing_mode: WritingMode,
|
||||
) -> ContentSizes {
|
||||
self.compute_inline_content_sizes(layout_context, writing_mode)
|
||||
.0
|
||||
}
|
||||
|
||||
pub(crate) fn layout(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
positioning_context: &mut PositioningContext,
|
||||
containing_block: &ContainingBlock,
|
||||
) -> IndependentLayout {
|
||||
let mut table_layout = TableLayout::new(&self);
|
||||
table_layout.compute_measures(layout_context, positioning_context, containing_block);
|
||||
let (fragments, content_block_size) =
|
||||
table_layout.layout_into_box_fragments(positioning_context);
|
||||
IndependentLayout {
|
||||
fragments,
|
||||
content_block_size,
|
||||
last_inflow_baseline_offset: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TableSlotCell {
|
||||
pub(crate) fn inline_content_sizes(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
writing_mode: WritingMode,
|
||||
) -> ContentSizes {
|
||||
let border = self.style.border_width(writing_mode);
|
||||
let padding = self.style.padding(writing_mode);
|
||||
|
||||
// For padding, a cyclic percentage is resolved against zero for determining intrinsic size
|
||||
// contributions.
|
||||
// https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
|
||||
let zero = Length::zero();
|
||||
let border_padding_sum = border.inline_sum() +
|
||||
padding.inline_start.resolve(zero) +
|
||||
padding.inline_end.resolve(zero);
|
||||
|
||||
let mut sizes = self
|
||||
.contents
|
||||
.contents
|
||||
.inline_content_sizes(layout_context, writing_mode);
|
||||
sizes.min_content += border_padding_sum;
|
||||
sizes.max_content += border_padding_sum;
|
||||
sizes
|
||||
}
|
||||
|
||||
fn create_fragment(
|
||||
&self,
|
||||
mut layout: CellLayout,
|
||||
cell_rect: LogicalRect<Length>,
|
||||
positioning_context: &mut PositioningContext,
|
||||
) -> BoxFragment {
|
||||
// This must be scoped to this function because it conflicts with euclid's Zero.
|
||||
use style::Zero as StyleZero;
|
||||
|
||||
let fragments = layout.layout.fragments;
|
||||
let content_rect = cell_rect.deflate(&(&layout.padding + &layout.border));
|
||||
|
||||
// Adjust the static position of all absolute children based on the
|
||||
// final content rect of this fragment.
|
||||
layout
|
||||
.positioning_context
|
||||
.adjust_static_position_of_hoisted_fragments_with_offset(
|
||||
&content_rect.start_corner,
|
||||
PositioningContextLength::zero(),
|
||||
);
|
||||
positioning_context.append(layout.positioning_context);
|
||||
|
||||
BoxFragment::new(
|
||||
self.base_fragment_info,
|
||||
self.style.clone(),
|
||||
fragments,
|
||||
content_rect,
|
||||
layout.padding,
|
||||
layout.border,
|
||||
LogicalSides::zero(), /* margin */
|
||||
None, /* clearance */
|
||||
layout
|
||||
.layout
|
||||
.last_inflow_baseline_offset
|
||||
.map(|baseline| baseline.into()),
|
||||
CollapsedBlockMargins::zero(),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -2,46 +2,81 @@
|
|||
* 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/. */
|
||||
|
||||
//! Table layout.
|
||||
//! See https://html.spec.whatwg.org/multipage/table-processing-model.
|
||||
//! HTML Tables (╯°□°)╯︵ ┻━┻.
|
||||
//!
|
||||
//! See <https://html.spec.whatwg.org/multipage/table-processing-model> and
|
||||
//! <https://drafts.csswg.org/css-tables>. This is heavily based on the latter specification, but
|
||||
//! note that it is still an Editor's Draft, so there is no guarantee that what is implemented here
|
||||
//! matches other browsers or the current specification.
|
||||
|
||||
mod construct;
|
||||
mod layout;
|
||||
|
||||
use app_units::Au;
|
||||
pub(crate) use construct::AnonymousTableContent;
|
||||
pub use construct::TableBuilder;
|
||||
use euclid::num::Zero;
|
||||
use euclid::{Point2D, UnknownUnit, Vector2D};
|
||||
use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
|
||||
use serde::Serialize;
|
||||
use servo_arc::Arc;
|
||||
use style::properties::ComputedValues;
|
||||
use style_traits::dom::OpaqueNode;
|
||||
|
||||
use super::flow::BlockFormattingContext;
|
||||
use crate::context::LayoutContext;
|
||||
use crate::flow::BlockContainer;
|
||||
use crate::formatting_contexts::IndependentLayout;
|
||||
use crate::positioned::PositioningContext;
|
||||
use crate::sizing::ContentSizes;
|
||||
use crate::ContainingBlock;
|
||||
use crate::fragment_tree::BaseFragmentInfo;
|
||||
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub type TableSize = Size2D<usize, UnknownUnit>;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Table {
|
||||
/// The style of this table.
|
||||
#[serde(skip_serializing)]
|
||||
style: Arc<ComputedValues>,
|
||||
|
||||
/// The content of the slots of this table.
|
||||
pub slots: Vec<Vec<TableSlot>>,
|
||||
|
||||
/// The size of this table.
|
||||
pub size: TableSize,
|
||||
}
|
||||
|
||||
impl Table {
|
||||
pub(crate) fn inline_content_sizes(&self) -> ContentSizes {
|
||||
ContentSizes::zero()
|
||||
pub(crate) fn new(style: Arc<ComputedValues>) -> Self {
|
||||
Self {
|
||||
style,
|
||||
slots: Vec::new(),
|
||||
size: TableSize::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn layout(
|
||||
/// Return the slot at the given coordinates, if it exists in the table, otherwise
|
||||
/// return None.
|
||||
fn get_slot<'a>(&'a self, coords: TableSlotCoordinates) -> Option<&'a TableSlot> {
|
||||
self.slots.get(coords.y)?.get(coords.x)
|
||||
}
|
||||
|
||||
fn resolve_first_cell_coords(
|
||||
&self,
|
||||
_layout_context: &LayoutContext,
|
||||
_positioning_context: &mut PositioningContext,
|
||||
_containing_block: &ContainingBlock,
|
||||
) -> IndependentLayout {
|
||||
IndependentLayout {
|
||||
fragments: Vec::new(),
|
||||
content_block_size: Au::zero(),
|
||||
last_inflow_baseline_offset: None,
|
||||
coords: TableSlotCoordinates,
|
||||
) -> Option<TableSlotCoordinates> {
|
||||
match self.get_slot(coords) {
|
||||
Some(&TableSlot::Cell(_)) => Some(coords),
|
||||
Some(&TableSlot::Spanned(ref offsets)) => Some(coords - offsets[0]),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_first_cell(&self, coords: TableSlotCoordinates) -> Option<&TableSlotCell> {
|
||||
let resolved_coords = match self.resolve_first_cell_coords(coords) {
|
||||
Some(coords) => coords,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let slot = self.get_slot(resolved_coords);
|
||||
match slot {
|
||||
Some(&TableSlot::Cell(ref cell)) => Some(cell),
|
||||
_ => unreachable!(
|
||||
"Spanned slot should not point to an empty cell or another spanned slot."
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,12 +96,16 @@ pub struct TableSlotCell {
|
|||
/// the remaining rows in the row group.
|
||||
rowspan: usize,
|
||||
|
||||
// An id used for testing purposes.
|
||||
pub id: u8,
|
||||
/// The style of this table cell.
|
||||
#[serde(skip_serializing)]
|
||||
style: Arc<ComputedValues>,
|
||||
|
||||
/// The [`BaseFragmentInfo`] of this cell.
|
||||
base_fragment_info: BaseFragmentInfo,
|
||||
}
|
||||
|
||||
impl TableSlotCell {
|
||||
pub fn mock_for_testing(id: u8, colspan: usize, rowspan: usize) -> Self {
|
||||
pub fn mock_for_testing(id: usize, colspan: usize, rowspan: usize) -> Self {
|
||||
Self {
|
||||
contents: BlockFormattingContext {
|
||||
contents: BlockContainer::BlockLevelBoxes(Vec::new()),
|
||||
|
@ -74,9 +113,15 @@ impl TableSlotCell {
|
|||
},
|
||||
colspan,
|
||||
rowspan,
|
||||
id,
|
||||
style: ComputedValues::initial_values().to_arc(),
|
||||
base_fragment_info: BaseFragmentInfo::new_for_node(OpaqueNode(id)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the node id of this cell's [`BaseFragmentInfo`]. This is used for unit tests.
|
||||
pub fn node_id(&self) -> usize {
|
||||
self.base_fragment_info.tag.node.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
@ -12,9 +12,9 @@ mod tables {
|
|||
table.slots.iter().map(|row| row.len()).collect()
|
||||
}
|
||||
|
||||
fn slot_is_cell_with_id(slot: &TableSlot, id: u8) -> bool {
|
||||
fn slot_is_cell_with_id(slot: &TableSlot, id: usize) -> bool {
|
||||
match slot {
|
||||
TableSlot::Cell(TableSlotCell { id: cell_id, .. }) if id == *cell_id => true,
|
||||
TableSlot::Cell(cell) if cell.node_id() == id => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -41,14 +41,14 @@ mod tables {
|
|||
|
||||
#[test]
|
||||
fn test_empty_table() {
|
||||
let table_builder = TableBuilder::default();
|
||||
let table_builder = TableBuilder::new_for_tests();
|
||||
let table = table_builder.finish();
|
||||
assert!(table.slots.is_empty())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_table() {
|
||||
let mut table_builder = TableBuilder::default();
|
||||
let mut table_builder = TableBuilder::new_for_tests();
|
||||
|
||||
table_builder.start_row();
|
||||
table_builder.add_cell(TableSlotCell::mock_for_testing(1, 1, 1));
|
||||
|
@ -71,7 +71,7 @@ mod tables {
|
|||
|
||||
#[test]
|
||||
fn test_simple_rowspan() {
|
||||
let mut table_builder = TableBuilder::default();
|
||||
let mut table_builder = TableBuilder::new_for_tests();
|
||||
|
||||
table_builder.start_row();
|
||||
table_builder.add_cell(TableSlotCell::mock_for_testing(1, 1, 1));
|
||||
|
@ -100,7 +100,7 @@ mod tables {
|
|||
|
||||
#[test]
|
||||
fn test_simple_colspan() {
|
||||
let mut table_builder = TableBuilder::default();
|
||||
let mut table_builder = TableBuilder::new_for_tests();
|
||||
|
||||
table_builder.start_row();
|
||||
table_builder.add_cell(TableSlotCell::mock_for_testing(1, 3, 1));
|
||||
|
@ -162,7 +162,7 @@ mod tables {
|
|||
|
||||
#[test]
|
||||
fn test_simple_table_model_error() {
|
||||
let mut table_builder = TableBuilder::default();
|
||||
let mut table_builder = TableBuilder::new_for_tests();
|
||||
|
||||
table_builder.start_row();
|
||||
table_builder.add_cell(TableSlotCell::mock_for_testing(1, 1, 1));
|
||||
|
@ -194,7 +194,7 @@ mod tables {
|
|||
|
||||
#[test]
|
||||
fn test_simple_rowspan_0() {
|
||||
let mut table_builder = TableBuilder::default();
|
||||
let mut table_builder = TableBuilder::new_for_tests();
|
||||
|
||||
table_builder.start_row();
|
||||
table_builder.add_cell(TableSlotCell::mock_for_testing(1, 1, 1));
|
||||
|
@ -232,7 +232,7 @@ mod tables {
|
|||
|
||||
#[test]
|
||||
fn test_incoming_rowspans() {
|
||||
let mut table_builder = TableBuilder::default();
|
||||
let mut table_builder = TableBuilder::new_for_tests();
|
||||
|
||||
table_builder.start_row();
|
||||
table_builder.add_cell(TableSlotCell::mock_for_testing(1, 1, 1));
|
||||
|
|
1
tests/wpt/meta/css/CSS2/floats-clear/__dir__.ini
Normal file
1
tests/wpt/meta/css/CSS2/floats-clear/__dir__.ini
Normal file
|
@ -0,0 +1 @@
|
|||
prefs: ["layout.tables.enabled:true"]
|
|
@ -1,2 +0,0 @@
|
|||
[floats-038.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[floats-149.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[margin-collapse-165.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[margin-collapse-166.xht]
|
||||
expected: FAIL
|
1
tests/wpt/meta/css/CSS2/floats/__dir__.ini
Normal file
1
tests/wpt/meta/css/CSS2/floats/__dir__.ini
Normal file
|
@ -0,0 +1 @@
|
|||
prefs: ["layout.tables.enabled:true"]
|
|
@ -0,0 +1,2 @@
|
|||
[floats-in-table-caption-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[floats-wrap-bfc-001-left-overflow.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[floats-wrap-bfc-001-right-overflow.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[floats-wrap-bfc-003-left-overflow.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[floats-wrap-bfc-003-right-overflow.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[floats-wrap-bfc-007.xht]
|
||||
expected: FAIL
|
1
tests/wpt/meta/css/CSS2/tables/__dir__.ini
Normal file
1
tests/wpt/meta/css/CSS2/tables/__dir__.ini
Normal file
|
@ -0,0 +1 @@
|
|||
prefs: ["layout.tables.enabled:true"]
|
|
@ -1,2 +0,0 @@
|
|||
[border-collapse-offset-002.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-008.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-009.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-010.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-011.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-012.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-013.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[empty-cells-applies-to-017.xht]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[height-table-cell-001.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-width-inline-table-001.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-width-inline-table-001a.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-width-inline-table-001b.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-width-inline-table-001c.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-width-inline-table-001d.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[height-width-inline-table-001e.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[separated-border-model-003a.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[separated-border-model-007.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[separated-border-model-008.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[separated-border-model-009.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-anonymous-objects-017.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-anonymous-objects-018.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-anonymous-objects-019.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-anonymous-objects-020.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-anonymous-objects-117.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-anonymous-objects-119.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +1,2 @@
|
|||
[table-anonymous-objects-211.xht]
|
||||
expected: FAIL
|
||||
expected: CRASH
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[table-column-rendering-001.xht]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[table-column-rendering-002.xht]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[table-visual-layout-017.xht]
|
||||
expected: FAIL
|
1
tests/wpt/meta/css/css-tables/__dir__.ini
Normal file
1
tests/wpt/meta/css/css-tables/__dir__.ini
Normal file
|
@ -0,0 +1 @@
|
|||
prefs: ["layout.tables.enabled:true"]
|
|
@ -0,0 +1,2 @@
|
|||
[absolute-tables-013.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[absolute-tables-014.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[absolute-tables-015.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[anonymous-table-ws-001.html]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[auto-layout-calc-width-001.html]
|
||||
[#theTable 1]
|
||||
expected: FAIL
|
|
@ -13,3 +13,15 @@
|
|||
|
||||
[Table-column-group is 100px tall]
|
||||
expected: FAIL
|
||||
|
||||
[Table-row is 100px wide]
|
||||
expected: FAIL
|
||||
|
||||
[Table-row-group is 100px wide]
|
||||
expected: FAIL
|
||||
|
||||
[Table-column is 100px wide]
|
||||
expected: FAIL
|
||||
|
||||
[Table-column-group is 100px wide]
|
||||
expected: FAIL
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
[Control test: Table height is 120px]
|
||||
expected: FAIL
|
||||
|
||||
[First (empty) table-row-group is 0px wide]
|
||||
expected: FAIL
|
||||
|
||||
[First (empty) table-row-group should be located at 10px left]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -22,3 +19,6 @@
|
|||
|
||||
[Second table-row-group should be located at 10px top]
|
||||
expected: FAIL
|
||||
|
||||
[Second table-row-group is 100px tall]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[caption-side-1.html]
|
||||
[Caption-side inherits and reorder captions properly]
|
||||
expected: FAIL
|
||||
|
||||
[Multiple captions can be rendered]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[col-definite-max-size-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[col-definite-min-size-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[col-definite-size-001.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[collapsed-border-paint-phase-001.html]
|
||||
expected: FAIL
|
|
@ -5,9 +5,6 @@
|
|||
[main table 2]
|
||||
expected: FAIL
|
||||
|
||||
[main table 3]
|
||||
expected: FAIL
|
||||
|
||||
[main table 4]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -34,3 +31,6 @@
|
|||
|
||||
[main table 13]
|
||||
expected: FAIL
|
||||
|
||||
[main table 9]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
[fixed-layout-1.html]
|
||||
[Table-layout:fixed distributes width equally to all auto-columns]
|
||||
[Table-layout:fixed grows the table if needed for minimum-width]
|
||||
expected: FAIL
|
||||
|
||||
[Table-layout:fixed deals with columns generated by subsequent rows]
|
||||
expected: FAIL
|
||||
|
||||
[Table-layout:fixed ignores sizes specified by subsequent rows]
|
||||
[Table-layout:fixed takes visual order into account, not dom order]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,15 +1,6 @@
|
|||
[fixed-layout-2.html]
|
||||
[Table-layout:fixed is not applied when width is auto]
|
||||
expected: FAIL
|
||||
|
||||
[Table-layout:fixed reports fixed when width is auto]
|
||||
expected: FAIL
|
||||
|
||||
[Table-layout:fixed is not applied when width is max-content]
|
||||
expected: FAIL
|
||||
|
||||
[Table-layout:fixed reports fixed when width is max-content]
|
||||
expected: FAIL
|
||||
|
||||
[Table-layout:fixed is applied when width is min-content]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[fixed-layout-calc-width-001.html]
|
||||
[#theTable 1]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[fixup-dynamic-anonymous-inline-table-001.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[fixup-dynamic-anonymous-inline-table-002.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[fixup-dynamic-anonymous-inline-table-003.html]
|
||||
expected: FAIL
|
|
@ -4,3 +4,9 @@
|
|||
|
||||
[Checking intermediate min-content height for span 1 (3)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 1 (2)]
|
||||
expected: FAIL
|
||||
|
||||
[Checking intermediate min-content height for span 1 (4)]
|
||||
expected: FAIL
|
||||
|
|
|
@ -11,11 +11,5 @@
|
|||
[Unresolvable percentage widths are resolved as auto in first pass (replaced elements)]
|
||||
expected: FAIL
|
||||
|
||||
[Unresolvable percentage widths are resolved as auto in first pass (unscrollable overflow)]
|
||||
expected: FAIL
|
||||
|
||||
[Unresolvable percentage widths are resolved as auto in first pass (scrollable overflow)]
|
||||
expected: FAIL
|
||||
|
||||
[Unresolvable percentage heights are resolved as 0px in first pass (scrollable overflow)]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[td-different-subpixel-padding-in-same-row-vertical-rl.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[td-different-subpixel-padding-in-same-row.html]
|
||||
expected: FAIL
|
|
@ -31,3 +31,6 @@
|
|||
|
||||
[Border-spacing is added between any two unmerged rows (5)]
|
||||
expected: FAIL
|
||||
|
||||
[Explicitely defined rows are not merged]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[percent-height-overflow-auto-in-restricted-block-size-cell.html]
|
||||
[#container 1]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[percent-height-overflow-auto-in-unrestricted-block-size-cell.tentative.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[row-group-margin-border-padding.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[row-group-order.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[row-margin-border-padding.html]
|
||||
expected: FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[rules-groups.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[subpixel-table-cell-height-001.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[table-cell-child-overflow-measure.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[table-cell-overflow-auto.html]
|
||||
expected: FAIL
|
|
@ -1,4 +1,5 @@
|
|||
[table-model-fixup-2.html]
|
||||
expected: CRASH
|
||||
[Replaced elements inside a table cannot be table-row and are considered inline -- input elements (top)]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[table-model-fixup.html]
|
||||
[1.1. Children of a table-column are treated as if they had display: none.]
|
||||
expected: FAIL
|
||||
|
||||
[1.2. Children of a table-column-group which are not a table-column are treated as if they had display: none.]
|
||||
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. (1/2)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -22,3 +16,9 @@
|
|||
|
||||
[3.2. An anonymous table or inline-table box must be generated around each sequence of consecutive proper table child box which are misparented]
|
||||
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. (3/3)]
|
||||
expected: FAIL
|
||||
|
||||
[1.4. Anonymous inline boxes which contains only white space and are between two immediate siblings *each* of which is a table-non-root element, are treated as if they had display: none.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[baseline-table.html]
|
||||
[.container 1]
|
||||
expected: FAIL
|
||||
|
||||
[.container 2]
|
||||
expected: FAIL
|
||||
|
||||
[.container 3]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -17,18 +11,6 @@
|
|||
[.container 6]
|
||||
expected: FAIL
|
||||
|
||||
[.container 7]
|
||||
expected: FAIL
|
||||
|
||||
[.container 8]
|
||||
expected: FAIL
|
||||
|
||||
[.container 9]
|
||||
expected: FAIL
|
||||
|
||||
[.container 10]
|
||||
expected: FAIL
|
||||
|
||||
[.container 11]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -40,6 +22,3 @@
|
|||
|
||||
[.container 14]
|
||||
expected: FAIL
|
||||
|
||||
[.container 15]
|
||||
expected: FAIL
|
||||
|
|
|
@ -22,3 +22,15 @@
|
|||
|
||||
[table 10]
|
||||
expected: FAIL
|
||||
|
||||
[table 4]
|
||||
expected: FAIL
|
||||
|
||||
[table 5]
|
||||
expected: FAIL
|
||||
|
||||
[table 12]
|
||||
expected: FAIL
|
||||
|
||||
[table 13]
|
||||
expected: FAIL
|
||||
|
|
|
@ -59,9 +59,6 @@
|
|||
[table 20]
|
||||
expected: FAIL
|
||||
|
||||
[table 21]
|
||||
expected: FAIL
|
||||
|
||||
[table 22]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -64,3 +64,6 @@
|
|||
|
||||
[table 24]
|
||||
expected: FAIL
|
||||
|
||||
[table 20]
|
||||
expected: FAIL
|
||||
|
|
|
@ -5,11 +5,5 @@
|
|||
[table 2]
|
||||
expected: FAIL
|
||||
|
||||
[table 3]
|
||||
expected: FAIL
|
||||
|
||||
[table 5]
|
||||
expected: FAIL
|
||||
|
||||
[table 6]
|
||||
expected: FAIL
|
||||
|
|
|
@ -31,3 +31,6 @@
|
|||
|
||||
[table 12]
|
||||
expected: FAIL
|
||||
|
||||
[table 10]
|
||||
expected: FAIL
|
||||
|
|
|
@ -2,20 +2,5 @@
|
|||
[.t 6]
|
||||
expected: FAIL
|
||||
|
||||
[.t 10]
|
||||
expected: FAIL
|
||||
|
||||
[.t 11]
|
||||
expected: FAIL
|
||||
|
||||
[.t 12]
|
||||
expected: FAIL
|
||||
|
||||
[.t 13]
|
||||
expected: FAIL
|
||||
|
||||
[.t 14]
|
||||
expected: FAIL
|
||||
|
||||
[.t 15]
|
||||
expected: FAIL
|
||||
|
|
|
@ -13,3 +13,21 @@
|
|||
|
||||
[table 11]
|
||||
expected: FAIL
|
||||
|
||||
[table 1]
|
||||
expected: FAIL
|
||||
|
||||
[table 4]
|
||||
expected: FAIL
|
||||
|
||||
[table 5]
|
||||
expected: FAIL
|
||||
|
||||
[table 6]
|
||||
expected: FAIL
|
||||
|
||||
[table 7]
|
||||
expected: FAIL
|
||||
|
||||
[table 10]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[th-text-align.html]
|
||||
expected: FAIL
|
|
@ -1,6 +1,3 @@
|
|||
[visibility-collapse-col-005.html]
|
||||
[col visibility:collapse doesn't change table height]
|
||||
expected: FAIL
|
||||
|
||||
[col visibility:collapse changes table width]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[visibility-collapse-colspan-001.html]
|
||||
[col visibility:collapse doesn't change table height]
|
||||
expected: FAIL
|
||||
|
||||
[col visibility:collapse changes table width]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[visibility-collapse-colspan-002.html]
|
||||
[col visibility:collapse doesn't change table height]
|
||||
expected: FAIL
|
||||
|
||||
[col visibility:collapse changes table width]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[visibility-collapse-row-005.html]
|
||||
[collapsed row should not contribute to overflow]
|
||||
expected: FAIL
|
||||
|
||||
[collapsed section should not contribute to overflow]
|
||||
expected: FAIL
|
|
@ -1,9 +1,3 @@
|
|||
[visibility-collapse-row-group-001.html]
|
||||
[row group visibility:collapse changes table height]
|
||||
expected: FAIL
|
||||
|
||||
[the first row should be collapsed]
|
||||
expected: FAIL
|
||||
|
||||
[the second row should be collapsed]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[visibility-collapse-rowspan-002-border-separate.html]
|
||||
[spanning row visibility:collapse makes row height 0]
|
||||
expected: FAIL
|
||||
|
||||
[spanning cell shrinks to sum of remaining three rows' height]
|
||||
expected: FAIL
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue