layout: Add support for table captions (#32657)

This adds initial support for table captions. To do this, the idea of
the table wrapper becomes a bit more concrete. Even so, the wrapper is
still reponsible for allocating space for the grid's border and padding,
as those properties are specified on the wrapper and not grid in CSS.

In order to account for this weirdness of HTML/CSS captions and grid are
now laid out and placed with a negative offset in the table wrapper
content rect.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2024-07-03 20:24:19 +02:00 committed by GitHub
parent f8e4ae6040
commit 959ffad99a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
76 changed files with 551 additions and 322 deletions

26
Cargo.lock generated
View file

@ -1288,7 +1288,7 @@ dependencies = [
[[package]]
name = "derive_common"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"darling",
"proc-macro2",
@ -3751,7 +3751,7 @@ dependencies = [
[[package]]
name = "malloc_size_of"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"accountable-refcell",
"app_units",
@ -5400,7 +5400,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.24.0"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"bitflags 2.6.0",
"cssparser",
@ -5688,7 +5688,7 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.2.0"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"nodrop",
"serde",
@ -5698,7 +5698,7 @@ dependencies = [
[[package]]
name = "servo_atoms"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"string_cache",
"string_cache_codegen",
@ -5885,7 +5885,7 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "size_of_test"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"static_assertions",
]
@ -6026,7 +6026,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_prefs"
version = "0.1.0"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
[[package]]
name = "strict-num"
@ -6063,7 +6063,7 @@ dependencies = [
[[package]]
name = "style"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"app_units",
"arrayvec",
@ -6121,7 +6121,7 @@ dependencies = [
[[package]]
name = "style_config"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"lazy_static",
]
@ -6129,7 +6129,7 @@ dependencies = [
[[package]]
name = "style_derive"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"darling",
"derive_common",
@ -6160,7 +6160,7 @@ dependencies = [
[[package]]
name = "style_traits"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"app_units",
"bitflags 2.6.0",
@ -6510,7 +6510,7 @@ dependencies = [
[[package]]
name = "to_shmem"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"cssparser",
"servo_arc",
@ -6523,7 +6523,7 @@ dependencies = [
[[package]]
name = "to_shmem_derive"
version = "0.0.1"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#e8a14bce9453ad5939eb0c34de150ad2f10d1bc2"
source = "git+https://github.com/servo/stylo?branch=2024-05-31#141446aeea5a0741927c3c83de54a9512e12f4ab"
dependencies = [
"darling",
"derive_common",

View file

@ -1367,9 +1367,9 @@ struct ContainingBlockPaddingAndBorder<'a> {
max_box_size: LogicalVec2<Option<Length>>,
}
struct ResolvedMargins {
pub(crate) struct ResolvedMargins {
/// Used value for the margin properties, as exposed in getComputedStyle().
margin: LogicalSides<Au>,
pub margin: LogicalSides<Au>,
/// Distance between the border box and the containing block on the inline-start side.
/// This is typically the same as the inline-start margin, but can be greater when
@ -1377,7 +1377,7 @@ struct ResolvedMargins {
/// The reason we aren't just adjusting the used margin-inline-start is that
/// this shouldn't be observable via getComputedStyle().
/// <https://drafts.csswg.org/css-align/#justify-self-property>
effective_margin_inline_start: Au,
pub effective_margin_inline_start: Au,
}
/// Given the style for an in-flow box and its containing block, determine the containing
@ -1440,7 +1440,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
/// Note that in the presence of floats, this shouldn't be used for a block-level box
/// that establishes an independent formatting context (or is replaced), since the
/// margins could then be incorrect.
fn solve_margins(
pub(crate) fn solve_margins(
containing_block: &ContainingBlock<'_>,
pbm: &PaddingBorderMargin,
inline_size: Au,

View file

@ -7,6 +7,7 @@ use serde::Serialize;
use servo_arc::Arc;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use style::values::specified::text::TextDecorationLine;
use crate::context::LayoutContext;
@ -59,12 +60,21 @@ pub(crate) enum NonReplacedFormattingContextContents {
/// The baselines of a layout or a [`crate::fragment_tree::BoxFragment`]. Some layout
/// uses the first and some layout uses the last.
#[derive(Debug, Default, Serialize)]
#[derive(Clone, Copy, Debug, Default, Serialize)]
pub(crate) struct Baselines {
pub first: Option<Au>,
pub last: Option<Au>,
}
impl Baselines {
pub(crate) fn offset(&self, block_offset: Au) -> Baselines {
Self {
first: self.first.map(|first| first + block_offset),
last: self.last.map(|last| last + block_offset),
}
}
}
pub(crate) struct IndependentLayout {
pub fragments: Vec<Fragment>,
@ -83,15 +93,16 @@ pub(crate) struct IndependentLayout {
}
impl IndependentFormattingContext {
pub fn construct<'dom>(
pub fn construct<'dom, Node: NodeExt<'dom>>(
context: &LayoutContext,
node_and_style_info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
node_and_style_info: &NodeAndStyleInfo<Node>,
display_inside: DisplayInside,
contents: Contents,
propagated_text_decoration_line: TextDecorationLine,
) -> Self {
match contents {
Contents::NonReplaced(non_replaced_contents) => {
let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
let contents = match display_inside {
DisplayInside::Flow { is_list_item } |
DisplayInside::FlowRoot { is_list_item } => {
@ -114,17 +125,27 @@ impl IndependentFormattingContext {
))
},
DisplayInside::Table => {
let table_grid_style = context
.shared_context()
.stylist
.style_for_anonymous::<Node::ConcreteElement>(
&context.shared_context().guards,
&PseudoElement::ServoTableGrid,
&node_and_style_info.style,
);
base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
NonReplacedFormattingContextContents::Table(Table::construct(
context,
node_and_style_info,
table_grid_style,
non_replaced_contents,
propagated_text_decoration_line,
))
},
};
Self::NonReplaced(NonReplacedFormattingContext {
base_fragment_info: node_and_style_info.into(),
style: Arc::clone(&node_and_style_info.style),
base_fragment_info,
content_sizes: None,
contents,
})

View file

@ -85,22 +85,23 @@ bitflags! {
#[derive(Clone, Copy, Debug, Serialize)]
pub(crate) struct FragmentFlags: u8 {
/// Whether or not the node that created this fragment is a `<body>` element on an HTML document.
const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 0b00000001;
const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 1 << 0;
/// Whether or not the node that created this Fragment is a `<br>` element.
const IS_BR_ELEMENT = 0b00000010;
const IS_BR_ELEMENT = 1 << 1;
/// Whether or not the node that created was a `<table>`, `<th>` or
/// `<td>` element. Note that this does *not* include elements with
/// `display: table` or `display: table-cell`.
const IS_TABLE_TH_OR_TD_ELEMENT = 0b00000100;
const IS_TABLE_TH_OR_TD_ELEMENT = 1 << 2;
/// Whether or not this Fragment was created to contain a replaced element or is
/// a replaced element.
const IS_REPLACED = 0b00001000;
const IS_REPLACED = 1 << 3;
/// Whether or not this Fragment was created to contain a list item marker
/// with a used value of `list-style-position: outside`.
const IS_OUTSIDE_LIST_ITEM_MARKER = 0b00010000;
/// Avoid painting the fragment, this is used for empty table cells when 'empty-cells' is 'hide'.
/// This flag doesn't avoid hit-testing.
const DO_NOT_PAINT = 0b00100000;
const IS_OUTSIDE_LIST_ITEM_MARKER = 1 << 4;
/// Avoid painting the borders, backgrounds, and drop shadow for this fragment, this is used
/// for empty table cells when 'empty-cells' is 'hide' and also table wrappers. This flag
/// doesn't avoid hit-testing nor does it prevent the painting outlines.
const DO_NOT_PAINT = 1 << 5;
}
}

View file

@ -215,14 +215,18 @@ impl BoxFragment {
)
}
pub fn padding_rect(&self) -> LogicalRect<Au> {
pub(crate) fn padding_rect(&self) -> LogicalRect<Au> {
self.content_rect.inflate(&self.padding)
}
pub fn border_rect(&self) -> LogicalRect<Au> {
pub(crate) fn border_rect(&self) -> LogicalRect<Au> {
self.padding_rect().inflate(&self.border)
}
pub(crate) fn margin_rect(&self) -> LogicalRect<Au> {
self.border_rect().inflate(&self.margin)
}
pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!(
"Box\

View file

@ -370,6 +370,19 @@ impl<T: Add<Output = T> + Copy> Add<LogicalSides<T>> for LogicalSides<T> {
}
}
impl<T: Sub<Output = T> + Copy> Sub<LogicalSides<T>> for LogicalSides<T> {
type Output = LogicalSides<T>;
fn sub(self, other: Self) -> Self::Output {
LogicalSides {
inline_start: self.inline_start - other.inline_start,
inline_end: self.inline_end - other.inline_end,
block_start: self.block_start - other.block_start,
block_end: self.block_end - other.block_end,
}
}
}
impl<T: Neg<Output = T> + Copy> Neg for LogicalSides<T> {
type Output = LogicalSides<T>;
fn neg(self) -> Self::Output {
@ -384,7 +397,7 @@ impl<T: Neg<Output = T> + Copy> Neg for LogicalSides<T> {
impl<T: Zero> LogicalSides<T> {
pub(crate) fn zero() -> LogicalSides<T> {
LogicalSides {
Self {
inline_start: T::zero(),
inline_end: T::zero(),
block_start: T::zero(),
@ -395,7 +408,7 @@ impl<T: Zero> LogicalSides<T> {
impl From<LogicalSides<CSSPixelLength>> for LogicalSides<Au> {
fn from(value: LogicalSides<CSSPixelLength>) -> Self {
LogicalSides {
Self {
inline_start: value.inline_start.into(),
inline_end: value.inline_end.into(),
block_start: value.block_start.into(),
@ -406,7 +419,7 @@ impl From<LogicalSides<CSSPixelLength>> for LogicalSides<Au> {
impl From<LogicalSides<Au>> for LogicalSides<CSSPixelLength> {
fn from(value: LogicalSides<Au>) -> Self {
LogicalSides {
Self {
inline_start: value.inline_start.into(),
inline_end: value.inline_end.into(),
block_start: value.block_start.into(),
@ -435,7 +448,7 @@ impl<T> LogicalRect<T> {
T: Add<Output = T> + Copy,
T: Sub<Output = T> + Copy,
{
LogicalRect {
Self {
start_corner: LogicalVec2 {
inline: self.start_corner.inline - sides.inline_start,
block: self.start_corner.block - sides.block_start,

View file

@ -134,6 +134,13 @@ impl PaddingBorderMargin {
padding_border_sums: LogicalVec2::zero(),
}
}
pub(crate) fn border_padding_start(&self) -> LogicalVec2<Au> {
LogicalVec2 {
inline: self.border.inline_start + self.padding.inline_start,
block: self.border.block_start + self.padding.block_start,
}
}
}
pub(crate) trait ComputedValuesExt {

View file

@ -15,8 +15,8 @@ use style::str::char_is_whitespace;
use style::values::specified::TextDecorationLine;
use super::{
Table, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset, TableTrack,
TableTrackGroup, TableTrackGroupType,
Table, TableCaption, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset,
TableTrack, TableTrackGroup, TableTrackGroupType,
};
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, NodeExt};
@ -73,12 +73,14 @@ impl Table {
pub(crate) fn construct<'dom>(
context: &LayoutContext,
info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
grid_style: Arc<ComputedValues>,
contents: NonReplacedContents,
propagated_text_decoration_line: TextDecorationLine,
) -> Self {
let text_decoration_line =
propagated_text_decoration_line | info.style.clone_text_decoration_line();
let mut traversal = TableBuilderTraversal::new(context, info, text_decoration_line);
let mut traversal =
TableBuilderTraversal::new(context, info, grid_style, text_decoration_line);
contents.traverse(context, info, &mut traversal);
traversal.finish()
}
@ -92,7 +94,7 @@ impl Table {
where
Node: crate::dom::NodeExt<'dom>,
{
let anonymous_style = context
let grid_and_wrapper_style = context
.shared_context()
.stylist
.style_for_anonymous::<Node::ConcreteElement>(
@ -100,10 +102,14 @@ impl Table {
&PseudoElement::ServoAnonymousTable,
&parent_info.style,
);
let anonymous_info = parent_info.new_anonymous(anonymous_style.clone());
let anonymous_info = parent_info.new_anonymous(grid_and_wrapper_style.clone());
let mut table_builder =
TableBuilderTraversal::new(context, &anonymous_info, propagated_text_decoration_line);
let mut table_builder = TableBuilderTraversal::new(
context,
&anonymous_info,
grid_and_wrapper_style.clone(),
propagated_text_decoration_line,
);
for content in contents {
match content {
@ -128,7 +134,7 @@ impl Table {
IndependentFormattingContext::NonReplaced(NonReplacedFormattingContext {
base_fragment_info: (&anonymous_info).into(),
style: anonymous_style,
style: grid_and_wrapper_style,
content_sizes: None,
contents: NonReplacedFormattingContextContents::Table(table),
})
@ -229,15 +235,23 @@ pub struct TableBuilder {
}
impl TableBuilder {
pub(super) fn new(style: Arc<ComputedValues>) -> Self {
pub(super) fn new(
style: Arc<ComputedValues>,
grid_style: Arc<ComputedValues>,
base_fragment_info: BaseFragmentInfo,
) -> Self {
Self {
table: Table::new(style),
table: Table::new(style, grid_style, base_fragment_info),
incoming_rowspans: Vec::new(),
}
}
pub fn new_for_tests() -> Self {
Self::new(ComputedValues::initial_values().to_arc())
Self::new(
ComputedValues::initial_values().to_arc(),
ComputedValues::initial_values().to_arc(),
BaseFragmentInfo::anonymous(),
)
}
pub fn last_row_index_in_row_group_at_row_n(&self, n: usize) -> usize {
@ -622,13 +636,14 @@ where
pub(crate) fn new(
context: &'style LayoutContext<'style>,
info: &'style NodeAndStyleInfo<Node>,
grid_style: Arc<ComputedValues>,
text_decoration_line: TextDecorationLine,
) -> Self {
TableBuilderTraversal {
context,
info,
current_text_decoration_line: text_decoration_line,
builder: TableBuilder::new(info.style.clone()),
builder: TableBuilder::new(info.style.clone(), grid_style, info.into()),
current_anonymous_row_content: Vec::new(),
current_row_group_index: None,
}
@ -825,9 +840,28 @@ where
::std::mem::forget(box_slot);
},
DisplayLayoutInternal::TableCaption => {
// TODO: Handle table captions.
let contents = match contents.try_into() {
Ok(non_replaced_contents) => {
BlockFormattingContext::construct(
self.context,
info,
non_replaced_contents,
self.current_text_decoration_line,
false, /* is_list_item */
)
},
Err(_replaced) => {
unreachable!("Replaced should not have a LayoutInternal display type.");
},
};
self.builder.table.captions.push(TableCaption {
contents,
style: info.style.clone(),
base_fragment_info: info.into(),
});
// We are doing this until we have actually set a Box for this `BoxSlot`.
::std::mem::forget(box_slot);
::std::mem::forget(box_slot)
},
DisplayLayoutInternal::TableCell => {
self.current_anonymous_row_content

View file

@ -10,6 +10,7 @@ use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIter
use servo_arc::Arc;
use style::computed_values::border_collapse::T as BorderCollapse;
use style::computed_values::box_sizing::T as BoxSizing;
use style::computed_values::caption_side::T as CaptionSide;
use style::computed_values::empty_cells::T as EmptyCells;
use style::computed_values::visibility::T as Visibility;
use style::logical_geometry::WritingMode;
@ -19,8 +20,9 @@ use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, Verti
use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage};
use style::Zero;
use super::{Table, TableSlot, TableSlotCell, TableTrack, TableTrackGroup};
use super::{Table, TableCaption, TableSlot, TableSlotCell, TableTrack, TableTrackGroup};
use crate::context::LayoutContext;
use crate::flow::{solve_margins, ResolvedMargins};
use crate::formatting_contexts::{Baselines, IndependentLayout};
use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags,
@ -103,6 +105,7 @@ pub(crate) struct TableLayout<'a> {
rows: Vec<RowLayout>,
columns: Vec<ColumnLayout>,
cell_measures: Vec<Vec<LogicalVec2<CellOrTrackMeasure>>>,
table_width: Au,
assignable_width: Au,
final_table_height: Au,
column_measures: Vec<CellOrTrackMeasure>,
@ -143,6 +146,7 @@ impl<'a> TableLayout<'a> {
rows: Vec::new(),
columns: Vec::new(),
cell_measures: Vec::new(),
table_width: Au::zero(),
assignable_width: Au::zero(),
final_table_height: Au::zero(),
column_measures: Vec::new(),
@ -660,11 +664,57 @@ impl<'a> TableLayout<'a> {
grid_min_max
}
/// Compute CAPMIN: <https://drafts.csswg.org/css-tables/#capmin>
fn compute_caption_minimum_inline_size(
&mut self,
layout_context: &LayoutContext,
writing_mode: WritingMode,
) -> Au {
self.table
.captions
.iter()
.map(|caption| {
let padding = caption
.style
.padding(writing_mode)
.percentages_relative_to(Length::zero());
let border = caption.style.border_width(writing_mode);
let margin = caption
.style
.margin(writing_mode)
.percentages_relative_to(Length::zero())
.auto_is(Length::zero);
let padding_border_sums = LogicalVec2 {
inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum())
.into(),
block: (padding.block_sum() + border.block_sum() + margin.block_sum()).into(),
};
let (size, min_size, max_size) =
get_outer_sizes_from_style(&caption.style, writing_mode, &padding_border_sums);
let mut inline_content_sizes = caption
.contents
.contents
.inline_content_sizes(layout_context, writing_mode);
inline_content_sizes.min_content += padding_border_sums.inline;
inline_content_sizes
.min_content
.min(max_size.inline)
.max(size.inline)
.max(min_size.inline)
})
.max()
.unwrap_or_default()
}
fn compute_table_width(
&mut self,
containing_block_for_children: &ContainingBlock,
containing_block_for_table: &ContainingBlock,
grid_min_max: ContentSizes,
caption_minimum_inline_size: Au,
) {
let style = &self.table.style;
self.pbm = style.padding_border_margin(containing_block_for_table);
@ -679,9 +729,9 @@ impl<'a> TableLayout<'a> {
// https://drafts.csswg.org/css-tables/#used-width-of-table
// * If table-root has a computed value for inline-size different than auto:
// use the maximum of the resolved table width and GRIDMIN.
// use the maximum of the resolved table width, GRIDMIN and CAPMIN.
// * If auto: use the resolved_table_width, clamped between GRIDMIN and GRIDMAX,
// but at least as big as min-inline-size.
// but at least as big as min-inline-size and CAPMIN.
// This diverges a little from the specification, but should be equivalent
// (other than using the stretch-fit size instead of the containing block width).
let used_width_of_table = match style
@ -701,6 +751,13 @@ impl<'a> TableLayout<'a> {
},
};
// Padding and border should apply to the table grid, but they are properties of the
// parent element (the table wrapper). In order to account for this, we subtract the
// border and padding inline size from the caption size.
let caption_minimum_inline_size =
caption_minimum_inline_size - self.pbm.padding_border_sums.inline;
self.table_width = used_width_of_table.max(caption_minimum_inline_size);
// > The assignable table width is the used width of the table minus the total horizontal
// > border spacing (if any). This is the width that we will be able to allocate to the
// > columns.
@ -1438,8 +1495,83 @@ impl<'a> TableLayout<'a> {
self.row_sizes = row_sizes;
}
/// Lay out the table of this [`TableLayout`] into fragments. This should only be be called
/// after calling [`TableLayout.compute_measures`].
fn layout_caption(
&mut self,
caption: &TableCaption,
table_pbm: &PaddingBorderMargin,
layout_context: &LayoutContext,
containing_block: &ContainingBlock,
parent_positioning_context: &mut PositioningContext,
) -> BoxFragment {
let pbm = caption.style.padding_border_margin(containing_block);
let box_size = caption.style.content_box_size(containing_block, &pbm);
let max_box_size = caption.style.content_max_box_size(containing_block, &pbm);
let min_box_size = caption
.style
.content_min_box_size(containing_block, &pbm)
.auto_is(Length::zero);
let block_size = box_size.block.map(|block_size| {
block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
});
let table_inline_content_size = self.table_width - pbm.padding_border_sums.inline +
table_pbm.padding_border_sums.inline;
let inline_size = box_size
.inline
.auto_is(|| table_inline_content_size.into())
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
let containing_block_for_children = &ContainingBlock {
inline_size: inline_size.into(),
block_size: block_size.map(|t| t.into()),
style: &caption.style,
};
let mut positioning_context = PositioningContext::new_for_style(&caption.style);
let layout = caption.contents.layout(
layout_context,
positioning_context
.as_mut()
.unwrap_or(parent_positioning_context),
containing_block_for_children,
);
if let Some(positioning_context) = positioning_context.take() {
parent_positioning_context.append(positioning_context);
}
let ResolvedMargins {
margin,
effective_margin_inline_start,
} = solve_margins(containing_block, &pbm, containing_block.inline_size);
let content_rect = LogicalRect {
start_corner: LogicalVec2 {
inline: effective_margin_inline_start +
pbm.border.inline_start +
pbm.padding.inline_start,
block: margin.block_start + pbm.border.block_start + pbm.padding.block_start,
},
size: LogicalVec2 {
inline: inline_size.into(),
block: layout.content_block_size,
},
};
BoxFragment::new(
caption.base_fragment_info,
caption.style.clone(),
layout.fragments,
content_rect,
pbm.padding,
pbm.border,
margin,
None, /* clearance */
CollapsedBlockMargins::zero(),
)
}
/// Lay out the table (grid and captions) of this [`TableLayout`] into fragments. This should
/// only be be called after calling [`TableLayout.compute_measures`].
fn layout(
mut self,
layout_context: &LayoutContext,
@ -1449,18 +1581,144 @@ impl<'a> TableLayout<'a> {
) -> IndependentLayout {
let writing_mode = containing_block_for_children.style.writing_mode;
let grid_min_max = self.compute_grid_min_max(layout_context, writing_mode);
let caption_minimum_inline_size =
self.compute_caption_minimum_inline_size(layout_context, writing_mode);
self.compute_table_width(
containing_block_for_children,
containing_block_for_table,
grid_min_max,
caption_minimum_inline_size,
);
self.distributed_column_widths = self.distribute_width_to_columns();
// The table wrapper is the one that has the CSS properties for the grid's border and padding. This
// weirdness is difficult to express in Servo's layout system. We have the wrapper size itself as if
// those properties applied to it and then just account for the discrepency in sizing here. In reality,
// the wrapper does not draw borders / backgrounds and all of its content (grid and captions) are
// placed with a negative offset in the table wrapper's content box so that they overlap the undrawn
// border / padding area.
//
// TODO: This is a pretty large hack. It would be nicer to actually have the grid sized properly,
// but it works for now.
let table_pbm = self
.table
.style
.padding_border_margin(containing_block_for_table);
let offset_from_wrapper = -table_pbm.padding - table_pbm.border;
let mut current_block_offset = offset_from_wrapper.block_start;
let mut table_layout = IndependentLayout {
fragments: Vec::new(),
content_block_size: Zero::zero(),
content_inline_size_for_table: None,
baselines: Baselines::default(),
};
table_layout
.fragments
.extend(self.table.captions.iter().filter_map(|caption| {
if caption.style.clone_caption_side() != CaptionSide::Top {
return None;
}
let original_positioning_context_length = positioning_context.len();
let mut caption_fragment = self.layout_caption(
caption,
&table_pbm,
layout_context,
containing_block_for_children,
positioning_context,
);
caption_fragment.content_rect.start_corner.inline +=
offset_from_wrapper.inline_start;
caption_fragment.content_rect.start_corner.block += current_block_offset;
current_block_offset += caption_fragment.margin_rect().size.block;
let caption_fragment = Fragment::Box(caption_fragment);
positioning_context.adjust_static_position_of_hoisted_fragments(
&caption_fragment,
original_positioning_context_length,
);
Some(caption_fragment)
}));
let original_positioning_context_length = positioning_context.len();
let mut grid_fragment = self.layout_grid(
layout_context,
&table_pbm,
positioning_context,
containing_block_for_children,
containing_block_for_table,
);
// Take the baseline of the grid fragment, after adjusting it to be in the coordinate system
// of the table wrapper.
table_layout.baselines = grid_fragment
.baselines
.offset(current_block_offset + grid_fragment.content_rect.start_corner.block);
grid_fragment.content_rect.start_corner.inline += offset_from_wrapper.inline_start;
grid_fragment.content_rect.start_corner.block += current_block_offset;
current_block_offset += grid_fragment.border_rect().size.block;
table_layout.content_inline_size_for_table = Some(grid_fragment.content_rect.size.inline);
let grid_fragment = Fragment::Box(grid_fragment);
positioning_context.adjust_static_position_of_hoisted_fragments(
&grid_fragment,
original_positioning_context_length,
);
table_layout.fragments.push(grid_fragment);
table_layout
.fragments
.extend(self.table.captions.iter().filter_map(|caption| {
if caption.style.clone_caption_side() != CaptionSide::Bottom {
return None;
}
let original_positioning_context_length = positioning_context.len();
let mut caption_fragment = self.layout_caption(
caption,
&table_pbm,
layout_context,
containing_block_for_children,
positioning_context,
);
caption_fragment.content_rect.start_corner.inline +=
offset_from_wrapper.inline_start;
caption_fragment.content_rect.start_corner.block += current_block_offset;
current_block_offset += caption_fragment.margin_rect().size.block;
let caption_fragment = Fragment::Box(caption_fragment);
positioning_context.adjust_static_position_of_hoisted_fragments(
&caption_fragment,
original_positioning_context_length,
);
Some(caption_fragment)
}));
table_layout.content_block_size = current_block_offset + offset_from_wrapper.block_end;
table_layout
}
/// Lay out the grid portion of this [`TableLayout`] into fragments. This should only be be
/// called after calling [`TableLayout.compute_measures`].
fn layout_grid(
&mut self,
layout_context: &LayoutContext,
table_pbm: &PaddingBorderMargin,
positioning_context: &mut PositioningContext,
containing_block_for_children: &ContainingBlock,
containing_block_for_table: &ContainingBlock,
) -> BoxFragment {
self.distributed_column_widths = self.distribute_width_to_columns();
self.layout_cells_in_row(
layout_context,
containing_block_for_children,
positioning_context,
);
let writing_mode = containing_block_for_children.style.writing_mode;
let first_layout_row_heights = self.do_first_row_layout(writing_mode);
self.compute_table_height_and_final_row_heights(
first_layout_row_heights,
@ -1471,24 +1729,35 @@ impl<'a> TableLayout<'a> {
assert_eq!(self.table.size.height, self.row_sizes.len());
assert_eq!(self.table.size.width, self.distributed_column_widths.len());
let mut baselines = Baselines::default();
let mut table_fragments = Vec::new();
if self.table.size.width == 0 && self.table.size.height == 0 {
return IndependentLayout {
fragments: table_fragments,
content_block_size: self.final_table_height,
content_inline_size_for_table: Some(self.assignable_width),
baselines,
let content_rect = LogicalRect {
start_corner: table_pbm.border_padding_start(),
size: LogicalVec2 {
inline: self.table_width,
block: self.final_table_height,
},
};
return BoxFragment::new(
self.table.grid_base_fragment_info,
self.table.grid_style.clone(),
Vec::new(),
content_rect,
table_pbm.padding,
table_pbm.border,
LogicalSides::zero(),
None, /* clearance */
CollapsedBlockMargins::zero(),
);
}
let table_and_track_dimensions = TableAndTrackDimensions::new(&self);
let mut table_fragments = Vec::new();
let table_and_track_dimensions = TableAndTrackDimensions::new(self);
self.make_fragments_for_columns_and_column_groups(
&table_and_track_dimensions,
&mut table_fragments,
);
let mut baselines = Baselines::default();
let mut row_group_fragment_layout = None;
for row_index in 0..self.table.size.height {
// From <https://drafts.csswg.org/css-align-3/#baseline-export>
@ -1589,14 +1858,25 @@ impl<'a> TableLayout<'a> {
)));
}
IndependentLayout {
fragments: table_fragments,
content_block_size: table_and_track_dimensions.table_rect.max_block_position(),
content_inline_size_for_table: Some(
table_and_track_dimensions.table_rect.max_inline_position(),
),
baselines,
}
let content_rect = LogicalRect {
start_corner: table_pbm.border_padding_start(),
size: LogicalVec2 {
inline: table_and_track_dimensions.table_rect.max_inline_position(),
block: table_and_track_dimensions.table_rect.max_block_position(),
},
};
BoxFragment::new(
self.table.grid_base_fragment_info,
self.table.grid_style.clone(),
table_fragments,
content_rect,
table_pbm.padding,
table_pbm.border,
LogicalSides::zero(),
None, /* clearance */
CollapsedBlockMargins::zero(),
)
.with_baselines(baselines)
}
fn is_row_collapsed(&self, row_index: usize) -> bool {
@ -1928,6 +2208,7 @@ impl RowGroupFragmentLayout {
row_group_fragment
}
}
struct TableAndTrackDimensions {
/// The rect of the full table, not counting for borders, padding, and margin.
table_rect: LogicalRect<Au>,
@ -2109,7 +2390,32 @@ impl Table {
layout_context: &LayoutContext,
writing_mode: WritingMode,
) -> ContentSizes {
TableLayout::new(self).compute_grid_min_max(layout_context, writing_mode)
let mut layout = TableLayout::new(self);
let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode);
let mut caption_minimum_inline_size =
layout.compute_caption_minimum_inline_size(layout_context, writing_mode);
if caption_minimum_inline_size > table_content_sizes.min_content ||
caption_minimum_inline_size > table_content_sizes.max_content
{
// Padding and border should apply to the table grid, but they will be taken into
// account when computing the inline content sizes of the table wrapper (our parent), so
// this code removes their contribution from the inline content size of the caption.
let padding = self
.style
.padding(writing_mode)
.percentages_relative_to(Length::zero());
let border = self.style.border_width(writing_mode);
caption_minimum_inline_size -= (padding.inline_sum() + border.inline_sum()).into();
table_content_sizes
.min_content
.max_assign(caption_minimum_inline_size);
table_content_sizes
.max_content
.max_assign(caption_minimum_inline_size);
}
table_content_sizes
}
fn get_column_measure_for_column_at_index(

View file

@ -86,10 +86,24 @@ pub type TableSize = Size2D<usize, UnknownUnit>;
#[derive(Debug, Serialize)]
pub struct Table {
/// The style of this table.
/// The style of this table. These are the properties that apply to the "wrapper" ie the element
/// that contains both the grid and the captions. Not all properties are actually used on the
/// wrapper though, such as background and borders, which apply to the grid.
#[serde(skip_serializing)]
style: Arc<ComputedValues>,
/// The style of this table's grid. This is an anonymous style based on the table's style, but
/// eliminating all the properties handled by the "wrapper."
#[serde(skip_serializing)]
grid_style: Arc<ComputedValues>,
/// The [`BaseFragmentInfo`] for this table's grid. This is necessary so that when the
/// grid has a background image, it can be associated with the table's node.
grid_base_fragment_info: BaseFragmentInfo,
/// The captions for this table.
pub captions: Vec<TableCaption>,
/// The column groups for this table.
pub column_groups: Vec<TableTrackGroup>,
@ -114,9 +128,16 @@ pub struct Table {
}
impl Table {
pub(crate) fn new(style: Arc<ComputedValues>) -> Self {
pub(crate) fn new(
style: Arc<ComputedValues>,
grid_style: Arc<ComputedValues>,
base_fragment_info: BaseFragmentInfo,
) -> Self {
Self {
style,
grid_style,
grid_base_fragment_info: base_fragment_info,
captions: Vec::new(),
column_groups: Vec::new(),
columns: Vec::new(),
row_groups: Vec::new(),
@ -291,3 +312,16 @@ impl TableTrackGroup {
self.track_range.is_empty()
}
}
#[derive(Debug, Serialize)]
pub struct TableCaption {
/// The contents of this cell, with its own layout.
contents: BlockFormattingContext,
/// The style of this table cell.
#[serde(skip_serializing)]
style: Arc<ComputedValues>,
/// The [`BaseFragmentInfo`] of this cell.
base_fragment_info: BaseFragmentInfo,
}

View file

@ -231,6 +231,41 @@ svg > * {
display: table-cell;
}
*|*::-servo-table-grid {
all: inherit;
margin: unset;
float: unset;
clear: unset;
position: unset;
z-index: unset;
page-break-before: unset;
page-break-after: unset;
page-break-inside: unset;
vertical-align: unset;
line-height: unset;
transform: unset;
transform-origin: unset;
backface-visibility: unset;
clip: unset;
transform-style: unset;
rotate: unset;
scale: unset;
translate: unset;
align-self: unset;
justify-self: unset;
grid-column-start: unset;
grid-column-end: unset;
grid-row-start: unset;
grid-row-end: unset;
order: unset;
outline: unset;
outline-offset: unset;
column-span: unset;
contain: unset;
container: unset;
scroll-margin: unset;
}
*|*::-servo-legacy-anonymous-block {
display: block;
position: static;

View file

@ -204,7 +204,10 @@ dir, menu, ul { list-style-type: disc; }
table { display: table; }
caption { display: table-caption; }
caption {
display: table-caption;
text-align: center;
}
colgroup, colgroup[hidden] { display: table-column-group; }
col, col[hidden] { display: table-column; }
thead, thead[hidden] { display: table-header-group; }

View file

@ -1,2 +0,0 @@
[border-bottom-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[border-bottom-color-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[border-bottom-width-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[border-top-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[border-top-color-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[border-top-width-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[display-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[color-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[font-variant-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[font-weight-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[after-content-display-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[before-content-display-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[line-height-applies-to-015.xht]
expected: FAIL

View file

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

View file

@ -1,2 +0,0 @@
[list-style-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[list-style-type-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-top-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-bottom-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-left-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-right-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[padding-top-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapsing-in-table-caption-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[margin-collapsing-in-table-caption-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-position-001.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-006.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-007.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-008.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-009.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-010.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-011.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-012.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-013.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-014.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[caption-side-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[letter-spacing-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-decoration-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-indent-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text-transform-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[word-spacing-applies-to-015.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[ortho-table-item-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-as-item-auto-min-width.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-as-item-fixed-min-width.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[table-as-item-inflexible-in-row-1.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[table-as-item-narrow-content.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[table-as-item-specified-width-vertical.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-as-item-wide-content.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[table-item-flex-percentage-min-width.html]
expected: FAIL

View file

@ -8,60 +8,6 @@
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (0.3) should be [initial\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (0.5) should be [bottom\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (0.6) should be [bottom\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (1) should be [bottom\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (1.5) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (-0.3) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (0) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (0.3) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (0.5) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (0.6) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (1) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition: all: property <caption-side> from [initial\] to [bottom\] at (1.5) should be [bottom\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (-0.3) should be [initial\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (0) should be [initial\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (0.3) should be [initial\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (0.5) should be [bottom\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (0.6) should be [bottom\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (1) should be [bottom\]]
expected: FAIL
[CSS Animations: property <caption-side> from [initial\] to [bottom\] at (1.5) should be [bottom\]]
expected: FAIL
[Web Animations: property <caption-side> from [initial\] to [bottom\] at (-0.3) should be [initial\]]
expected: FAIL
@ -83,15 +29,6 @@
[Web Animations: property <caption-side> from [initial\] to [bottom\] at (1.5) should be [bottom\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (-0.3) should be [bottom\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (0) should be [bottom\]]
expected: FAIL
[CSS Transitions: property <caption-side> from [initial\] to [bottom\] at (0.3) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-behavior:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (-0.3) should be [initial\]]
expected: FAIL
@ -101,18 +38,6 @@
[CSS Transitions with transition-behavior:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (0.3) should be [initial\]]
expected: FAIL
[CSS Transitions with transition-behavior:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (0.5) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-behavior:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (0.6) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-behavior:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (1) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-behavior:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (1.5) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (-0.3) should be [initial\]]
expected: FAIL
@ -121,15 +46,3 @@
[CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (0.3) should be [initial\]]
expected: FAIL
[CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (0.5) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (0.6) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (1) should be [bottom\]]
expected: FAIL
[CSS Transitions with transition-property:all and transition-behavor:allow-discrete: property <caption-side> from [initial\] to [bottom\] at (1.5) should be [bottom\]]
expected: FAIL

View file

@ -1,6 +0,0 @@
[caption-side-1.html]
[Caption-side inherits and reorder captions properly]
expected: FAIL
[Multiple captions can be rendered]
expected: FAIL

View file

@ -2,12 +2,6 @@
[Property border-spacing has initial value 0px 0px]
expected: FAIL
[Property caption-side has initial value top]
expected: FAIL
[Property caption-side inherits]
expected: FAIL
[Property table-layout has initial value auto]
expected: FAIL

View file

@ -1,6 +0,0 @@
[caption-side-computed.html]
[Property caption-side value 'top']
expected: FAIL
[Property caption-side value 'bottom']
expected: FAIL

View file

@ -1,6 +0,0 @@
[caption-side-valid.html]
[e.style['caption-side'\] = "top" should set the property value]
expected: FAIL
[e.style['caption-side'\] = "bottom" should set the property value]
expected: FAIL

View file

@ -31,3 +31,12 @@
[Replaced elements outside a table cannot be table-row-group and are considered inline -- input=button elements]
expected: FAIL
[Replaced elements outside a table cannot be table-caption and are considered inline -- input=text elements]
expected: FAIL
[Replaced elements outside a table cannot be table-caption and are considered inline -- input=button elements]
expected: FAIL
[Replaced elements outside a table cannot be table-caption and are considered inline -- input=file elements]
expected: FAIL

View file

@ -8,9 +8,6 @@
[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. (2/3)]
expected: FAIL
[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

View file

@ -8,24 +8,6 @@
[table 3]
expected: FAIL
[table 7]
expected: FAIL
[table 8]
expected: FAIL
[table 9]
expected: FAIL
[table 10]
expected: FAIL
[table 4]
expected: FAIL
[table 5]
expected: FAIL
[table 12]
expected: FAIL

View file

@ -5,9 +5,6 @@
[table 5]
expected: FAIL
[table 16]
expected: FAIL
[table 1]
expected: FAIL

View file

@ -1,2 +0,0 @@
[ws-break-spaces-applies-to-015.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform-transformed-caption-contains-fixed-position.html]
expected: FAIL

View file

@ -1,3 +0,0 @@
[offsetTopLeft-table-caption.html]
[offset* APIs on tables with captions.]
expected: FAIL

View file

@ -2,9 +2,6 @@
[Caption with margin]
expected: FAIL
[Table with separated border]
expected: FAIL
[Table with collapsed border]
expected: FAIL

View file

@ -83,15 +83,6 @@
[list-style-type: lower-roman]
expected: FAIL
[caption-side: top]
expected: FAIL
[caption-side: bottom]
expected: FAIL
[caption-side: inherit]
expected: FAIL
[direction: ltr]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table_caption_bottom_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table_caption_top_a.html]
expected: FAIL