From baf3f756af5942a658691f8e972992f888b1fae9 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Tue, 6 Feb 2024 11:57:42 +0100 Subject: [PATCH] layout: Add support for table rows, columns, rowgroups and colgroups This adds support for table rows, columns, rowgroups and colgroups. There are few additions here: 1. The createion of fragments, which allows script queries and hit testing to work properly. These fragments are empty as all cells are still direct descendants of the table fragment. 2. Properly handling size information from tracks and track groups as well as frustrating rules about reordering rowgroups. 3. Painting a background seemlessly across track groups and groups. This is a thing that isn't done in legacy layout (nor WebKit)! Co-authored-by: Oriol Brufau --- components/layout/table_cell.rs | 4 +- .../layout_2020/display_list/background.rs | 96 ++-- .../layout_2020/display_list/conversions.rs | 22 + components/layout_2020/display_list/mod.rs | 133 +++-- .../display_list/stacking_context.rs | 15 +- components/layout_2020/flow/inline.rs | 6 +- components/layout_2020/flow/mod.rs | 2 +- .../layout_2020/fragment_tree/box_fragment.rs | 14 + .../layout_2020/fragment_tree/fragment.rs | 67 +-- .../fragment_tree/fragment_tree.rs | 70 +-- components/layout_2020/fragment_tree/mod.rs | 2 + .../fragment_tree/positioning_fragment.rs | 100 ++++ components/layout_2020/positioned.rs | 2 +- components/layout_2020/query.rs | 76 +-- components/layout_2020/table/construct.rs | 363 +++++++++++++- components/layout_2020/table/layout.rs | 468 ++++++++++++++---- components/layout_2020/table/mod.rs | 68 +++ components/script/dom/element.rs | 36 +- components/script/dom/htmltablecolelement.rs | 55 +- components/script/dom/virtualmethods.rs | 4 + .../dom/webidls/HTMLTableColElement.webidl | 4 +- components/script/layout_dom/node.rs | 13 +- .../shared/script_layout/wrapper_traits.rs | 6 +- .../table-backgrounds-bs-colgroup-001.xht.ini | 2 - .../table-backgrounds-bs-column-001.xht.ini | 2 - .../table-backgrounds-bs-row-001.xht.ini | 2 - .../table-backgrounds-bs-rowgroup-001.xht.ini | 2 - .../border-collapse-rowspan-cell.html.ini | 2 - .../bounding-box-computation-1.html.ini | 12 - .../bounding-box-computation-2.html.ini | 12 - .../bounding-box-computation-3.html.ini | 12 - .../col-definite-max-size-001.html.ini | 2 + .../col-definite-min-size-001.html.ini | 2 + .../css-tables/col-definite-size-001.html.ini | 2 + .../computing-row-measure-1.html.ini | 3 - .../css-tables/html-to-css-mapping-1.html.ini | 3 - .../paint/col-paint-htb-rtl.html.ini | 2 + .../css-tables/table-model-fixup-2.html.ini | 9 + .../css/css-tables/table-model-fixup.html.ini | 6 +- .../tentative/baseline-table.html.ini | 3 + .../tentative/colspan-redistribution.html.ini | 3 + .../tentative/element-sizing.html.ini | 3 - .../rowspan-height-redistribution.html.ini | 18 - .../tentative/td-box-sizing-003.html.ini | 6 - ...visibility-collapse-row-group-001.html.ini | 6 + ...lapse-rowspan-002-border-separate.html.ini | 3 + .../visibility-collapse-rowspan-002.html.ini | 3 + ...lapse-rowspan-003-border-separate.html.ini | 3 + .../visibility-collapse-rowspan-003.html.ini | 3 + ...lity-collapse-rowspan-004-dynamic.html.ini | 6 + .../HTMLTableColElement.html.ini | 6 - .../meta/html/dom/idlharness.https.html.ini | 9 - .../meta/html/dom/reflection-tabular.html.ini | 384 -------------- 53 files changed, 1309 insertions(+), 848 deletions(-) create mode 100644 components/layout_2020/fragment_tree/positioning_fragment.rs delete mode 100644 tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-colgroup-001.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-column-001.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-row-001.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-rowgroup-001.xht.ini delete mode 100644 tests/wpt/meta/css/css-tables/border-collapse-rowspan-cell.html.ini create mode 100644 tests/wpt/meta/css/css-tables/col-definite-max-size-001.html.ini create mode 100644 tests/wpt/meta/css/css-tables/col-definite-min-size-001.html.ini create mode 100644 tests/wpt/meta/css/css-tables/col-definite-size-001.html.ini create mode 100644 tests/wpt/meta/css/css-tables/paint/col-paint-htb-rtl.html.ini create mode 100644 tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003-border-separate.html.ini create mode 100644 tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003.html.ini delete mode 100644 tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLTableColElement.html.ini diff --git a/components/layout/table_cell.rs b/components/layout/table_cell.rs index 7d4148181f3f3..cf8c7d3f5bb12 100644 --- a/components/layout/table_cell.rs +++ b/components/layout/table_cell.rs @@ -73,8 +73,8 @@ impl TableCellFlow { TableCellFlow { block_flow: BlockFlow::from_fragment(fragment), collapsed_borders: CollapsedBordersForCell::new(), - column_span: node.get_colspan(), - row_span: node.get_rowspan(), + column_span: node.get_colspan().unwrap_or(1), + row_span: node.get_rowspan().unwrap_or(1), visible, } } diff --git a/components/layout_2020/display_list/background.rs b/components/layout_2020/display_list/background.rs index bbae424d5a038..ac8017f79d891 100644 --- a/components/layout_2020/display_list/background.rs +++ b/components/layout_2020/display_list/background.rs @@ -36,62 +36,68 @@ fn get_cyclic(values: &[T], layer_index: usize) -> &T { &values[layer_index % values.len()] } -pub(super) enum Source<'a> { - Fragment, - Canvas { - style: &'a ComputedValues, - - // Theoretically the painting area is the infinite 2D plane, - // but WebRender doesn’t really do infinite so this is the part of it that can be visible. - painting_area: units::LayoutRect, - }, +pub(super) struct BackgroundPainter<'a> { + pub style: &'a ComputedValues, + pub positioning_area_override: Option, + pub painting_area_override: Option, } -pub(super) fn painting_area<'a>( - fragment_builder: &'a super::BuilderForBoxFragment, - source: &'a Source, - builder: &mut super::DisplayListBuilder, - layer_index: usize, -) -> (&'a units::LayoutRect, wr::CommonItemProperties) { - let fb = fragment_builder; - let (painting_area, clip) = match source { - Source::Canvas { painting_area, .. } => (painting_area, None), - Source::Fragment => { - let b = fb.fragment.style.get_background(); - match get_cyclic(&b.background_clip.0, layer_index) { - Clip::ContentBox => (fb.content_rect(), fb.content_edge_clip(builder)), - Clip::PaddingBox => (fb.padding_rect(), fb.padding_edge_clip(builder)), - Clip::BorderBox => (&fb.border_rect, fb.border_edge_clip(builder)), +impl<'a> BackgroundPainter<'a> { + pub(super) fn painting_area( + &self, + fragment_builder: &'a super::BuilderForBoxFragment, + builder: &mut super::DisplayListBuilder, + layer_index: usize, + ) -> (&units::LayoutRect, wr::CommonItemProperties) { + let fb = fragment_builder; + let (painting_area, clip) = match self.painting_area_override { + Some(ref painting_area) => (painting_area, None), + None if self.positioning_area_override.is_none() => { + let b = self.style.get_background(); + match get_cyclic(&b.background_clip.0, layer_index) { + Clip::ContentBox => (fb.content_rect(), fb.content_edge_clip(builder)), + Clip::PaddingBox => (fb.padding_rect(), fb.padding_edge_clip(builder)), + Clip::BorderBox => (&fb.border_rect, fb.border_edge_clip(builder)), + } + }, + None => (&fb.border_rect, fb.border_edge_clip(builder)), + }; + + // The 'backgound-clip' property maps directly to `clip_rect` in `CommonItemProperties`: + let mut common = builder.common_properties(*painting_area, &fb.fragment.style); + if let Some(clip_chain_id) = clip { + common.clip_id = wr::ClipId::ClipChain(clip_chain_id) + } + (painting_area, common) + } + + pub(super) fn positioning_area( + &self, + fragment_builder: &'a super::BuilderForBoxFragment, + layer_index: usize, + ) -> &units::LayoutRect { + self.positioning_area_override.as_ref().unwrap_or_else(|| { + match get_cyclic( + &self.style.get_background().background_origin.0, + layer_index, + ) { + Origin::ContentBox => fragment_builder.content_rect(), + Origin::PaddingBox => fragment_builder.padding_rect(), + Origin::BorderBox => &fragment_builder.border_rect, } - }, - }; - // The 'backgound-clip' property maps directly to `clip_rect` in `CommonItemProperties`: - let mut common = builder.common_properties(*painting_area, &fb.fragment.style); - if let Some(clip_chain_id) = clip { - common.clip_id = wr::ClipId::ClipChain(clip_chain_id) + }) } - (painting_area, common) } pub(super) fn layout_layer( fragment_builder: &mut super::BuilderForBoxFragment, - source: &Source, + painter: &BackgroundPainter, builder: &mut super::DisplayListBuilder, layer_index: usize, intrinsic: IntrinsicSizes, ) -> Option { - let style = match *source { - Source::Canvas { style, .. } => style, - Source::Fragment => &fragment_builder.fragment.style, - }; - let b = style.get_background(); - let (painting_area, common) = painting_area(fragment_builder, source, builder, layer_index); - - let positioning_area = match get_cyclic(&b.background_origin.0, layer_index) { - Origin::ContentBox => fragment_builder.content_rect(), - Origin::PaddingBox => fragment_builder.padding_rect(), - Origin::BorderBox => &fragment_builder.border_rect, - }; + let (painting_area, common) = painter.painting_area(fragment_builder, builder, layer_index); + let positioning_area = painter.positioning_area(fragment_builder, layer_index); // https://drafts.csswg.org/css-backgrounds/#background-size enum ContainOrCover { @@ -117,6 +123,8 @@ pub(super) fn layout_layer( } tile_size }; + + let b = painter.style.get_background(); let mut tile_size = match get_cyclic(&b.background_size.0, layer_index) { Size::Contain => size_contain_or_cover(ContainOrCover::Contain), Size::Cover => size_contain_or_cover(ContainOrCover::Cover), diff --git a/components/layout_2020/display_list/conversions.rs b/components/layout_2020/display_list/conversions.rs index a0434422f9dea..e659a97f373a3 100644 --- a/components/layout_2020/display_list/conversions.rs +++ b/components/layout_2020/display_list/conversions.rs @@ -2,6 +2,7 @@ * 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 style::color::AbsoluteColor; use style::computed_values::mix_blend_mode::T as ComputedMixBlendMode; use style::computed_values::text_decoration_style::T as ComputedTextDecorationStyle; @@ -86,6 +87,13 @@ impl ToWebRender for PhysicalPoint { } } +impl ToWebRender for PhysicalPoint { + type Type = units::LayoutPoint; + fn to_webrender(&self) -> Self::Type { + units::LayoutPoint::new(self.x.to_f32_px(), self.y.to_f32_px()) + } +} + impl ToWebRender for PhysicalSize { type Type = units::LayoutSize; fn to_webrender(&self) -> Self::Type { @@ -93,6 +101,13 @@ impl ToWebRender for PhysicalSize { } } +impl ToWebRender for PhysicalSize { + type Type = units::LayoutSize; + fn to_webrender(&self) -> Self::Type { + units::LayoutSize::new(self.width.to_f32_px(), self.height.to_f32_px()) + } +} + impl ToWebRender for PhysicalRect { type Type = units::LayoutRect; fn to_webrender(&self) -> Self::Type { @@ -100,6 +115,13 @@ impl ToWebRender for PhysicalRect { } } +impl ToWebRender for PhysicalRect { + type Type = units::LayoutRect; + fn to_webrender(&self) -> Self::Type { + units::LayoutRect::new(self.origin.to_webrender(), self.size.to_webrender()) + } +} + impl ToWebRender for PhysicalSides { type Type = units::LayoutSideOffsets; fn to_webrender(&self) -> Self::Type { diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index 42da68c5e706c..0066b25f01b7d 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -28,7 +28,7 @@ use crate::context::LayoutContext; use crate::display_list::conversions::ToWebRender; use crate::display_list::stacking_context::StackingContextSection; use crate::fragment_tree::{BoxFragment, Fragment, FragmentTree, Tag, TextFragment}; -use crate::geom::{PhysicalPoint, PhysicalRect}; +use crate::geom::{LogicalRect, PhysicalPoint, PhysicalRect}; use crate::replaced::IntrinsicSizes; use crate::style_ext::ComputedValuesExt; @@ -37,6 +37,7 @@ mod conversions; mod gradient; mod stacking_context; +use background::BackgroundPainter; pub use stacking_context::*; #[derive(Clone, Copy)] @@ -205,7 +206,21 @@ impl Fragment { Visibility::Collapse => (), }, Fragment::AbsoluteOrFixedPositioned(_) => {}, - Fragment::Anonymous(_) => {}, + Fragment::Positioning(positioning_fragment) => { + if let Some(style) = positioning_fragment.style.as_ref() { + let rect = positioning_fragment + .rect + .to_physical(style.writing_mode, containing_block) + .translate(containing_block.origin.to_vector()); + self.maybe_push_hit_test_for_style_and_tag( + builder, + style, + positioning_fragment.base.tag, + rect, + Cursor::Default, + ); + } + }, Fragment::Image(i) => match i.style.get_inherited_box().visibility { Visibility::Visible => { builder.is_contentful = true; @@ -265,6 +280,33 @@ impl Fragment { } } + fn maybe_push_hit_test_for_style_and_tag( + &self, + builder: &mut DisplayListBuilder, + style: &ComputedValues, + tag: Option, + rect: PhysicalRect, + cursor: Cursor, + ) { + let hit_info = builder.hit_info(style, tag, cursor); + let hit_info = match hit_info { + Some(hit_info) => hit_info, + None => return, + }; + + let clip_chain_id = builder.current_clip_chain_id; + let spatial_id = builder.current_scroll_node_id.spatial_id; + builder.wr().push_hit_test( + &CommonItemProperties { + clip_rect: rect.to_webrender(), + clip_id: ClipId::ClipChain(clip_chain_id), + spatial_id, + flags: style.get_webrender_primitive_flags(), + }, + hit_info, + ); + } + fn build_display_list_for_text_fragment( &self, fragment: &TextFragment, @@ -291,21 +333,13 @@ impl Fragment { return; } - if let Some(hit_info) = - builder.hit_info(&fragment.parent_style, fragment.base.tag, Cursor::Text) - { - let clip_chain_id = builder.current_clip_chain_id; - let spatial_id = builder.current_scroll_node_id.spatial_id; - builder.wr().push_hit_test( - &CommonItemProperties { - clip_rect: rect.to_webrender(), - clip_id: ClipId::ClipChain(clip_chain_id), - spatial_id, - flags: fragment.parent_style.get_webrender_primitive_flags(), - }, - hit_info, - ); - } + self.maybe_push_hit_test_for_style_and_tag( + builder, + &fragment.parent_style, + fragment.base.tag, + rect, + Cursor::Text, + ); let color = fragment.parent_style.clone_color(); let font_metrics = &fragment.font_metrics; @@ -540,6 +574,27 @@ impl<'a> BuilderForBoxFragment<'a> { builder.wr().push_hit_test(&common, hit_info); } + fn build_background_for_painter( + &mut self, + builder: &mut DisplayListBuilder, + painter: &BackgroundPainter, + ) { + let b = painter.style.get_background(); + let background_color = painter.style.resolve_color(b.background_color.clone()); + if background_color.alpha > 0.0 { + // https://drafts.csswg.org/css-backgrounds/#background-color + // “The background color is clipped according to the background-clip + // value associated with the bottom-most background image layer.” + let layer_index = b.background_image.0.len() - 1; + let (bounds, common) = painter.painting_area(self, builder, layer_index); + builder + .wr() + .push_rect(&common, *bounds, rgba(background_color)) + } + + self.build_background_image(builder, painter); + } + fn build_background(&mut self, builder: &mut DisplayListBuilder) { if self .fragment @@ -550,34 +605,36 @@ impl<'a> BuilderForBoxFragment<'a> { return; } - let source = background::Source::Fragment; - let style = &self.fragment.style; - let b = style.get_background(); - let background_color = style.resolve_color(b.background_color.clone()); - if background_color.alpha > 0.0 { - // https://drafts.csswg.org/css-backgrounds/#background-color - // “The background color is clipped according to the background-clip - // value associated with the bottom-most background image layer.” - let layer_index = b.background_image.0.len() - 1; - let (bounds, common) = background::painting_area(self, &source, builder, layer_index); - builder - .wr() - .push_rect(&common, *bounds, rgba(background_color)) + for extra_background in self.fragment.extra_backgrounds.iter() { + let positioning_area: LogicalRect = extra_background.rect.clone().into(); + let painter = BackgroundPainter { + style: &extra_background.style, + painting_area_override: None, + positioning_area_override: Some( + positioning_area + .to_physical(self.fragment.style.writing_mode, self.containing_block) + .translate(self.containing_block.origin.to_vector()) + .to_webrender(), + ), + }; + self.build_background_for_painter(builder, &painter); } - self.build_background_image(builder, source); + let painter = BackgroundPainter { + style: &self.fragment.style, + painting_area_override: None, + positioning_area_override: None, + }; + self.build_background_for_painter(builder, &painter); } fn build_background_image( &mut self, builder: &mut DisplayListBuilder, - source: background::Source<'a>, + painter: &BackgroundPainter, ) { use style::values::computed::image::Image; - let style = match source { - background::Source::Canvas { style, .. } => style, - background::Source::Fragment => &self.fragment.style, - }; + let style = painter.style; let b = style.get_background(); // Reverse because the property is top layer first, we want to paint bottom layer first. for (index, image) in b.background_image.0.iter().enumerate().rev() { @@ -586,7 +643,7 @@ impl<'a> BuilderForBoxFragment<'a> { Image::Gradient(ref gradient) => { let intrinsic = IntrinsicSizes::empty(); if let Some(layer) = - &background::layout_layer(self, &source, builder, index, intrinsic) + &background::layout_layer(self, painter, builder, index, intrinsic) { gradient::build(style, gradient, layer, builder) } @@ -627,7 +684,7 @@ impl<'a> BuilderForBoxFragment<'a> { ); if let Some(layer) = - background::layout_layer(self, &source, builder, index, intrinsic) + background::layout_layer(self, &painter, builder, index, intrinsic) { let image_rendering = image_rendering(style.clone_image_rendering()); if layer.repeat { diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index fee7c159bb89b..47819d0bd103a 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -32,7 +32,7 @@ use crate::cell::ArcRefCell; use crate::display_list::conversions::{FilterToWebRender, ToWebRender}; use crate::display_list::DisplayListBuilder; use crate::fragment_tree::{ - AnonymousFragment, BoxFragment, ContainingBlockManager, Fragment, FragmentTree, + BoxFragment, ContainingBlockManager, Fragment, FragmentTree, PositioningFragment, }; use crate::geom::{PhysicalRect, PhysicalSides}; use crate::style_ext::ComputedValuesExt; @@ -636,11 +636,12 @@ impl StackingContext { let mut fragment_builder = super::BuilderForBoxFragment::new(box_fragment, containing_block); - let source = super::background::Source::Canvas { + let painter = super::background::BackgroundPainter { style, - painting_area, + painting_area_override: Some(painting_area), + positioning_area_override: None, }; - fragment_builder.build_background_image(builder, source); + fragment_builder.build_background_image(builder, &painter); } pub(crate) fn build_display_list(&self, builder: &mut DisplayListBuilder) { @@ -858,7 +859,7 @@ impl Fragment { StackingContextBuildMode::IncludeHoisted, ); }, - Fragment::Anonymous(fragment) => { + Fragment::Positioning(fragment) => { fragment.build_stacking_context_tree( display_list, containing_block, @@ -1501,7 +1502,7 @@ impl BoxFragment { } } -impl AnonymousFragment { +impl PositioningFragment { fn build_stacking_context_tree( &self, display_list: &mut DisplayList, @@ -1511,7 +1512,7 @@ impl AnonymousFragment { ) { let rect = self .rect - .to_physical(self.mode, &containing_block.rect) + .to_physical(self.writing_mode, &containing_block.rect) .translate(containing_block.rect.origin.to_vector()); let new_containing_block = containing_block.new_replacing_rect(&rect); let new_containing_block_info = diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index b90ee2857edd2..9d688d72a829c 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -35,8 +35,8 @@ use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::flow::FlowLayout; use crate::formatting_contexts::{Baselines, IndependentFormattingContext}; use crate::fragment_tree::{ - AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, - Fragment, + BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, + PositioningFragment, }; use crate::geom::{LogicalRect, LogicalVec2}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; @@ -815,7 +815,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> { ); self.fragments - .push(Fragment::Anonymous(AnonymousFragment::new( + .push(Fragment::Positioning(PositioningFragment::new_anonymous( line_rect, fragments, self.containing_block.style.writing_mode, diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index e04670c42c1bd..363dd5d80b86d 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -1519,7 +1519,7 @@ impl PlacementState { block_offset_from_containing_block_top.into(), ); }, - Fragment::Anonymous(_) => {}, + Fragment::Positioning(_) => {}, _ => unreachable!(), } } diff --git a/components/layout_2020/fragment_tree/box_fragment.rs b/components/layout_2020/fragment_tree/box_fragment.rs index edf4e13ba2218..031285360a7e9 100644 --- a/components/layout_2020/fragment_tree/box_fragment.rs +++ b/components/layout_2020/fragment_tree/box_fragment.rs @@ -2,6 +2,7 @@ * 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 gfx_traits::print_tree::PrintTree; use serde::Serialize; use servo_arc::Arc as ServoArc; @@ -20,6 +21,11 @@ use crate::geom::{ }; use crate::style_ext::ComputedValuesExt; +pub(crate) struct ExtraBackground { + pub style: ServoArc, + pub rect: LogicalRect, +} + #[derive(Serialize)] pub(crate) struct BoxFragment { pub base: BaseFragment, @@ -62,6 +68,9 @@ pub(crate) struct BoxFragment { /// during stacking context tree construction because they rely on the size of the /// scroll container. pub(crate) resolved_sticky_insets: Option>, + + #[serde(skip_serializing)] + pub extra_backgrounds: Vec, } impl BoxFragment { @@ -149,6 +158,7 @@ impl BoxFragment { scrollable_overflow_from_children, overconstrained, resolved_sticky_insets: None, + extra_backgrounds: Vec::new(), } } @@ -165,6 +175,10 @@ impl BoxFragment { self } + pub fn add_extra_background(&mut self, extra_background: ExtraBackground) { + self.extra_backgrounds.push(extra_background); + } + pub fn scrollable_overflow( &self, containing_block: &PhysicalRect, diff --git a/components/layout_2020/fragment_tree/fragment.rs b/components/layout_2020/fragment_tree/fragment.rs index dc1314d24d081..ccbfe229cc4bd 100644 --- a/components/layout_2020/fragment_tree/fragment.rs +++ b/components/layout_2020/fragment_tree/fragment.rs @@ -10,14 +10,16 @@ use gfx_traits::print_tree::PrintTree; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use serde::Serialize; use servo_arc::Arc as ServoArc; -use style::logical_geometry::WritingMode; use style::properties::ComputedValues; use style::values::computed::Length; use style::values::specified::text::TextDecorationLine; use style::Zero; use webrender_api::{FontInstanceKey, ImageKey}; -use super::{BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, Tag}; +use super::{ + BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, PositioningFragment, + Tag, +}; use crate::cell::ArcRefCell; use crate::geom::{LogicalRect, LogicalSides, PhysicalRect}; use crate::style_ext::ComputedValuesExt; @@ -31,7 +33,7 @@ pub(crate) enum Fragment { /// the [SequentialLayoutState] of their float containing block formatting /// context. Float(BoxFragment), - Anonymous(AnonymousFragment), + Positioning(PositioningFragment), /// Absolute and fixed position fragments are hoisted up so that they /// are children of the BoxFragment that establishes their containing /// blocks, so that they can be laid out properly. When this happens @@ -63,18 +65,6 @@ pub(crate) struct CollapsedMargin { min_negative: Length, } -/// Can contain child fragments with relative coordinates, but does not contribute to painting itself. -#[derive(Serialize)] -pub(crate) struct AnonymousFragment { - pub base: BaseFragment, - pub rect: LogicalRect, - pub children: Vec>, - pub mode: WritingMode, - - /// The scrollable overflow of this anonymous fragment's children. - pub scrollable_overflow: PhysicalRect, -} - #[derive(Serialize)] pub(crate) struct TextFragment { pub base: BaseFragment, @@ -119,7 +109,7 @@ impl Fragment { Fragment::Box(fragment) => &fragment.base, Fragment::Text(fragment) => &fragment.base, Fragment::AbsoluteOrFixedPositioned(_) => return None, - Fragment::Anonymous(fragment) => &fragment.base, + Fragment::Positioning(fragment) => &fragment.base, Fragment::Image(fragment) => &fragment.base, Fragment::IFrame(fragment) => &fragment.base, Fragment::Float(fragment) => &fragment.base, @@ -141,7 +131,7 @@ impl Fragment { Fragment::AbsoluteOrFixedPositioned(_) => { tree.add_item("AbsoluteOrFixedPositioned".to_string()); }, - Fragment::Anonymous(fragment) => fragment.print(tree), + Fragment::Positioning(fragment) => fragment.print(tree), Fragment::Text(fragment) => fragment.print(tree), Fragment::Image(fragment) => fragment.print(tree), Fragment::IFrame(fragment) => fragment.print(tree), @@ -166,7 +156,7 @@ impl Fragment { fragment.scrollable_overflow_for_parent(containing_block) }, Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(), - Fragment::Anonymous(fragment) => fragment.scrollable_overflow, + Fragment::Positioning(fragment) => fragment.scrollable_overflow, Fragment::Text(fragment) => fragment .rect .to_physical(fragment.parent_style.writing_mode, containing_block), @@ -219,10 +209,10 @@ impl Fragment { .iter() .find_map(|child| child.borrow().find(&new_manager, level + 1, process_func)) }, - Fragment::Anonymous(fragment) => { + Fragment::Positioning(fragment) => { let content_rect = fragment .rect - .to_physical(fragment.mode, containing_block) + .to_physical(fragment.writing_mode, containing_block) .translate(containing_block.origin.to_vector()); let new_manager = manager.new_for_non_absolute_descendants(&content_rect); fragment @@ -235,43 +225,6 @@ impl Fragment { } } -impl AnonymousFragment { - pub fn new(rect: LogicalRect, children: Vec, mode: WritingMode) -> Self { - // FIXME(mrobinson, bug 25564): We should be using the containing block - // here to properly convert scrollable overflow to physical geometry. - let containing_block = PhysicalRect::zero(); - let content_origin = rect.start_corner.to_physical(mode); - let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| { - acc.union( - &child - .scrollable_overflow(&containing_block) - .translate(content_origin.to_vector()), - ) - }); - AnonymousFragment { - base: BaseFragment::anonymous(), - rect, - children: children.into_iter().map(ArcRefCell::new).collect(), - mode, - scrollable_overflow, - } - } - - pub fn print(&self, tree: &mut PrintTree) { - tree.new_level(format!( - "Anonymous\ - \nrect={:?}\ - \nscrollable_overflow={:?}", - self.rect, self.scrollable_overflow - )); - - for child in &self.children { - child.borrow().print(tree); - } - tree.end_level(); - } -} - impl TextFragment { pub fn print(&self, tree: &mut PrintTree) { tree.add_item(format!( diff --git a/components/layout_2020/fragment_tree/fragment_tree.rs b/components/layout_2020/fragment_tree/fragment_tree.rs index 5e50a54768c65..830e0a5d2335f 100644 --- a/components/layout_2020/fragment_tree/fragment_tree.rs +++ b/components/layout_2020/fragment_tree/fragment_tree.rs @@ -106,13 +106,15 @@ impl FragmentTree { Fragment::Box(fragment) | Fragment::Float(fragment) => fragment .border_rect() .to_physical(fragment.style.writing_mode, containing_block), + Fragment::Positioning(fragment) => fragment + .rect + .to_physical(fragment.writing_mode, containing_block), Fragment::Text(fragment) => fragment .rect .to_physical(fragment.parent_style.writing_mode, containing_block), Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Image(_) | - Fragment::IFrame(_) | - Fragment::Anonymous(_) => return None, + Fragment::IFrame(_) => return None, }; found_any_nodes = true; @@ -145,37 +147,45 @@ impl FragmentTree { return None; } - let (style, padding_rect) = match fragment { - Fragment::Box(fragment) => (&fragment.style, fragment.padding_rect()), + let rect = match fragment { + Fragment::Box(fragment) => { + // https://drafts.csswg.org/cssom-view/#dom-element-clienttop + // " If the element has no associated CSS layout box or if the + // CSS layout box is inline, return zero." For this check we + // also explicitly ignore the list item portion of the display + // style. + let display = &fragment.style.get_box().display; + if display.inside() == style::values::specified::box_::DisplayInside::Flow && + display.outside() == + style::values::specified::box_::DisplayOutside::Inline + { + return Some(Rect::zero()); + } + + let border = fragment.style.get_border(); + let padding_rect = fragment + .padding_rect() + .to_physical(fragment.style.writing_mode, containing_block); + Rect::new( + Point2D::new( + border.border_left_width.into(), + border.border_top_width.into(), + ), + Size2D::new(padding_rect.size.width, padding_rect.size.height), + ) + }, + Fragment::Positioning(fragment) => fragment + .rect + .to_physical(fragment.writing_mode, containing_block) + .cast_unit(), _ => return None, }; - // https://drafts.csswg.org/cssom-view/#dom-element-clienttop - // " If the element has no associated CSS layout box or if the - // CSS layout box is inline, return zero." For this check we - // also explicitly ignore the list item portion of the display - // style. - let display = &style.get_box().display; - if display.inside() == style::values::specified::box_::DisplayInside::Flow && - display.outside() == style::values::specified::box_::DisplayOutside::Inline - { - return Some(Rect::zero()); - } - - let border = style.get_border(); - let padding_rect = padding_rect.to_physical(style.writing_mode, containing_block); - Some( - Rect::new( - Point2D::new( - border.border_left_width.to_f32_px(), - border.border_top_width.to_f32_px(), - ), - Size2D::new(padding_rect.size.width.px(), padding_rect.size.height.px()), - ) - .round() - .to_i32() - .to_untyped(), - ) + let rect = Rect::new( + Point2D::new(rect.origin.x.px(), rect.origin.y.px()), + Size2D::new(rect.size.width.px(), rect.size.height.px()), + ); + Some(rect.round().to_i32().to_untyped()) }) .unwrap_or_else(Rect::zero) } diff --git a/components/layout_2020/fragment_tree/mod.rs b/components/layout_2020/fragment_tree/mod.rs index 03e112d7adffc..4579638685a4f 100644 --- a/components/layout_2020/fragment_tree/mod.rs +++ b/components/layout_2020/fragment_tree/mod.rs @@ -8,6 +8,7 @@ mod containing_block; mod fragment; mod fragment_tree; mod hoisted_shared_fragment; +mod positioning_fragment; pub(crate) use base_fragment::*; pub(crate) use box_fragment::*; @@ -15,3 +16,4 @@ pub(crate) use containing_block::*; pub(crate) use fragment::*; pub use fragment_tree::*; pub(crate) use hoisted_shared_fragment::*; +pub(crate) use positioning_fragment::*; diff --git a/components/layout_2020/fragment_tree/positioning_fragment.rs b/components/layout_2020/fragment_tree/positioning_fragment.rs new file mode 100644 index 0000000000000..4a629b3169811 --- /dev/null +++ b/components/layout_2020/fragment_tree/positioning_fragment.rs @@ -0,0 +1,100 @@ +/* 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 gfx_traits::print_tree::PrintTree; +use serde::Serialize; +use servo_arc::Arc as ServoArc; +use style::logical_geometry::WritingMode; +use style::properties::ComputedValues; +use style::values::computed::Length; + +use super::{BaseFragment, BaseFragmentInfo, Fragment}; +use crate::cell::ArcRefCell; +use crate::geom::{LogicalRect, PhysicalRect}; + +/// Can contain child fragments with relative coordinates, but does not contribute to painting +/// itself. [`PositioningFragments`] may be completely anonymous, or just non-painting Fragments +/// generated by boxes. +#[derive(Serialize)] +pub(crate) struct PositioningFragment { + pub base: BaseFragment, + pub rect: LogicalRect, + pub children: Vec>, + pub writing_mode: WritingMode, + + /// The scrollable overflow of this anonymous fragment's children. + pub scrollable_overflow: PhysicalRect, + + /// If this fragment was created with a style, the style of the fragment. + #[serde(skip_serializing)] + pub style: Option>, +} + +impl PositioningFragment { + pub fn new_anonymous( + rect: LogicalRect, + children: Vec, + mode: WritingMode, + ) -> Self { + Self::new_with_base_fragment(BaseFragment::anonymous(), None, rect, children, mode) + } + + pub fn new_empty( + base_fragment_info: BaseFragmentInfo, + rect: LogicalRect, + style: ServoArc, + ) -> Self { + let writing_mode = style.writing_mode; + Self::new_with_base_fragment( + base_fragment_info.into(), + Some(style), + rect, + Vec::new(), + writing_mode, + ) + } + + fn new_with_base_fragment( + base: BaseFragment, + style: Option>, + rect: LogicalRect, + children: Vec, + mode: WritingMode, + ) -> Self { + // FIXME(mrobinson, bug 25564): We should be using the containing block + // here to properly convert scrollable overflow to physical geometry. + let containing_block = PhysicalRect::zero(); + let content_origin = rect.start_corner.to_physical(mode); + let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| { + acc.union( + &child + .scrollable_overflow(&containing_block) + .translate(content_origin.to_vector()), + ) + }); + PositioningFragment { + base, + style, + rect, + children: children.into_iter().map(ArcRefCell::new).collect(), + writing_mode: mode, + scrollable_overflow, + } + } + + pub fn print(&self, tree: &mut PrintTree) { + tree.new_level(format!( + "PositioningFragment\ + \nbase={:?}\ + \nrect={:?}\ + \nscrollable_overflow={:?}", + self.base, self.rect, self.scrollable_overflow + )); + + for child in &self.children { + child.borrow().print(tree); + } + tree.end_level(); + } +} diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index e742ab0d184fa..75c35cc83a823 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -173,7 +173,7 @@ impl PositioningContext { let start_offset = match &parent_fragment { Fragment::Box(b) | Fragment::Float(b) => &b.content_rect.start_corner, Fragment::AbsoluteOrFixedPositioned(_) => return, - Fragment::Anonymous(a) => &a.rect.start_corner, + Fragment::Positioning(a) => &a.rect.start_corner, _ => unreachable!(), }; self.adjust_static_position_of_hoisted_fragments_with_offset(start_offset, index); diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 05ea42d5ab75d..8ec2f0b4570a6 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex}; use app_units::Au; use euclid::default::{Point2D, Rect}; -use euclid::{Size2D, Vector2D}; +use euclid::{SideOffsets2D, Size2D, Vector2D}; use log::warn; use msg::constellation_msg::PipelineId; use script_layout_interface::rpc::{ @@ -309,32 +309,46 @@ pub fn process_resolved_style_request<'dom>( return None; } - let box_fragment = match fragment { - Fragment::Box(ref box_fragment) => box_fragment, + let (content_rect, margins, padding) = match fragment { + Fragment::Box(ref box_fragment) | Fragment::Float(ref box_fragment) => { + if style.get_box().position != Position::Static { + let resolved_insets = || { + box_fragment.calculate_resolved_insets_if_positioned(containing_block) + }; + match longhand_id { + LonghandId::Top => return Some(resolved_insets().top.to_css_string()), + LonghandId::Right => { + return Some(resolved_insets().right.to_css_string()) + }, + LonghandId::Bottom => { + return Some(resolved_insets().bottom.to_css_string()) + }, + LonghandId::Left => { + return Some(resolved_insets().left.to_css_string()) + }, + _ => {}, + } + } + let content_rect = box_fragment + .content_rect + .to_physical(box_fragment.style.writing_mode, containing_block); + let margins = box_fragment + .margin + .to_physical(box_fragment.style.writing_mode); + let padding = box_fragment + .padding + .to_physical(box_fragment.style.writing_mode); + (content_rect, margins, padding) + }, + Fragment::Positioning(positioning_fragment) => { + let content_rect = positioning_fragment + .rect + .to_physical(positioning_fragment.writing_mode, containing_block); + (content_rect, SideOffsets2D::zero(), SideOffsets2D::zero()) + }, _ => return None, }; - if style.get_box().position != Position::Static { - let resolved_insets = - || box_fragment.calculate_resolved_insets_if_positioned(containing_block); - match longhand_id { - LonghandId::Top => return Some(resolved_insets().top.to_css_string()), - LonghandId::Right => return Some(resolved_insets().right.to_css_string()), - LonghandId::Bottom => return Some(resolved_insets().bottom.to_css_string()), - LonghandId::Left => return Some(resolved_insets().left.to_css_string()), - _ => {}, - } - } - - let content_rect = box_fragment - .content_rect - .to_physical(box_fragment.style.writing_mode, containing_block); - let margins = box_fragment - .margin - .to_physical(box_fragment.style.writing_mode); - let padding = box_fragment - .padding - .to_physical(box_fragment.style.writing_mode); match longhand_id { LonghandId::Width => Some(content_rect.size.width), LonghandId::Height => Some(content_rect.size.height), @@ -464,10 +478,12 @@ fn process_offset_parent_query_inner( Fragment::Text(fragment) => fragment .rect .to_physical(fragment.parent_style.writing_mode, containing_block), + Fragment::Positioning(fragment) => fragment + .rect + .to_physical(fragment.writing_mode, containing_block), Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Image(_) | - Fragment::IFrame(_) | - Fragment::Anonymous(_) => unreachable!(), + Fragment::IFrame(_) => unreachable!(), }; let border_box = fragment_relative_rect.translate(containing_block.origin.to_vector()); @@ -541,10 +557,10 @@ fn process_offset_parent_query_inner( } }, Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Text(_) | - Fragment::Image(_) | Fragment::IFrame(_) | - Fragment::Anonymous(_) => None, + Fragment::Image(_) | + Fragment::Positioning(_) | + Fragment::Text(_) => None, }; while parent_node_addresses.len() <= level { @@ -596,7 +612,7 @@ fn process_offset_parent_query_inner( Fragment::Text(_) | Fragment::Image(_) | Fragment::IFrame(_) | - Fragment::Anonymous(_) => None, + Fragment::Positioning(_) => None, } }) .unwrap() diff --git a/components/layout_2020/table/construct.rs b/components/layout_2020/table/construct.rs index 708113fd21d3e..03646b708d283 100644 --- a/components/layout_2020/table/construct.rs +++ b/components/layout_2020/table/construct.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::convert::{TryFrom, TryInto}; +use std::iter::repeat; use log::warn; use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode; @@ -13,7 +14,10 @@ use style::selector_parser::PseudoElement; use style::str::char_is_whitespace; use style::values::specified::TextDecorationLine; -use super::{Table, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset}; +use super::{ + Table, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset, TableTrack, + TableTrackGroup, TableTrackGroupType, +}; use crate::context::LayoutContext; use crate::dom::{BoxSlot, NodeExt}; use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler}; @@ -224,30 +228,217 @@ impl TableBuilder { Self::new(ComputedValues::initial_values().to_arc()) } + pub fn last_row_index_in_row_group_at_row_n(&self, n: usize) -> usize { + for row_group in self.table.row_groups.iter() { + if row_group.track_range.start > n { + return row_group.track_range.start - 1; + } + } + self.table.size.height - 1 + } + pub fn finish(mut self) -> Table { - // Make sure that every row has the same number of cells. + self.do_missing_cells_fixup(); + self.remove_extra_columns_and_column_groups(); + self.reorder_first_thead_and_tfoot(); + self.do_final_rowspan_calculation(); + self.table + } + + /// Do which ensures + /// that every row has the same number of cells. + fn do_missing_cells_fixup(&mut self) { for row in self.table.slots.iter_mut() { row.resize_with(self.table.size.width, || TableSlot::Empty); } + } + + /// It's possible to define more table columns via `` and `` elements + /// than actually exist in the table. In that case, remove these bogus columns + /// to prevent using them later in layout. + fn remove_extra_columns_and_column_groups(&mut self) { + let number_of_actual_table_columns = self.table.size.width; + self.table.columns.truncate(number_of_actual_table_columns); + + let mut remove_from = None; + for (group_index, column_group) in self.table.column_groups.iter_mut().enumerate() { + if column_group.track_range.start >= number_of_actual_table_columns { + remove_from = Some(group_index); + break; + } + column_group.track_range.end = column_group + .track_range + .end + .min(number_of_actual_table_columns); + } + + if let Some(remove_from) = remove_from { + self.table.column_groups.truncate(remove_from - 1); + } + } + + /// Reorder the first `` and `` to be the first and last row groups respectively. + /// This requires fixing up all row group indices. + /// See and + /// . + fn reorder_first_thead_and_tfoot(&mut self) { + let mut thead_index = None; + let mut tfoot_index = None; + for (row_group_index, row_group) in self.table.row_groups.iter().enumerate() { + if thead_index.is_none() && row_group.group_type == TableTrackGroupType::HeaderGroup { + thead_index = Some(row_group_index); + } + if tfoot_index.is_none() && row_group.group_type == TableTrackGroupType::FooterGroup { + tfoot_index = Some(row_group_index); + } + if thead_index.is_some() && tfoot_index.is_some() { + break; + } + } + + if let Some(thead_index) = thead_index { + self.move_row_group_to_front(thead_index) + } + + if let Some(mut tfoot_index) = tfoot_index { + // We may have moved a `` which means the original index we + // we found for this this ` also needs to be updated! + if thead_index.unwrap_or(0) > tfoot_index { + tfoot_index += 1; + } + self.move_row_group_to_end(tfoot_index) + } + } + + fn regenerate_track_ranges(&mut self) { + // Now update all track group ranges. + let mut current_row_group = None; + for (row_index, row) in self.table.rows.iter().enumerate() { + if current_row_group == row.group_index { + continue; + } + + if let Some(new_group_index) = row.group_index { + if let Some(new_group) = self.table.row_groups.get_mut(new_group_index) { + new_group.track_range = row_index..self.table.rows.len(); + } + } + + if let Some(current_group_index) = current_row_group { + if let Some(current_group) = self.table.row_groups.get_mut(current_group_index) { + current_group.track_range.end = row_index; + } + } + + current_row_group = row.group_index; + } + } + + fn move_row_group_to_front(&mut self, index_to_move: usize) { + if index_to_move == 0 { + return; + } + + // Move the slots associated with this group. + let row_range = self.table.row_groups[index_to_move].track_range.clone(); + let removed_slots: Vec> = self + .table + .slots + .splice(row_range.clone(), std::iter::empty()) + .collect(); + self.table.slots.splice(0..0, removed_slots); + + // Move the rows associated with this group. + let removed_rows: Vec = self + .table + .rows + .splice(row_range, std::iter::empty()) + .collect(); + self.table.rows.splice(0..0, removed_rows); + + // Move the group itself. + let removed_row_group: Vec = self + .table + .row_groups + .splice(index_to_move..index_to_move + 1, std::iter::empty()) + .collect(); + self.table.row_groups.splice(0..0, removed_row_group); + + for row in self.table.rows.iter_mut() { + match row.group_index.as_mut() { + Some(group_index) if *group_index < index_to_move => *group_index += 1, + Some(group_index) if *group_index == index_to_move => *group_index = 0, + _ => {}, + } + } + + self.regenerate_track_ranges(); + } + + fn move_row_group_to_end(&mut self, index_to_move: usize) { + let last_row_group_index = self.table.row_groups.len() - 1; + if index_to_move == last_row_group_index { + return; + } + + // Move the slots associated with this group. + let row_range = self.table.row_groups[index_to_move].track_range.clone(); + let removed_slots: Vec> = self + .table + .slots + .splice(row_range.clone(), std::iter::empty()) + .collect(); + self.table.slots.extend(removed_slots); + + // Move the rows associated with this group. + let removed_rows: Vec = self + .table + .rows + .splice(row_range, std::iter::empty()) + .collect(); + self.table.rows.extend(removed_rows); + + // Move the group itself. + let removed_row_group: Vec = self + .table + .row_groups + .splice(index_to_move..index_to_move + 1, std::iter::empty()) + .collect(); + self.table.row_groups.extend(removed_row_group); + + for row in self.table.rows.iter_mut() { + match row.group_index.as_mut() { + Some(group_index) if *group_index > index_to_move => *group_index -= 1, + Some(group_index) if *group_index == index_to_move => { + *group_index = last_row_group_index + }, + _ => {}, + } + } + + self.regenerate_track_ranges(); + } - // Turn all rowspan=0 rows into the real value to avoid having to - // make the calculation continually during layout. In addition, make - // sure that there are no rowspans that extend past the end of the - // table. + /// Turn all rowspan=0 rows into the real value to avoid having to make the calculation + /// continually during layout. In addition, make sure that there are no rowspans that extend + /// past the end of their row group. + fn do_final_rowspan_calculation(&mut self) { for row_index in 0..self.table.size.height { + let last_row_index_in_group = self.last_row_index_in_row_group_at_row_n(row_index); for cell in self.table.slots[row_index].iter_mut() { if let TableSlot::Cell(ref mut cell) = cell { - let rowspan_to_end_of_table = self.table.size.height - row_index; + if cell.rowspan == 1 { + continue; + } + let rowspan_to_end_of_group = last_row_index_in_group - row_index + 1; if cell.rowspan == 0 { - cell.rowspan = rowspan_to_end_of_table; + cell.rowspan = rowspan_to_end_of_group; } else { - cell.rowspan = cell.rowspan.min(rowspan_to_end_of_table); + cell.rowspan = cell.rowspan.min(rowspan_to_end_of_group); } } } } - - self.table } fn current_y(&self) -> usize { @@ -408,6 +599,9 @@ pub(crate) struct TableBuilderTraversal<'style, 'dom, Node> { builder: TableBuilder, current_anonymous_row_content: Vec>, + + /// The index of the current row group, if there is one. + current_row_group_index: Option, } impl<'style, 'dom, Node> TableBuilderTraversal<'style, 'dom, Node> @@ -425,6 +619,7 @@ where propagated_text_decoration_line, builder: TableBuilder::new(info.style.clone()), current_anonymous_row_content: Vec::new(), + current_row_group_index: None, } } @@ -499,17 +694,26 @@ where DisplayLayoutInternal::TableFooterGroup | DisplayLayoutInternal::TableHeaderGroup => { self.finish_anonymous_row_if_needed(); - - // TODO: Should we fixup `rowspan=0` to the actual resolved value and - // any other rowspans that have been cut short? self.builder.incoming_rowspans.clear(); + + let next_row_index = self.builder.table.rows.len(); + self.builder.table.row_groups.push(TableTrackGroup { + base_fragment_info: info.into(), + style: info.style.clone(), + group_type: internal.into(), + track_range: next_row_index..next_row_index, + }); + + let new_row_group_index = self.builder.table.row_groups.len() - 1; + self.current_row_group_index = Some(new_row_group_index); NonReplacedContents::try_from(contents).unwrap().traverse( self.context, info, self, ); - // TODO: Handle style for row groups here. + self.current_row_group_index = None; + self.builder.incoming_rowspans.clear(); // We are doing this until we have actually set a Box for this `BoxSlot`. ::std::mem::forget(box_slot) @@ -527,17 +731,87 @@ where ); row_builder.finish(); + self.builder.table.rows.push(TableTrack { + base_fragment_info: info.into(), + style: info.style.clone(), + group_index: self.current_row_group_index, + is_anonymous: false, + }); + + let last_row = self.builder.table.rows.len(); + let row_group = self + .current_row_group_index + .map(|index| &mut self.builder.table.row_groups[index]); + if let Some(row_group) = row_group { + row_group.track_range.end = last_row; + } + // We are doing this until we have actually set a Box for this `BoxSlot`. ::std::mem::forget(box_slot) }, - DisplayLayoutInternal::TableCaption | - DisplayLayoutInternal::TableColumn | - DisplayLayoutInternal::TableColumnGroup => { - // TODO: Handle these other types of table elements. + DisplayLayoutInternal::TableColumn => { + let node = info.node.to_threadsafe(); + let span = (node.get_span().unwrap_or(1) as usize).min(1000); + for _ in 0..span + 1 { + self.builder.table.columns.push(TableTrack { + base_fragment_info: info.into(), + style: info.style.clone(), + group_index: None, + is_anonymous: false, + }) + } // We are doing this until we have actually set a Box for this `BoxSlot`. ::std::mem::forget(box_slot) }, + DisplayLayoutInternal::TableColumnGroup => { + let column_group_index = self.builder.table.column_groups.len(); + let mut column_group_builder = TableColumnGroupBuilder { + column_group_index, + columns: Vec::new(), + }; + + NonReplacedContents::try_from(contents).unwrap().traverse( + self.context, + info, + &mut column_group_builder, + ); + + let first_column = self.builder.table.columns.len(); + if column_group_builder.columns.is_empty() { + let node = info.node.to_threadsafe(); + let span = (node.get_span().unwrap_or(1) as usize).min(1000); + + self.builder.table.columns.extend( + repeat(TableTrack { + base_fragment_info: info.into(), + style: info.style.clone(), + group_index: Some(column_group_index), + is_anonymous: true, + }) + .take(span), + ); + } else { + self.builder + .table + .columns + .extend(column_group_builder.columns); + } + + self.builder.table.column_groups.push(TableTrackGroup { + base_fragment_info: info.into(), + style: info.style.clone(), + group_type: internal.into(), + track_range: first_column..self.builder.table.columns.len(), + }); + + ::std::mem::forget(box_slot); + }, + DisplayLayoutInternal::TableCaption => { + // TODO: Handle table captions. + // We are doing this until we have actually set a Box for this `BoxSlot`. + ::std::mem::forget(box_slot); + }, DisplayLayoutInternal::TableCell => { self.current_anonymous_row_content .push(AnonymousTableContent::Element { @@ -682,8 +956,8 @@ where // when dealing with arbitrary DOM elements (perhaps created via // script). let node = info.node.to_threadsafe(); - let rowspan = std::cmp::min(node.get_rowspan() as usize, 65534); - let colspan = std::cmp::min(node.get_colspan() as usize, 1000); + let rowspan = (node.get_rowspan().unwrap_or(1) as usize).min(65534); + let colspan = (node.get_colspan().unwrap_or(1) as usize).min(1000); let contents = match contents.try_into() { Ok(non_replaced_contents) => { @@ -735,3 +1009,50 @@ where } } } + +struct TableColumnGroupBuilder { + column_group_index: usize, + columns: Vec, +} + +impl<'dom, Node: 'dom> TraversalHandler<'dom, Node> for TableColumnGroupBuilder +where + Node: NodeExt<'dom>, +{ + fn handle_text(&mut self, _info: &NodeAndStyleInfo, _text: Cow<'dom, str>) {} + fn handle_element( + &mut self, + info: &NodeAndStyleInfo, + display: DisplayGeneratingBox, + _contents: Contents, + box_slot: BoxSlot<'dom>, + ) { + // We are doing this until we have actually set a Box for this `BoxSlot`. + ::std::mem::forget(box_slot); + + if !matches!( + display, + DisplayGeneratingBox::LayoutInternal(DisplayLayoutInternal::TableColumn) + ) { + return; + } + self.columns.push(TableTrack { + base_fragment_info: info.into(), + style: info.style.clone(), + group_index: Some(self.column_group_index), + is_anonymous: false, + }); + } +} + +impl From for TableTrackGroupType { + fn from(value: DisplayLayoutInternal) -> Self { + match value { + DisplayLayoutInternal::TableColumnGroup => TableTrackGroupType::ColumnGroup, + DisplayLayoutInternal::TableFooterGroup => TableTrackGroupType::FooterGroup, + DisplayLayoutInternal::TableHeaderGroup => TableTrackGroupType::HeaderGroup, + DisplayLayoutInternal::TableRowGroup => TableTrackGroupType::RowGroup, + _ => unreachable!(), + } + } +} diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index 9cf4cd02c314d..9ebdc162cb83e 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -2,22 +2,26 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::ops::Add; - use app_units::{Au, MAX_AU}; -use euclid::num::Zero; use log::warn; +use servo_arc::Arc; use style::computed_values::border_collapse::T as BorderCollapse; use style::logical_geometry::WritingMode; -use style::values::computed::{CSSPixelLength, Length, LengthOrAuto, Percentage}; +use style::properties::ComputedValues; +use style::values::computed::{ + CSSPixelLength, Length, LengthOrAuto, LengthPercentage as ComputedLengthPercentage, Percentage, +}; use style::values::generics::box_::{GenericVerticalAlign as VerticalAlign, VerticalAlignKeyword}; use style::values::generics::length::GenericLengthPercentageOrAuto::{Auto, LengthPercentage}; +use style::Zero; -use super::{Table, TableSlot, TableSlotCell}; +use super::{Table, TableSlot, TableSlotCell, TableTrackGroup}; use crate::context::LayoutContext; use crate::formatting_contexts::{Baselines, IndependentLayout}; -use crate::fragment_tree::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment}; -use crate::geom::{LogicalRect, LogicalSides, LogicalVec2}; +use crate::fragment_tree::{ + BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, PositioningFragment, +}; +use crate::geom::{LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2}; use crate::positioned::{PositioningContext, PositioningContextLength}; use crate::sizing::ContentSizes; use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin}; @@ -108,7 +112,7 @@ impl<'a> TableLayout<'a> { let writing_mode = containing_block.style.writing_mode; self.compute_column_constrainedness_and_has_originating_cells(writing_mode); self.compute_cell_measures(layout_context, containing_block); - self.compute_column_measures(); + self.compute_column_measures(containing_block.style.writing_mode); self.compute_table_width(containing_block); self.distributed_column_widths = self.distribute_width_to_columns(); self.do_row_layout_first_pass(layout_context, containing_block, positioning_context); @@ -132,84 +136,47 @@ impl<'a> TableLayout<'a> { _ => continue, }; - // TODO: Should `box_size` percentages be treated as zero here or resolved against - // the containing block? - let pbm = cell.style.padding_border_margin(containing_block); - let min_inline_size: Au = cell - .style - .min_box_size(writing_mode) - .inline - .percentage_relative_to(Length::zero()) - .map(|value| value.into()) - .auto_is(Au::zero); - let max_inline_size: Au = cell.style.max_box_size(writing_mode).inline.map_or_else( - || MAX_AU, - |length_percentage| length_percentage.resolve(Length::zero()).into(), - ); - let inline_size: Au = cell - .style - .box_size(writing_mode) - .inline - .percentage_relative_to(Length::zero()) - .map(|value| value.into()) - .auto_is(Au::zero); - + let (size, min_size, max_size) = get_sizes_from_style(&cell.style, writing_mode); let content_sizes = cell .contents .contents .inline_content_sizes(layout_context, writing_mode); + let percentage_contribution = + get_size_percentage_contribution_from_style(&cell.style, writing_mode); // > The outer min-content width of a table-cell is max(min-width, min-content width) // > adjusted by the cell intrinsic offsets. - let mut outer_min_content_width = content_sizes.min_content.max(min_inline_size); + let mut outer_min_content_width = content_sizes.min_content.max(min_size.inline); let mut outer_max_content_width = if !self.column_constrainedness[column_index] { // > The outer max-content width of a table-cell in a non-constrained column is // > max(min-width, width, min-content width, min(max-width, max-content width)) // > adjusted by the cell intrinsic offsets. - min_inline_size - .max(inline_size) + min_size + .inline + .max(size.inline) .max(content_sizes.min_content) - .max(max_inline_size.min(content_sizes.max_content)) + .max(max_size.inline.min(content_sizes.max_content)) } else { // > The outer max-content width of a table-cell in a constrained column is // > max(min-width, width, min-content width, min(max-width, width)) adjusted by the // > cell intrinsic offsets. - min_inline_size - .max(inline_size) + min_size + .inline + .max(size.inline) .max(content_sizes.min_content) - .max(max_inline_size.min(inline_size)) + .max(max_size.inline.min(size.inline)) }; - // > The percentage contribution of a table cell, column, or column group is defined - // > in terms of the computed values of width and max-width that have computed values - // > that are percentages: - // > min(percentage width, percentage max-width). - // > If the computed values are not percentages, then 0% is used for width, and an - // > infinite percentage is used for max-width. - let inline_size_percent = cell - .style - .box_size(writing_mode) - .inline - .non_auto() - .and_then(|length_percentage| length_percentage.to_percentage()) - .unwrap_or(Percentage(0.)); - let max_inline_size_percent = cell - .style - .max_box_size(writing_mode) - .inline - .and_then(|length_percentage| length_percentage.to_percentage()) - .unwrap_or(Percentage(f32::INFINITY)); - let percentage_contribution = - Percentage(inline_size_percent.0.min(max_inline_size_percent.0)); - + let pbm = cell.style.padding_border_margin(containing_block); outer_min_content_width += pbm.padding_border_sums.inline; outer_max_content_width += pbm.padding_border_sums.inline; + row_measures[column_index] = CellOrColumnMeasure { content_sizes: ContentSizes { min_content: outer_min_content_width, max_content: outer_max_content_width, }, - percentage_width: percentage_contribution, + percentage_width: percentage_contribution.inline, }; } @@ -254,7 +221,7 @@ impl<'a> TableLayout<'a> { /// This is an implementation of *Computing Column Measures* from /// . - fn compute_column_measures(&mut self) { + fn compute_column_measures(&mut self, writing_mode: WritingMode) { let mut column_measures = Vec::new(); // Compute the column measures only taking into account cells with colspan == 1. @@ -289,7 +256,9 @@ impl<'a> TableLayout<'a> { // TODO: Take into account changes to this computation for fixed table layout. let mut next_span_n = usize::MAX; for column_index in 0..self.table.size.width { - let mut column_measure = CellOrColumnMeasure::zero(); + let mut column_measure = self + .table + .get_column_measure_for_column_at_index(writing_mode, column_index); for row_index in 0..self.table.size.height { let coords = TableSlotCoordinates::new(column_index, row_index); @@ -652,8 +621,6 @@ impl<'a> TableLayout<'a> { let mut max_content_sizing_guesses = Vec::new(); for column_idx in 0..self.table.size.width { - use style::Zero; - let column_measure = &self.column_measures[column_idx]; let min_content_width = column_measure.content_sizes.min_content; let max_content_width = column_measure.content_sizes.max_content; @@ -1040,14 +1007,20 @@ impl<'a> TableLayout<'a> { assert_eq!(self.table.size.width, self.distributed_column_widths.len()); let mut baselines = Baselines::default(); - let border_spacing = self.table.border_spacing(); let mut fragments = Vec::new(); - let mut row_offset = border_spacing.block; - for row_index in 0..self.table.size.height { - let mut column_offset = border_spacing.inline; - let row_size = self.row_sizes[row_index]; - let row_baseline = self.row_baselines[row_index]; + if self.table.size.width == 0 || self.table.size.height == 0 { + return IndependentLayout { + fragments, + content_block_size: Au::zero(), + baselines, + }; + } + + let dimensions = TableAndTrackDimensions::new(&self); + self.make_fragments_for_columns_rows_and_groups(&dimensions, &mut fragments); + + for row_index in 0..self.table.size.height { // From // > If any cells in the row participate in first baseline/last baseline alignment along // > the inline axis, the first/last baseline set of the row is generated from their @@ -1058,8 +1031,9 @@ impl<'a> TableLayout<'a> { // If any cell below has baseline alignment, these values will be overwritten, // but they are initialized to the content edge of the first row. if row_index == 0 { - baselines.first = Some(row_offset + row_size); - baselines.last = Some(row_offset + row_size); + let row_end = dimensions.get_row_rect(0).max_block_position(); + baselines.first = Some(row_end); + baselines.last = Some(row_end); } for column_index in 0..self.table.size.width { @@ -1078,46 +1052,65 @@ impl<'a> TableLayout<'a> { }, }; + let cell_rect = dimensions.get_cell_rect( + TableSlotCoordinates::new(column_index, row_index), + cell.rowspan, + cell.colspan, + ); + // If this cell has baseline alignment, it can adjust the table's overall baseline. + let row_baseline = self.row_baselines[row_index]; if cell.effective_vertical_align() == VerticalAlignKeyword::Baseline { + let baseline = cell_rect.start_corner.block + row_baseline; if row_index == 0 { - baselines.first = Some(row_offset + row_baseline); + baselines.first = Some(baseline); } - baselines.last = Some(row_offset + row_baseline); + baselines.last = Some(baseline); } - // Calculate the inline and block size of all rows and columns that this cell spans. - let inline_size: Au = (column_index..column_index + cell.colspan) - .map(|index| self.distributed_column_widths[index]) - .fold(Au::zero(), Au::add) + - ((cell.colspan - 1) as i32 * border_spacing.inline); - let block_size: Au = (row_index..row_index + cell.rowspan) - .map(|index| self.row_sizes[index]) - .fold(Au::zero(), Au::add) + - ((cell.rowspan - 1) as i32 * border_spacing.block); - - let cell_rect: LogicalRect = LogicalRect { - start_corner: LogicalVec2 { - inline: column_offset.into(), - block: row_offset.into(), - }, - size: LogicalVec2 { - inline: inline_size.into(), - block: block_size.into(), - }, - }; + let mut fragment = + cell.create_fragment(layout, cell_rect, row_baseline, positioning_context); + + let column = self.table.columns.get(column_index); + let column_group = column + .and_then(|column| column.group_index) + .and_then(|index| self.table.column_groups.get(index)); + if let Some(column_group) = column_group { + fragment.add_extra_background(ExtraBackground { + style: column_group.style.clone(), + rect: dimensions.get_column_group_rect(column_group), + }) + } - fragments.push(Fragment::Box(cell.create_fragment( - layout, - cell_rect, - row_baseline, - positioning_context, - ))); + if let Some(column) = column { + if !column.is_anonymous { + fragment.add_extra_background(ExtraBackground { + style: column.style.clone(), + rect: dimensions.get_column_rect(column_index), + }) + } + } - column_offset += inline_size + border_spacing.inline; - } + let row = self.table.rows.get(row_index); + let row_group = row + .and_then(|row| row.group_index) + .and_then(|index| self.table.row_groups.get(index)); + if let Some(row_group) = row_group { + fragment.add_extra_background(ExtraBackground { + style: row_group.style.clone(), + rect: dimensions.get_row_group_rect(row_group), + }) + } - row_offset += row_size + border_spacing.block; + if let Some(row) = row { + fragment.add_extra_background(ExtraBackground { + style: row.style.clone(), + rect: dimensions.get_row_rect(row_index), + }) + } + + fragments.push(Fragment::Box(fragment)); + } } if self.table.anonymous { @@ -1127,10 +1120,173 @@ impl<'a> TableLayout<'a> { IndependentLayout { fragments, - content_block_size: row_offset, + content_block_size: dimensions.table_rect.max_block_position(), baselines, } } + + fn make_fragments_for_columns_rows_and_groups( + &mut self, + dimensions: &TableAndTrackDimensions, + fragments: &mut Vec, + ) { + for column_group in self.table.column_groups.iter() { + if !column_group.is_empty() { + fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + column_group.base_fragment_info, + dimensions.get_column_group_rect(column_group).into(), + column_group.style.clone(), + ))); + } + } + + for (column_index, column) in self.table.columns.iter().enumerate() { + fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + column.base_fragment_info, + dimensions.get_column_rect(column_index).into(), + column.style.clone(), + ))); + } + + for row_group in self.table.row_groups.iter() { + if !row_group.is_empty() { + fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + row_group.base_fragment_info, + dimensions.get_row_group_rect(row_group).into(), + row_group.style.clone(), + ))); + } + } + + for (row_index, row) in self.table.rows.iter().enumerate() { + fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + row.base_fragment_info, + dimensions.get_row_rect(row_index).into(), + row.style.clone(), + ))); + } + } +} + +struct TableAndTrackDimensions { + /// The rect of the full table, not counting for borders, padding, and margin. + table_rect: LogicalRect, + /// The rect of the full table, not counting for borders, padding, and margin + /// and offset by any border spacing and caption. + table_cells_rect: LogicalRect, + /// The min and max block offsets of each table row. + row_dimensions: Vec<(Au, Au)>, + /// The min and max inline offsets of each table column + column_dimensions: Vec<(Au, Au)>, +} + +impl TableAndTrackDimensions { + fn new(table_layout: &TableLayout) -> Self { + let border_spacing = table_layout.table.border_spacing(); + + let mut column_dimensions = Vec::new(); + let mut column_offset = border_spacing.inline; + for column_index in 0..table_layout.table.size.width { + let column_size = table_layout.distributed_column_widths[column_index]; + column_dimensions.push((column_offset, column_offset + column_size)); + column_offset += column_size + border_spacing.inline; + } + + let mut row_dimensions = Vec::new(); + let mut row_offset = border_spacing.block; + for row_index in 0..table_layout.table.size.height { + let row_size = table_layout.row_sizes[row_index]; + row_dimensions.push((row_offset, row_offset + row_size)); + row_offset += row_size + border_spacing.block; + } + + let table_start_corner = LogicalVec2 { + inline: column_dimensions[0].0, + block: row_dimensions[0].0, + }; + let table_size = &LogicalVec2 { + inline: column_dimensions[column_dimensions.len() - 1].1, + block: row_dimensions[row_dimensions.len() - 1].1, + } - &table_start_corner; + let table_cells_rect = LogicalRect { + start_corner: table_start_corner, + size: table_size, + }; + + let table_rect = LogicalRect { + start_corner: LogicalVec2::zero(), + size: LogicalVec2 { + inline: column_offset, + block: row_offset, + }, + }; + + Self { + table_rect, + table_cells_rect, + row_dimensions, + column_dimensions, + } + } + + fn get_row_rect(&self, row_index: usize) -> LogicalRect { + let mut row_rect = self.table_cells_rect.clone(); + let row_dimensions = self.row_dimensions[row_index]; + row_rect.start_corner.block = row_dimensions.0; + row_rect.size.block = row_dimensions.1 - row_dimensions.0; + row_rect + } + + fn get_column_rect(&self, column_index: usize) -> LogicalRect { + let mut row_rect = self.table_cells_rect.clone(); + let column_dimensions = self.column_dimensions[column_index]; + row_rect.start_corner.inline = column_dimensions.0; + row_rect.size.inline = column_dimensions.1 - column_dimensions.0; + row_rect + } + + fn get_row_group_rect(&self, row_group: &TableTrackGroup) -> LogicalRect { + if row_group.is_empty() { + return LogicalRect::zero(); + } + + let mut row_group_rect = self.table_cells_rect.clone(); + let block_start = self.row_dimensions[row_group.track_range.start].0; + let block_end = self.row_dimensions[row_group.track_range.end - 1].1; + row_group_rect.start_corner.block = block_start; + row_group_rect.size.block = block_end - block_start; + row_group_rect + } + + fn get_column_group_rect(&self, column_group: &TableTrackGroup) -> LogicalRect { + if column_group.is_empty() { + return LogicalRect::zero(); + } + + let mut column_group_rect = self.table_cells_rect.clone(); + let inline_start = self.column_dimensions[column_group.track_range.start].0; + let inline_end = self.column_dimensions[column_group.track_range.end - 1].1; + column_group_rect.start_corner.inline = inline_start; + column_group_rect.size.inline = inline_end - inline_start; + column_group_rect + } + + fn get_cell_rect( + &self, + coordinates: TableSlotCoordinates, + rowspan: usize, + colspan: usize, + ) -> LogicalRect { + let start_corner = LogicalVec2 { + inline: self.column_dimensions[coordinates.x].0, + block: self.row_dimensions[coordinates.y].0, + }; + let size = &LogicalVec2 { + inline: self.column_dimensions[coordinates.x + colspan - 1].1, + block: self.row_dimensions[coordinates.y + rowspan - 1].1, + } - &start_corner; + LogicalRect { start_corner, size } + } } impl Table { @@ -1200,6 +1356,33 @@ impl Table { .0 } + fn get_column_measure_for_column_at_index( + &self, + writing_mode: WritingMode, + column_index: usize, + ) -> CellOrColumnMeasure { + let column = match self.columns.get(column_index) { + Some(column) => column, + None => return CellOrColumnMeasure::zero(), + }; + + let (size, min_size, max_size) = get_sizes_from_style(&column.style, writing_mode); + let percentage_contribution = + get_size_percentage_contribution_from_style(&column.style, writing_mode); + + CellOrColumnMeasure { + content_sizes: ContentSizes { + // > The outer min-content width of a table-column or table-column-group is + // > max(min-width, width). + min_content: min_size.inline.max(size.inline), + // > The outer max-content width of a table-column or table-column-group is + // > max(min-width, min(max-width, width)). + max_content: min_size.inline.max(max_size.inline.min(size.inline)), + }, + percentage_width: percentage_contribution.inline, + } + } + pub(crate) fn layout( &self, layout_context: &LayoutContext, @@ -1250,13 +1433,14 @@ impl TableSlotCell { fn create_fragment( &self, mut layout: CellLayout, - cell_rect: LogicalRect, + cell_rect: LogicalRect, cell_baseline: Au, 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 cell_rect: LogicalRect = cell_rect.into(); let cell_content_rect = cell_rect.deflate(&(&layout.padding + &layout.border)); let content_block_size = layout.layout.content_block_size.into(); let vertical_align_offset = match self.effective_vertical_align() { @@ -1278,7 +1462,7 @@ impl TableSlotCell { inline: Length::new(0.), block: vertical_align_offset, }; - let vertical_align_fragment = AnonymousFragment::new( + let vertical_align_fragment = PositioningFragment::new_anonymous( vertical_align_fragment_rect, layout.layout.fragments, self.style.writing_mode, @@ -1302,7 +1486,7 @@ impl TableSlotCell { BoxFragment::new( self.base_fragment_info, self.style.clone(), - vec![Fragment::Anonymous(vertical_align_fragment)], + vec![Fragment::Positioning(vertical_align_fragment)], cell_content_rect, layout.padding, layout.border, @@ -1313,3 +1497,73 @@ impl TableSlotCell { .with_baselines(layout.layout.baselines) } } + +fn get_size_percentage_contribution_from_style( + style: &Arc, + writing_mode: WritingMode, +) -> LogicalVec2 { + // From + // > The percentage contribution of a table cell, column, or column group is defined + // > in terms of the computed values of width and max-width that have computed values + // > that are percentages: + // > min(percentage width, percentage max-width). + // > If the computed values are not percentages, then 0% is used for width, and an + // > infinite percentage is used for max-width. + let size = style.box_size(writing_mode); + let max_size = style.max_box_size(writing_mode); + + let get_contribution_for_axis = + |size: LengthPercentageOrAuto<'_>, max_size: Option<&ComputedLengthPercentage>| { + let size_percentage = size + .non_auto() + .and_then(|length_percentage| length_percentage.to_percentage()) + .unwrap_or(Percentage(0.)); + let max_size_percentage = max_size + .and_then(|length_percentage| length_percentage.to_percentage()) + .unwrap_or(Percentage(f32::INFINITY)); + Percentage(size_percentage.0.min(max_size_percentage.0)) + }; + + LogicalVec2 { + inline: get_contribution_for_axis(size.inline, max_size.inline), + block: get_contribution_for_axis(size.block, max_size.block), + } +} + +fn get_sizes_from_style( + style: &Arc, + writing_mode: WritingMode, +) -> (LogicalVec2, LogicalVec2, LogicalVec2) { + let get_max_size_for_axis = |size: Option<&ComputedLengthPercentage>| { + size.map_or_else( + || MAX_AU, + |length_percentage| length_percentage.resolve(Length::zero()).into(), + ) + }; + + let max_size = style.max_box_size(writing_mode); + let max_size = LogicalVec2 { + inline: get_max_size_for_axis(max_size.inline), + block: get_max_size_for_axis(max_size.block), + }; + + let get_size_for_axis = |size: LengthPercentageOrAuto<'_>| { + size.percentage_relative_to(Length::zero()) + .map(|value| value.into()) + .auto_is(Au::zero) + }; + + let min_size = style.min_box_size(writing_mode); + let min_size = LogicalVec2 { + inline: get_size_for_axis(min_size.inline), + block: get_size_for_axis(min_size.block), + }; + + let size = style.box_size(writing_mode); + let size = LogicalVec2 { + inline: get_size_for_axis(size.inline), + block: get_size_for_axis(size.block), + }; + + (size, min_size, max_size) +} diff --git a/components/layout_2020/table/mod.rs b/components/layout_2020/table/mod.rs index 25314cdeb32bd..f11016a894868 100644 --- a/components/layout_2020/table/mod.rs +++ b/components/layout_2020/table/mod.rs @@ -12,6 +12,8 @@ mod construct; mod layout; +use std::ops::Range; + pub(crate) use construct::AnonymousTableContent; pub use construct::TableBuilder; use euclid::{Point2D, Size2D, UnknownUnit, Vector2D}; @@ -32,6 +34,19 @@ pub struct Table { #[serde(skip_serializing)] style: Arc, + /// The column groups for this table. + pub column_groups: Vec, + + /// The columns of this tabled defined by ` | display: table-column-group` + /// and ` | display: table-column` elements as well as `display: table-column`. + pub columns: Vec, + + /// The rows groups for this table deinfed by ``, ``, and ``. + pub row_groups: Vec, + + /// The rows of this tabled defined by `` or `display: table-row` elements. + pub rows: Vec, + /// The content of the slots of this table. pub slots: Vec>, @@ -46,6 +61,10 @@ impl Table { pub(crate) fn new(style: Arc) -> Self { Self { style, + column_groups: Vec::new(), + columns: Vec::new(), + row_groups: Vec::new(), + rows: Vec::new(), slots: Vec::new(), size: TableSize::zero(), anonymous: false, @@ -167,3 +186,52 @@ impl TableSlot { Self::Spanned(vec![offset]) } } + +/// A row or column of a table. +#[derive(Clone, Debug, Serialize)] +pub struct TableTrack { + /// The [`BaseFragmentInfo`] of this cell. + base_fragment_info: BaseFragmentInfo, + + /// The style of this table column. + #[serde(skip_serializing)] + style: Arc, + + /// The index of the table row or column group parent in the table's list of row or column + /// groups. + group_index: Option, + + /// Whether or not this [`TableTrack`] was anonymous, for instance created due to + /// a `span` attribute set on a parent ``. + is_anonymous: bool, +} + +#[derive(Debug, PartialEq, Serialize)] +pub enum TableTrackGroupType { + HeaderGroup, + FooterGroup, + RowGroup, + ColumnGroup, +} + +#[derive(Debug, Serialize)] +pub struct TableTrackGroup { + /// The [`BaseFragmentInfo`] of this [`TableTrackGroup`]. + base_fragment_info: BaseFragmentInfo, + + /// The style of this [`TableTrackGroup`]. + #[serde(skip_serializing)] + style: Arc, + + /// The type of this [`TableTrackGroup`]. + group_type: TableTrackGroupType, + + /// The range of tracks in this [`TableTrackGroup`]. + track_range: Range, +} + +impl TableTrackGroup { + pub(super) fn is_empty(&self) -> bool { + self.track_range.is_empty() + } +} diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 7d2c9afb2aa79..546df3126b382 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -64,6 +64,7 @@ use xml5ever::serialize::TraversalScope::{ }; use xml5ever::serialize::{SerializeOpts as XmlSerializeOpts, TraversalScope as XmlTraversalScope}; +use super::htmltablecolelement::{HTMLTableColElement, HTMLTableColElementLayoutHelpers}; use crate::dom::activation::Activatable; use crate::dom::attr::{Attr, AttrHelpersForLayout}; use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref, RefMut}; @@ -614,8 +615,9 @@ pub trait LayoutElementHelpers<'dom> { fn synthesize_presentational_hints_for_legacy_attributes(self, hints: &mut V) where V: Push; - fn get_colspan(self) -> u32; - fn get_rowspan(self) -> u32; + fn get_span(self) -> Option; + fn get_colspan(self) -> Option; + fn get_rowspan(self) -> Option; fn is_html_element(self) -> bool; fn id_attribute(self) -> *const Option; fn style_attribute(self) -> *const Option>>; @@ -1019,24 +1021,22 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> { } } - fn get_colspan(self) -> u32 { - if let Some(this) = self.downcast::() { - this.get_colspan().unwrap_or(1) - } else { - // Don't panic since `display` can cause this to be called on arbitrary - // elements. - 1 - } + fn get_span(self) -> Option { + // Don't panic since `display` can cause this to be called on arbitrary elements. + self.downcast::() + .and_then(|element| element.get_span()) } - fn get_rowspan(self) -> u32 { - if let Some(this) = self.downcast::() { - this.get_rowspan().unwrap_or(1) - } else { - // Don't panic since `display` can cause this to be called on arbitrary - // elements. - 1 - } + fn get_colspan(self) -> Option { + // Don't panic since `display` can cause this to be called on arbitrary elements. + self.downcast::() + .and_then(|element| element.get_colspan()) + } + + fn get_rowspan(self) -> Option { + // Don't panic since `display` can cause this to be called on arbitrary elements. + self.downcast::() + .and_then(|element| element.get_rowspan()) } #[inline] diff --git a/components/script/dom/htmltablecolelement.rs b/components/script/dom/htmltablecolelement.rs index 3829f59534167..78aadee01c2a2 100644 --- a/components/script/dom/htmltablecolelement.rs +++ b/components/script/dom/htmltablecolelement.rs @@ -3,14 +3,23 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use dom_struct::dom_struct; -use html5ever::{LocalName, Prefix}; +use html5ever::{local_name, namespace_url, ns, LocalName, Prefix}; use js::rust::HandleObject; +use style::attr::AttrValue; +use super::bindings::root::LayoutDom; +use super::element::Element; +use crate::dom::bindings::codegen::Bindings::HTMLTableColElementBinding::HTMLTableColElementMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; use crate::dom::document::Document; +use crate::dom::element::LayoutElementHelpers; use crate::dom::htmlelement::HTMLElement; use crate::dom::node::Node; +use crate::dom::virtualmethods::VirtualMethods; + +const DEFAULT_SPAN: u32 = 1; #[dom_struct] pub struct HTMLTableColElement { @@ -47,3 +56,47 @@ impl HTMLTableColElement { n } } + +impl HTMLTableColElementMethods for HTMLTableColElement { + // + make_uint_getter!(Span, "span", DEFAULT_SPAN); + // + make_uint_setter!(SetSpan, "span", DEFAULT_SPAN); +} + +pub trait HTMLTableColElementLayoutHelpers<'dom> { + fn get_span(self) -> Option; +} + +impl<'dom> HTMLTableColElementLayoutHelpers<'dom> for LayoutDom<'dom, HTMLTableColElement> { + fn get_span(self) -> Option { + self.upcast::() + .get_attr_for_layout(&ns!(), &local_name!("span")) + .map(AttrValue::as_uint) + } +} + +impl VirtualMethods for HTMLTableColElement { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::() as &dyn VirtualMethods) + } + + fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue { + match *local_name { + local_name!("span") => { + let mut attr = AttrValue::from_u32(value.into(), DEFAULT_SPAN); + if let AttrValue::UInt(ref mut s, ref mut val) = attr { + if *val == 0 { + *val = 1; + *s = "1".into(); + } + } + attr + }, + _ => self + .super_type() + .unwrap() + .parse_plain_attribute(local_name, value), + } + } +} diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 889eab0738002..673816827831f 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -5,6 +5,7 @@ use html5ever::LocalName; use style::attr::AttrValue; +use super::htmltablecolelement::HTMLTableColElement; use crate::dom::attr::Attr; use crate::dom::bindings::inheritance::{ Castable, ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId, @@ -250,6 +251,9 @@ pub fn vtable_for(node: &Node) -> &dyn VirtualMethods { NodeTypeId::Element(ElementTypeId::HTMLElement( HTMLElementTypeId::HTMLTableCellElement, )) => node.downcast::().unwrap() as &dyn VirtualMethods, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement)) => { + node.downcast::().unwrap() as &dyn VirtualMethods + }, NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement)) => { node.downcast::().unwrap() as &dyn VirtualMethods }, diff --git a/components/script/dom/webidls/HTMLTableColElement.webidl b/components/script/dom/webidls/HTMLTableColElement.webidl index 1d8536438276c..efb50baaa21df 100644 --- a/components/script/dom/webidls/HTMLTableColElement.webidl +++ b/components/script/dom/webidls/HTMLTableColElement.webidl @@ -7,8 +7,8 @@ interface HTMLTableColElement : HTMLElement { [HTMLConstructor] constructor(); - // [CEReactions] - // attribute unsigned long span; + [CEReactions] + attribute unsigned long span; // also has obsolete members }; diff --git a/components/script/layout_dom/node.rs b/components/script/layout_dom/node.rs index 521e20957f7a7..9ce25c78f8417 100644 --- a/components/script/layout_dom/node.rs +++ b/components/script/layout_dom/node.rs @@ -465,7 +465,16 @@ impl<'dom, LayoutDataType: LayoutDataTrait> ThreadSafeLayoutNode<'dom> this.iframe_pipeline_id() } - fn get_colspan(&self) -> u32 { + fn get_span(&self) -> Option { + unsafe { + self.get_jsmanaged() + .downcast::() + .unwrap() + .get_span() + } + } + + fn get_colspan(&self) -> Option { unsafe { self.get_jsmanaged() .downcast::() @@ -474,7 +483,7 @@ impl<'dom, LayoutDataType: LayoutDataTrait> ThreadSafeLayoutNode<'dom> } } - fn get_rowspan(&self) -> u32 { + fn get_rowspan(&self) -> Option { unsafe { self.get_jsmanaged() .downcast::() diff --git a/components/shared/script_layout/wrapper_traits.rs b/components/shared/script_layout/wrapper_traits.rs index bc7f2518d8280..54cafc26beaa8 100644 --- a/components/shared/script_layout/wrapper_traits.rs +++ b/components/shared/script_layout/wrapper_traits.rs @@ -294,9 +294,9 @@ pub trait ThreadSafeLayoutNode<'dom>: /// not an iframe element, fails. Returns None if there is no nested browsing context. fn iframe_pipeline_id(&self) -> Option; - fn get_colspan(&self) -> u32; - - fn get_rowspan(&self) -> u32; + fn get_span(&self) -> Option; + fn get_colspan(&self) -> Option; + fn get_rowspan(&self) -> Option; fn fragment_type(&self) -> FragmentType { self.get_pseudo_element_type().fragment_type() diff --git a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-colgroup-001.xht.ini b/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-colgroup-001.xht.ini deleted file mode 100644 index 2c7bc4c779070..0000000000000 --- a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-colgroup-001.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[table-backgrounds-bs-colgroup-001.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-column-001.xht.ini b/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-column-001.xht.ini deleted file mode 100644 index 1c1b0224f08f7..0000000000000 --- a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-column-001.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[table-backgrounds-bs-column-001.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-row-001.xht.ini b/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-row-001.xht.ini deleted file mode 100644 index 6825f1c18101f..0000000000000 --- a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-row-001.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[table-backgrounds-bs-row-001.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-rowgroup-001.xht.ini b/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-rowgroup-001.xht.ini deleted file mode 100644 index 6c8d8b5ba3d4a..0000000000000 --- a/tests/wpt/meta/css/CSS2/tables/table-backgrounds-bs-rowgroup-001.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[table-backgrounds-bs-rowgroup-001.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/border-collapse-rowspan-cell.html.ini b/tests/wpt/meta/css/css-tables/border-collapse-rowspan-cell.html.ini deleted file mode 100644 index 8e2e4d7a3f26a..0000000000000 --- a/tests/wpt/meta/css/css-tables/border-collapse-rowspan-cell.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[border-collapse-rowspan-cell.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/bounding-box-computation-1.html.ini b/tests/wpt/meta/css/css-tables/bounding-box-computation-1.html.ini index 884d05d6c38de..9f6ca8b767fe1 100644 --- a/tests/wpt/meta/css/css-tables/bounding-box-computation-1.html.ini +++ b/tests/wpt/meta/css/css-tables/bounding-box-computation-1.html.ini @@ -2,26 +2,14 @@ [Table-cell is 100px tall] expected: FAIL - [Table-row is 100px wide] - expected: FAIL - [Table-row is 100px tall] expected: FAIL - [Table-row-group is 100px wide] - expected: FAIL - [Table-row-group is 100px tall] expected: FAIL - [Table-column is 100px wide] - expected: FAIL - [Table-column is 100px tall] expected: FAIL - [Table-column-group is 100px wide] - expected: FAIL - [Table-column-group is 100px tall] expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/bounding-box-computation-2.html.ini b/tests/wpt/meta/css/css-tables/bounding-box-computation-2.html.ini index 1a0b942842e07..7db55a8a39d41 100644 --- a/tests/wpt/meta/css/css-tables/bounding-box-computation-2.html.ini +++ b/tests/wpt/meta/css/css-tables/bounding-box-computation-2.html.ini @@ -13,15 +13,3 @@ [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 diff --git a/tests/wpt/meta/css/css-tables/bounding-box-computation-3.html.ini b/tests/wpt/meta/css/css-tables/bounding-box-computation-3.html.ini index 5283592a112ee..b75475e96e590 100644 --- a/tests/wpt/meta/css/css-tables/bounding-box-computation-3.html.ini +++ b/tests/wpt/meta/css/css-tables/bounding-box-computation-3.html.ini @@ -7,15 +7,3 @@ [First (empty) table-row-group should be located at 10px top] expected: FAIL - - [Second table-row-group is 100px wide] - expected: FAIL - - [Second table-row-group should be located at 10px left] - expected: FAIL - - [Second table-row-group should be located at 10px top] - expected: FAIL - - [Second table-row-group is 100px tall] - expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/col-definite-max-size-001.html.ini b/tests/wpt/meta/css/css-tables/col-definite-max-size-001.html.ini new file mode 100644 index 0000000000000..373b19d934635 --- /dev/null +++ b/tests/wpt/meta/css/css-tables/col-definite-max-size-001.html.ini @@ -0,0 +1,2 @@ +[col-definite-max-size-001.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/col-definite-min-size-001.html.ini b/tests/wpt/meta/css/css-tables/col-definite-min-size-001.html.ini new file mode 100644 index 0000000000000..124a656e90b69 --- /dev/null +++ b/tests/wpt/meta/css/css-tables/col-definite-min-size-001.html.ini @@ -0,0 +1,2 @@ +[col-definite-min-size-001.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/col-definite-size-001.html.ini b/tests/wpt/meta/css/css-tables/col-definite-size-001.html.ini new file mode 100644 index 0000000000000..c5752353987a9 --- /dev/null +++ b/tests/wpt/meta/css/css-tables/col-definite-size-001.html.ini @@ -0,0 +1,2 @@ +[col-definite-size-001.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/height-distribution/computing-row-measure-1.html.ini b/tests/wpt/meta/css/css-tables/height-distribution/computing-row-measure-1.html.ini index bfaa02bea2697..1d47684a4eac0 100644 --- a/tests/wpt/meta/css/css-tables/height-distribution/computing-row-measure-1.html.ini +++ b/tests/wpt/meta/css/css-tables/height-distribution/computing-row-measure-1.html.ini @@ -5,8 +5,5 @@ [Checking intermediate min-content width for span 2 (2)] expected: FAIL - [Checking intermediate min-content width for span 2 (3)] - expected: FAIL - [Checking intermediate min-content width for span 2 (4)] expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/html-to-css-mapping-1.html.ini b/tests/wpt/meta/css/css-tables/html-to-css-mapping-1.html.ini index 55bc6f8e5f691..917c5128e7f24 100644 --- a/tests/wpt/meta/css/css-tables/html-to-css-mapping-1.html.ini +++ b/tests/wpt/meta/css/css-tables/html-to-css-mapping-1.html.ini @@ -2,9 +2,6 @@ [HTML -> CSS Mapping is applied correctly on proper table markup (border-spacing, padding)] expected: FAIL - [HTML -> CSS Mapping is applied correctly on improper table markup (no table => no border-spacing, but padding)] - expected: FAIL - [HTML -> CSS Mapping is applied correctly on improper table markup (no td => border-spacing, but no padding)] expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/paint/col-paint-htb-rtl.html.ini b/tests/wpt/meta/css/css-tables/paint/col-paint-htb-rtl.html.ini new file mode 100644 index 0000000000000..5b4666dc7775f --- /dev/null +++ b/tests/wpt/meta/css/css-tables/paint/col-paint-htb-rtl.html.ini @@ -0,0 +1,2 @@ +[col-paint-htb-rtl.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/table-model-fixup-2.html.ini b/tests/wpt/meta/css/css-tables/table-model-fixup-2.html.ini index e0fca6d7734b9..55d8e50706fbe 100644 --- a/tests/wpt/meta/css/css-tables/table-model-fixup-2.html.ini +++ b/tests/wpt/meta/css/css-tables/table-model-fixup-2.html.ini @@ -7,3 +7,12 @@ [Replaced elements inside a table cannot be table-row and are considered inline -- input elements (width)] expected: FAIL + + [Replaced elements inside a table cannot be table-row and are considered inline -- input elements (top)] + expected: FAIL + + [Replaced elements inside a table cannot be table-column and are considered inline -- input elements (top)] + expected: FAIL + + [Replaced elements outside a table cannot be table-row and are considered inline -- input=file elements] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/table-model-fixup.html.ini b/tests/wpt/meta/css/css-tables/table-model-fixup.html.ini index 8d5efdc7f21c9..cc92bd8ce4a01 100644 --- a/tests/wpt/meta/css/css-tables/table-model-fixup.html.ini +++ b/tests/wpt/meta/css/css-tables/table-model-fixup.html.ini @@ -11,9 +11,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 - [2.3 happens after 2.1. and 2.2. (2/2)] - 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 @@ -22,3 +19,6 @@ [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 + + [2.3 happens after 2.1. and 2.2. (1/2)] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/tentative/baseline-table.html.ini b/tests/wpt/meta/css/css-tables/tentative/baseline-table.html.ini index 2acbad5377051..d89695fbaef8c 100644 --- a/tests/wpt/meta/css/css-tables/tentative/baseline-table.html.ini +++ b/tests/wpt/meta/css/css-tables/tentative/baseline-table.html.ini @@ -7,3 +7,6 @@ [.container 13] expected: FAIL + + [.container 3] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/tentative/colspan-redistribution.html.ini b/tests/wpt/meta/css/css-tables/tentative/colspan-redistribution.html.ini index 60cfeb3eafdd7..56f4a2c45afe2 100644 --- a/tests/wpt/meta/css/css-tables/tentative/colspan-redistribution.html.ini +++ b/tests/wpt/meta/css/css-tables/tentative/colspan-redistribution.html.ini @@ -82,3 +82,6 @@ [table 31] expected: FAIL + + [table 8] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/tentative/element-sizing.html.ini b/tests/wpt/meta/css/css-tables/tentative/element-sizing.html.ini index 2a474ee5dc539..03e6e778159ce 100644 --- a/tests/wpt/meta/css/css-tables/tentative/element-sizing.html.ini +++ b/tests/wpt/meta/css/css-tables/tentative/element-sizing.html.ini @@ -1,6 +1,3 @@ [element-sizing.html] - [table 1] - expected: FAIL - [table 2] expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/tentative/rowspan-height-redistribution.html.ini b/tests/wpt/meta/css/css-tables/tentative/rowspan-height-redistribution.html.ini index 0783c23e66628..97ade62c85aea 100644 --- a/tests/wpt/meta/css/css-tables/tentative/rowspan-height-redistribution.html.ini +++ b/tests/wpt/meta/css/css-tables/tentative/rowspan-height-redistribution.html.ini @@ -23,27 +23,12 @@ [table 9] expected: FAIL - [table 10] - expected: FAIL - - [table 11] - expected: FAIL - - [table 12] - expected: FAIL - - [table 13] - expected: FAIL - [table 14] expected: FAIL [table 15] expected: FAIL - [table 16] - expected: FAIL - [table 17] expected: FAIL @@ -62,8 +47,5 @@ [table 23] expected: FAIL - [table 24] - expected: FAIL - [table 20] expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/tentative/td-box-sizing-003.html.ini b/tests/wpt/meta/css/css-tables/tentative/td-box-sizing-003.html.ini index 121baab5ecb25..67959ec278c82 100644 --- a/tests/wpt/meta/css/css-tables/tentative/td-box-sizing-003.html.ini +++ b/tests/wpt/meta/css/css-tables/tentative/td-box-sizing-003.html.ini @@ -1,10 +1,4 @@ [td-box-sizing-003.html] - [table 2] - expected: FAIL - - [table 3] - expected: FAIL - [table 8] expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/visibility-collapse-row-group-001.html.ini b/tests/wpt/meta/css/css-tables/visibility-collapse-row-group-001.html.ini index bd16f222639f6..a97baf9ec4317 100644 --- a/tests/wpt/meta/css/css-tables/visibility-collapse-row-group-001.html.ini +++ b/tests/wpt/meta/css/css-tables/visibility-collapse-row-group-001.html.ini @@ -1,3 +1,9 @@ [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 diff --git a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002-border-separate.html.ini b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002-border-separate.html.ini index 2adc27f445d3a..bd22dfeb3e026 100644 --- a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002-border-separate.html.ini +++ b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002-border-separate.html.ini @@ -1,3 +1,6 @@ [visibility-collapse-rowspan-002-border-separate.html] [spanning cell shrinks to sum of remaining three rows' height] expected: FAIL + + [spanning row visibility:collapse makes row height 0] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002.html.ini b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002.html.ini index de808e0731a3d..8d062d5c74c98 100644 --- a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002.html.ini +++ b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-002.html.ini @@ -1,3 +1,6 @@ [visibility-collapse-rowspan-002.html] [spanning cell shrinks to sum of remaining three rows' height] expected: FAIL + + [spanning row visibility:collapse makes row height 0] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003-border-separate.html.ini b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003-border-separate.html.ini new file mode 100644 index 0000000000000..5f7a6fe9baa12 --- /dev/null +++ b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003-border-separate.html.ini @@ -0,0 +1,3 @@ +[visibility-collapse-rowspan-003-border-separate.html] + [collapsed row has zero height] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003.html.ini b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003.html.ini new file mode 100644 index 0000000000000..bd40ed0a98249 --- /dev/null +++ b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-003.html.ini @@ -0,0 +1,3 @@ +[visibility-collapse-rowspan-003.html] + [collapsed row has zero height] + expected: FAIL diff --git a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-004-dynamic.html.ini b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-004-dynamic.html.ini index ea8a6fe368b5f..a7d5292bed6bd 100644 --- a/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-004-dynamic.html.ini +++ b/tests/wpt/meta/css/css-tables/visibility-collapse-rowspan-004-dynamic.html.ini @@ -4,3 +4,9 @@ [(2nd collapse) spanning cell shrinks to sum of remaining three rows' height] expected: FAIL + + [third row visibility:collapse makes row height 0] + expected: FAIL + + [(2nd collapse) third row visibility:collapse makes row height 0] + expected: FAIL diff --git a/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLTableColElement.html.ini b/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLTableColElement.html.ini deleted file mode 100644 index c50556892c885..0000000000000 --- a/tests/wpt/meta/custom-elements/reactions/customized-builtins/HTMLTableColElement.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[HTMLTableColElement.html] - [span on HTMLTableColElement must enqueue an attributeChanged reaction when adding a new attribute] - expected: FAIL - - [span on HTMLTableColElement must enqueue an attributeChanged reaction when replacing an existing attribute] - expected: FAIL diff --git a/tests/wpt/meta/html/dom/idlharness.https.html.ini b/tests/wpt/meta/html/dom/idlharness.https.html.ini index eb3d5b62b5ac4..42dfe6b0f2b7a 100644 --- a/tests/wpt/meta/html/dom/idlharness.https.html.ini +++ b/tests/wpt/meta/html/dom/idlharness.https.html.ini @@ -3432,9 +3432,6 @@ [HTMLAreaElement interface: attribute origin] expected: FAIL - [HTMLTableColElement interface: document.createElement("colgroup") must inherit property "span" with the proper type] - expected: FAIL - [HTMLTableSectionElement interface: attribute chOff] expected: FAIL @@ -3831,9 +3828,6 @@ [HTMLAreaElement interface: attribute hostname] expected: FAIL - [HTMLTableColElement interface: attribute span] - expected: FAIL - [HTMLTableElement interface: document.createElement("table") must inherit property "border" with the proper type] expected: FAIL @@ -4221,9 +4215,6 @@ [HTMLFrameElement interface: attribute scrolling] expected: FAIL - [HTMLTableColElement interface: document.createElement("col") must inherit property "span" with the proper type] - expected: FAIL - [HTMLElement interface: document.createElement("noscript") must inherit property "accessKeyLabel" with the proper type] expected: FAIL diff --git a/tests/wpt/meta/html/dom/reflection-tabular.html.ini b/tests/wpt/meta/html/dom/reflection-tabular.html.ini index 5bf1b18d07e9e..b833e5d930cf4 100644 --- a/tests/wpt/meta/html/dom/reflection-tabular.html.ini +++ b/tests/wpt/meta/html/dom/reflection-tabular.html.ini @@ -1829,30 +1829,9 @@ [colgroup.tabIndex: IDL set to -2147483648] expected: FAIL - [colgroup.span: typeof IDL attribute] - expected: FAIL - - [colgroup.span: IDL get with DOM attribute unset] - expected: FAIL - - [colgroup.span: setAttribute() to -2147483649] - expected: FAIL - - [colgroup.span: setAttribute() to -2147483648] - expected: FAIL - - [colgroup.span: setAttribute() to -36] - expected: FAIL - - [colgroup.span: setAttribute() to -1] - expected: FAIL - [colgroup.span: setAttribute() to 0] expected: FAIL - [colgroup.span: setAttribute() to 1] - expected: FAIL - [colgroup.span: setAttribute() to 2147483647] expected: FAIL @@ -1865,171 +1844,24 @@ [colgroup.span: setAttribute() to 4294967296] expected: FAIL - [colgroup.span: setAttribute() to ""] - expected: FAIL - - [colgroup.span: setAttribute() to "-1"] - expected: FAIL - [colgroup.span: setAttribute() to "-0"] expected: FAIL [colgroup.span: setAttribute() to "0"] expected: FAIL - [colgroup.span: setAttribute() to "1"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\t7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\v7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\f7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to "7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\n7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\r7"] - expected: FAIL - - [colgroup.span: setAttribute() to "
7"] - expected: FAIL - - [colgroup.span: setAttribute() to "
7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to "᠎7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " 7"] - expected: FAIL - - [colgroup.span: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "] - expected: FAIL - - [colgroup.span: setAttribute() to undefined] - expected: FAIL - - [colgroup.span: setAttribute() to 1.5] - expected: FAIL - - [colgroup.span: setAttribute() to "5%"] - expected: FAIL - - [colgroup.span: setAttribute() to "+100"] - expected: FAIL - - [colgroup.span: setAttribute() to ".5"] - expected: FAIL - - [colgroup.span: setAttribute() to true] - expected: FAIL - - [colgroup.span: setAttribute() to false] - expected: FAIL - - [colgroup.span: setAttribute() to object "[object Object\]"] - expected: FAIL - - [colgroup.span: setAttribute() to NaN] - expected: FAIL - - [colgroup.span: setAttribute() to Infinity] - expected: FAIL - - [colgroup.span: setAttribute() to -Infinity] - expected: FAIL - - [colgroup.span: setAttribute() to "\\0"] - expected: FAIL - - [colgroup.span: setAttribute() to object "2"] - expected: FAIL - - [colgroup.span: setAttribute() to object "3"] - expected: FAIL - - [colgroup.span: setAttribute() to 1000] - expected: FAIL - [colgroup.span: setAttribute() to 1001] expected: FAIL [colgroup.span: IDL set to 0] expected: FAIL - [colgroup.span: IDL set to 1] - expected: FAIL - - [colgroup.span: IDL set to 257] - expected: FAIL - [colgroup.span: IDL set to 2147483647] expected: FAIL [colgroup.span: IDL set to "-0"] expected: FAIL - [colgroup.span: IDL set to 2147483648] - expected: FAIL - - [colgroup.span: IDL set to 4294967295] - expected: FAIL - - [colgroup.span: IDL set to 1000] - expected: FAIL - [colgroup.span: IDL set to 1001] expected: FAIL @@ -2909,30 +2741,9 @@ [col.tabIndex: IDL set to -2147483648] expected: FAIL - [col.span: typeof IDL attribute] - expected: FAIL - - [col.span: IDL get with DOM attribute unset] - expected: FAIL - - [col.span: setAttribute() to -2147483649] - expected: FAIL - - [col.span: setAttribute() to -2147483648] - expected: FAIL - - [col.span: setAttribute() to -36] - expected: FAIL - - [col.span: setAttribute() to -1] - expected: FAIL - [col.span: setAttribute() to 0] expected: FAIL - [col.span: setAttribute() to 1] - expected: FAIL - [col.span: setAttribute() to 2147483647] expected: FAIL @@ -2945,171 +2756,24 @@ [col.span: setAttribute() to 4294967296] expected: FAIL - [col.span: setAttribute() to ""] - expected: FAIL - - [col.span: setAttribute() to "-1"] - expected: FAIL - [col.span: setAttribute() to "-0"] expected: FAIL [col.span: setAttribute() to "0"] expected: FAIL - [col.span: setAttribute() to "1"] - expected: FAIL - - [col.span: setAttribute() to "\\t7"] - expected: FAIL - - [col.span: setAttribute() to "\\v7"] - expected: FAIL - - [col.span: setAttribute() to "\\f7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to "7"] - expected: FAIL - - [col.span: setAttribute() to "\\n7"] - expected: FAIL - - [col.span: setAttribute() to "\\r7"] - expected: FAIL - - [col.span: setAttribute() to "
7"] - expected: FAIL - - [col.span: setAttribute() to "
7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to "᠎7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " 7"] - expected: FAIL - - [col.span: setAttribute() to " \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07 \\b\\t\\n\\v\\f\\r\\x0e\\x0f \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17 \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f foo "] - expected: FAIL - - [col.span: setAttribute() to undefined] - expected: FAIL - - [col.span: setAttribute() to 1.5] - expected: FAIL - - [col.span: setAttribute() to "5%"] - expected: FAIL - - [col.span: setAttribute() to "+100"] - expected: FAIL - - [col.span: setAttribute() to ".5"] - expected: FAIL - - [col.span: setAttribute() to true] - expected: FAIL - - [col.span: setAttribute() to false] - expected: FAIL - - [col.span: setAttribute() to object "[object Object\]"] - expected: FAIL - - [col.span: setAttribute() to NaN] - expected: FAIL - - [col.span: setAttribute() to Infinity] - expected: FAIL - - [col.span: setAttribute() to -Infinity] - expected: FAIL - - [col.span: setAttribute() to "\\0"] - expected: FAIL - - [col.span: setAttribute() to object "2"] - expected: FAIL - - [col.span: setAttribute() to object "3"] - expected: FAIL - - [col.span: setAttribute() to 1000] - expected: FAIL - [col.span: setAttribute() to 1001] expected: FAIL [col.span: IDL set to 0] expected: FAIL - [col.span: IDL set to 1] - expected: FAIL - - [col.span: IDL set to 257] - expected: FAIL - [col.span: IDL set to 2147483647] expected: FAIL [col.span: IDL set to "-0"] expected: FAIL - [col.span: IDL set to 2147483648] - expected: FAIL - - [col.span: IDL set to 4294967295] - expected: FAIL - - [col.span: IDL set to 1000] - expected: FAIL - [col.span: IDL set to 1001] expected: FAIL @@ -9995,60 +9659,12 @@ [colgroup.tabIndex: setAttribute() to object "3"] expected: FAIL - [colgroup.span: setAttribute() to "-"] - expected: FAIL - - [colgroup.span: setAttribute() to "+"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\t\\v7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\n\\v7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\f\\v7"] - expected: FAIL - - [colgroup.span: setAttribute() to "\\r\\v7"] - expected: FAIL - - [colgroup.span: setAttribute() to " \\v7"] - expected: FAIL - - [colgroup.span: setAttribute() to "7\\v"] - expected: FAIL - [col.tabIndex: setAttribute() to "7\\v"] expected: FAIL [col.tabIndex: setAttribute() to object "3"] expected: FAIL - [col.span: setAttribute() to "-"] - expected: FAIL - - [col.span: setAttribute() to "+"] - expected: FAIL - - [col.span: setAttribute() to "\\t\\v7"] - expected: FAIL - - [col.span: setAttribute() to "\\n\\v7"] - expected: FAIL - - [col.span: setAttribute() to "\\f\\v7"] - expected: FAIL - - [col.span: setAttribute() to "\\r\\v7"] - expected: FAIL - - [col.span: setAttribute() to " \\v7"] - expected: FAIL - - [col.span: setAttribute() to "7\\v"] - expected: FAIL - [tbody.tabIndex: setAttribute() to "7\\v"] expected: FAIL