diff options
Diffstat (limited to 'components')
-rw-r--r-- | components/layout_2020/flow/mod.rs | 121 | ||||
-rw-r--r-- | components/layout_2020/formatting_contexts.rs | 6 | ||||
-rw-r--r-- | components/layout_2020/geom.rs | 42 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 63 | ||||
-rw-r--r-- | components/layout_2020/replaced.rs | 4 | ||||
-rw-r--r-- | components/layout_2020/table/layout.rs | 53 |
6 files changed, 146 insertions, 143 deletions
diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index 38a9356c507..1061ad8ac88 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -10,6 +10,7 @@ use inline::InlineFormattingContext; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use servo_arc::Arc; use style::computed_values::clear::T as StyleClear; +use style::logical_geometry::Direction; use style::properties::ComputedValues; use style::servo::selector_parser::PseudoElement; use style::values::computed::Size as StyleSize; @@ -167,10 +168,12 @@ impl BlockLevelBox { .sizes }; let inline_size = content_box_sizes.inline.resolve( + Direction::Inline, Size::Stretch, Au::zero(), available_inline_size, get_inline_content_sizes, + false, /* is_table */ ); let containing_block_for_children = ContainingBlock { @@ -990,10 +993,12 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context( } let block_size = block_sizes.resolve( + Direction::Block, Size::FitContent, Au::zero(), available_block_size.unwrap_or(content_block_size), || content_block_size.into(), + false, /* is_table */ ); if let Some(ref mut sequential_layout_state) = sequential_layout_state { @@ -1105,18 +1110,17 @@ impl IndependentNonReplacedContents { containing_block, ); - let (block_size, inline_size) = match layout.content_inline_size_for_table { - Some(inline_size) => (layout.content_block_size, inline_size), - None => { - let block_size = block_sizes.resolve( - Size::FitContent, - Au::zero(), - available_block_size.unwrap_or(layout.content_block_size), - || layout.content_block_size.into(), - ); - (block_size, containing_block_for_children.size.inline) - }, - }; + let inline_size = layout + .content_inline_size_for_table + .unwrap_or(containing_block_for_children.size.inline); + let block_size = block_sizes.resolve( + Direction::Block, + Size::FitContent, + Au::zero(), + available_block_size.unwrap_or(layout.content_block_size), + || layout.content_block_size.into(), + layout_style.is_table(), + ); let ResolvedMargins { margin, @@ -1236,26 +1240,31 @@ impl IndependentNonReplacedContents { }; // TODO: the automatic inline size should take `justify-self` into account. - let automatic_inline_size = if self.is_table() { + let is_table = self.is_table(); + let automatic_inline_size = if is_table { Size::FitContent } else { Size::Stretch }; let compute_inline_size = |stretch_size| { content_box_sizes.inline.resolve( + Direction::Inline, automatic_inline_size, Au::zero(), stretch_size, get_inline_content_sizes, + is_table, ) }; let compute_block_size = |layout: &IndependentLayout| { content_box_sizes.block.resolve( + Direction::Block, Size::FitContent, Au::zero(), available_block_size.unwrap_or(layout.content_block_size), || layout.content_block_size.into(), + is_table, ) }; @@ -1286,17 +1295,10 @@ impl IndependentNonReplacedContents { containing_block, ); - if let Some(inline_size) = layout.content_inline_size_for_table { - content_size = LogicalVec2 { - block: layout.content_block_size, - inline: inline_size, - }; - } else { - content_size = LogicalVec2 { - block: compute_block_size(&layout), - inline: inline_size, - }; - } + content_size = LogicalVec2 { + block: compute_block_size(&layout), + inline: layout.content_inline_size_for_table.unwrap_or(inline_size), + }; let mut placement = PlacementAmongFloats::new( &sequential_layout_state.floats, @@ -1356,29 +1358,20 @@ impl IndependentNonReplacedContents { containing_block, ); - if let Some(inline_size) = layout.content_inline_size_for_table { - // If this is a table, it's impossible to know the inline size it will take - // up until after trying to place it. If the table doesn't fit into this - // positioning rectangle due to incompatibility in the inline axis, - // then retry at another location. - // Note if we get a narrower size due to collapsed columns, we don't backtrack - // to consider areas that we thought weren't big enough. + let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table { + // This is a table that ended up being smaller than predicted because of + // collapsed columns. Note we don't backtrack to consider areas that we + // previously thought weren't big enough. // TODO: Should `minimum_size_of_block.inline` be zero for tables? - let outer_inline_size = inline_size + pbm.padding_border_sums.inline; - if outer_inline_size > placement_rect.size.inline { - positioning_context.truncate(&positioning_context_length); - continue; - } - content_size = LogicalVec2 { - block: layout.content_block_size, - inline: inline_size, - }; + debug_assert!(inline_size < proposed_inline_size); + inline_size } else { - content_size = LogicalVec2 { - block: compute_block_size(&layout), - inline: proposed_inline_size, - }; - } + proposed_inline_size + }; + content_size = LogicalVec2 { + block: compute_block_size(&layout), + inline: inline_size, + }; // Now we know the block size of this attempted layout of a box with block // size of auto. Try to fit it into our precalculated placement among the @@ -1696,16 +1689,19 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>( )) }; // TODO: the automatic inline size should take `justify-self` into account. - let automatic_inline_size = if layout_style.is_table() { + let is_table = layout_style.is_table(); + let automatic_inline_size = if is_table { Size::FitContent } else { Size::Stretch }; let inline_size = content_box_sizes.inline.resolve( + Direction::Inline, automatic_inline_size, Au::zero(), available_inline_size, get_inline_content_sizes, + is_table, ); let containing_block_for_children = ContainingBlock { @@ -2153,9 +2149,9 @@ impl IndependentFormattingContext { ) -> IndependentLayoutResult { let style = self.style(); let container_writing_mode = containing_block.style.writing_mode; - let content_box_sizes_and_pbm = self - .layout_style() - .content_box_sizes_and_padding_border_margin(&containing_block.into()); + let layout_style = self.layout_style(); + let content_box_sizes_and_pbm = + layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into()); let pbm = &content_box_sizes_and_pbm.pbm; let margin = pbm.margin.auto_is(Au::zero); let pbm_sums = pbm.padding + pbm.border + margin; @@ -2200,11 +2196,14 @@ impl IndependentFormattingContext { .sizes }; + let is_table = layout_style.is_table(); let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve( + Direction::Inline, Size::FitContent, Au::zero(), available_inline_size, get_content_size, + is_table, ); let containing_block_for_children = ContainingBlock { @@ -2226,21 +2225,17 @@ impl IndependentFormattingContext { &containing_block_for_children, containing_block, ); - let (inline_size, block_size) = match independent_layout + let inline_size = independent_layout .content_inline_size_for_table - { - Some(inline) => (inline, independent_layout.content_block_size), - None => { - // https://drafts.csswg.org/css2/visudet.html#block-root-margin - let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve( - Size::FitContent, - Au::zero(), - available_block_size.unwrap_or(independent_layout.content_block_size), - || independent_layout.content_block_size.into(), - ); - (inline_size, block_size) - }, - }; + .unwrap_or(inline_size); + let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve( + Direction::Block, + Size::FitContent, + Au::zero(), + available_block_size.unwrap_or(independent_layout.content_block_size), + || independent_layout.content_block_size.into(), + is_table, + ); let content_size = LogicalVec2 { block: block_size, diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index 1e28aa01d6c..96b78bf6eef 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -74,9 +74,9 @@ pub(crate) struct IndependentLayout { /// <https://drafts.csswg.org/css2/visudet.html#root-height> pub content_block_size: Au, - /// The contents of a table may force it to become wider than what we would expect - /// from 'width' and 'min-width'. It can also become smaller due to collapsed columns. - /// This is the resulting inline content size, or None for non-table layouts. + /// If a table has collapsed columns, it can become smaller than what the parent + /// formatting context decided. This is the resulting inline content size. + /// This is None for non-table layouts and for tables without collapsed columns. pub content_inline_size_for_table: Option<Au>, /// The offset of the last inflow baseline of this layout in the content area, if diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index eb4f625906f..0f367cc0fe5 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -8,7 +8,7 @@ use std::fmt; use std::ops::{Add, AddAssign, Neg, Sub, SubAssign}; use app_units::Au; -use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode}; +use style::logical_geometry::{BlockFlowDirection, Direction, InlineBaseDirection, WritingMode}; use style::values::computed::{ CSSPixelLength, LengthPercentage, MaxSize as StyleMaxSize, Percentage, Size as StyleSize, }; @@ -912,16 +912,20 @@ impl Sizes { #[inline] pub(crate) fn resolve( &self, + axis: Direction, automatic_size: Size<Au>, automatic_minimum_size: Au, stretch_size: Au, get_content_size: impl FnOnce() -> ContentSizes, + is_table: bool, ) -> Au { let (preferred, min, max) = self.resolve_each( + axis, automatic_size, automatic_minimum_size, stretch_size, get_content_size, + is_table, ); preferred.clamp_between_extremums(min, max) } @@ -930,22 +934,42 @@ impl Sizes { #[inline] pub(crate) fn resolve_each( &self, + axis: Direction, automatic_size: Size<Au>, automatic_minimum_size: Au, stretch_size: Au, get_content_size: impl FnOnce() -> ContentSizes, + is_table: bool, ) -> (Au, Au, Option<Au>) { // The provided `get_content_size` is a FnOnce but we may need its result multiple times. // A LazyCell will only invoke it once if needed, and then reuse the result. let content_size = LazyCell::new(get_content_size); - ( - self.preferred - .resolve(automatic_size, stretch_size, &content_size), - self.min - .resolve_non_initial(stretch_size, &content_size) - .unwrap_or(automatic_minimum_size), - self.max.resolve_non_initial(stretch_size, &content_size), - ) + + if is_table && axis == Direction::Block { + // The intrinsic block size of a table already takes sizing properties into account, + // but it can be a smaller amount if there are collapsed rows. + // Therefore, disregard sizing properties and just defer to the intrinsic size. + // This is being discussed in https://github.com/w3c/csswg-drafts/issues/11408 + return (content_size.max_content, content_size.min_content, None); + } + + let preferred = self + .preferred + .resolve(automatic_size, stretch_size, &content_size); + let mut min = self + .min + .resolve_non_initial(stretch_size, &content_size) + .unwrap_or(automatic_minimum_size); + if is_table { + // In addition to the specified minimum, the inline size of a table is forced to be + // at least as big as its min-content size. + // Note that if there are collapsed columns, only the inline size of the table grid will + // shrink, while the size of the table wrapper (being computed here) won't be affected. + // This is being discussed in https://github.com/w3c/csswg-drafts/issues/11408 + min.max_assign(content_size.min_content); + } + let max = self.max.resolve_non_initial(stretch_size, &content_size); + (preferred, min, max) } /// Tries to extrinsically resolve the three sizes into a single [`SizeConstraint`]. diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 35bec2ded8a..b94294fd7ac 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -8,9 +8,9 @@ use app_units::Au; use rayon::iter::IntoParallelRefMutIterator; use rayon::prelude::{IndexedParallelIterator, ParallelIterator}; use style::computed_values::position::T as Position; -use style::logical_geometry::WritingMode; +use style::logical_geometry::{Direction, WritingMode}; use style::properties::ComputedValues; -use style::values::specified::align::{AlignFlags, AxisDirection}; +use style::values::specified::align::AlignFlags; use style::Zero; use crate::cell::ArcRefCell; @@ -493,6 +493,7 @@ impl HoistedAbsolutelyPositionedBox { }; let mut inline_axis_solver = AbsoluteAxisSolver { + axis: Direction::Inline, containing_size: cbis, padding_border_sum: pbm.padding_border_sums.inline, computed_margin_start: pbm.margin.inline_start, @@ -500,7 +501,7 @@ impl HoistedAbsolutelyPositionedBox { computed_sizes: content_box_sizes.inline, avoid_negative_margin_start: true, box_offsets: inline_box_offsets, - static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Inline), + static_position_rect_axis: static_position_rect.get_axis(Direction::Inline), alignment: inline_alignment, flip_anchor: shared_fragment.original_parent_writing_mode.is_bidi_ltr() != containing_block_writing_mode.is_bidi_ltr(), @@ -518,6 +519,7 @@ impl HoistedAbsolutelyPositionedBox { false => shared_fragment.resolved_alignment.block, }; let mut block_axis_solver = AbsoluteAxisSolver { + axis: Direction::Block, containing_size: cbbs, padding_border_sum: pbm.padding_border_sums.block, computed_margin_start: pbm.margin.block_start, @@ -525,7 +527,7 @@ impl HoistedAbsolutelyPositionedBox { computed_sizes: content_box_sizes.block, avoid_negative_margin_start: false, box_offsets: block_box_offsets, - static_position_rect_axis: static_position_rect.get_axis(AxisDirection::Block), + static_position_rect_axis: static_position_rect.get_axis(Direction::Block), alignment: block_alignment, flip_anchor: false, is_table, @@ -621,29 +623,22 @@ impl HoistedAbsolutelyPositionedBox { containing_block, ); - let (block_size, inline_size) = - match independent_layout.content_inline_size_for_table { - Some(table_inline_size) => { - // Tables can override their sizes regardless of the sizing properties, - // so we may need to solve again to update margins. - if inline_size != table_inline_size { - inline_axis_solver.override_size(table_inline_size); - inline_axis = inline_axis_solver.solve_tentatively(); - } - let table_block_size = independent_layout.content_block_size; - if block_axis.size != SizeConstraint::Definite(table_block_size) { - block_axis_solver.override_size(table_block_size); - block_axis = block_axis_solver.solve_tentatively(); - } - (table_block_size, table_inline_size) - }, - None => { - // Now we can properly solve the block size. - block_axis = block_axis_solver - .solve(Some(|| independent_layout.content_block_size.into())); - (block_axis.size.to_definite().unwrap(), inline_size) - }, - }; + let inline_size = if let Some(inline_size) = + independent_layout.content_inline_size_for_table + { + // Tables can become narrower than predicted due to collapsed columns, + // so we need to solve again to update margins. + inline_axis_solver.override_size(inline_size); + inline_axis = inline_axis_solver.solve_tentatively(); + inline_size + } else { + inline_size + }; + + // Now we can properly solve the block size. + block_axis = block_axis_solver + .solve(Some(|| independent_layout.content_block_size.into())); + let block_size = block_axis.size.to_definite().unwrap(); content_size = LogicalVec2 { inline: inline_size, @@ -664,14 +659,12 @@ impl HoistedAbsolutelyPositionedBox { let pb = pbm.padding + pbm.border; let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum(); let inline_origin = inline_axis_solver.origin_for_margin_box( - AxisDirection::Inline, margin_rect_size.inline, style.writing_mode, shared_fragment.original_parent_writing_mode, containing_block_writing_mode, ); let block_origin = block_axis_solver.origin_for_margin_box( - AxisDirection::Block, margin_rect_size.block, style.writing_mode, shared_fragment.original_parent_writing_mode, @@ -726,13 +719,13 @@ struct RectAxis { } impl LogicalRect<Au> { - fn get_axis(&self, axis: AxisDirection) -> RectAxis { + fn get_axis(&self, axis: Direction) -> RectAxis { match axis { - AxisDirection::Block => RectAxis { + Direction::Block => RectAxis { origin: self.start_corner.block, length: self.size.block, }, - AxisDirection::Inline => RectAxis { + Direction::Inline => RectAxis { origin: self.start_corner.inline, length: self.size.inline, }, @@ -769,6 +762,7 @@ struct AxisResult { } struct AbsoluteAxisSolver<'a> { + axis: Direction, containing_size: Au, padding_border_sum: Au, computed_margin_start: AuOrAuto, @@ -825,10 +819,12 @@ impl AbsoluteAxisSolver<'_> { let stretch_size = stretch_size.max(Au::zero()); if let Some(get_content_size) = get_content_size { SizeConstraint::Definite(self.computed_sizes.resolve( + self.axis, initial_behavior, Au::zero(), stretch_size, get_content_size, + self.is_table, )) } else { self.computed_sizes.resolve_extrinsic( @@ -906,7 +902,6 @@ impl AbsoluteAxisSolver<'_> { fn origin_for_margin_box( &self, - axis: AxisDirection, size: Au, self_writing_mode: WritingMode, original_parent_writing_mode: WritingMode, @@ -958,7 +953,7 @@ impl AbsoluteAxisSolver<'_> { "Mixed horizontal and vertical writing modes are not supported yet" ); let self_value_matches_container = || { - axis == AxisDirection::Block || + self.axis == Direction::Block || self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr() }; diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 8739428758d..b00633d6a13 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -533,10 +533,12 @@ impl ReplacedContents { .into() }; let (preferred_inline, min_inline, max_inline) = sizes.inline.resolve_each( + Direction::Inline, automatic_size.inline, Au::zero(), inline_stretch_size, get_inline_content_size, + false, /* is_table */ ); let inline_size = preferred_inline.clamp_between_extremums(min_inline, max_inline); @@ -560,10 +562,12 @@ impl ReplacedContents { .into() }); let block_size = sizes.block.resolve( + Direction::Block, automatic_size.block, Au::zero(), block_stretch_size.unwrap_or_else(|| block_content_size.max_content), || *block_content_size, + false, /* is_table */ ); LogicalVec2 { diff --git a/components/layout_2020/table/layout.rs b/components/layout_2020/table/layout.rs index ed54370abd8..8fa78b8a891 100644 --- a/components/layout_2020/table/layout.rs +++ b/components/layout_2020/table/layout.rs @@ -630,16 +630,14 @@ impl<'a> TableLayout<'a> { } } - /// Compute the GRIDMIN and GRIDMAX. - fn compute_grid_min_max( - &mut self, - layout_context: &LayoutContext, - writing_mode: WritingMode, - ) -> ContentSizes { + fn compute_measures(&mut self, layout_context: &LayoutContext, writing_mode: WritingMode) { self.compute_track_constrainedness_and_has_originating_cells(writing_mode); self.compute_cell_measures(layout_context, writing_mode); self.compute_column_measures(writing_mode); + } + /// Compute the GRIDMIN and GRIDMAX. + fn compute_grid_min_max(&self) -> ContentSizes { // https://drafts.csswg.org/css-tables/#gridmin: // > The row/column-grid width minimum (GRIDMIN) width is the sum of the min-content width of // > all the columns plus cell spacing or borders. @@ -731,25 +729,12 @@ impl<'a> TableLayout<'a> { .unwrap_or_default() } - fn compute_table_width( - &mut self, - containing_block_for_children: &ContainingBlock, - grid_min_max: ContentSizes, - caption_minimum_inline_size: Au, - ) { - // These diverge a little from the specification, but should be roughtly equivalent - // to what the spec calls "resolved-table-width" and "used width of a table". - // https://drafts.csswg.org/css-tables/#resolved-table-width + fn compute_table_width(&mut self, containing_block_for_children: &ContainingBlock) { + // This assumes that the parent formatting context computed the correct inline size + // of the table, by enforcing its min-content size as a minimum. + // This should be roughly equivalent to what the spec calls "used width of a table". // https://drafts.csswg.org/css-tables/#used-width-of-table - let resolved_table_width = containing_block_for_children.size.inline; - let used_width_of_table = resolved_table_width.max(grid_min_max.min_content); - - // Padding and border should apply to the table grid, but they are properties of the - // parent element (the table wrapper). In order to account for this, we subtract the - // border and padding inline size from the caption size. - let caption_minimum_inline_size = - caption_minimum_inline_size - self.pbm.padding_border_sums.inline; - self.table_width = used_width_of_table.max(caption_minimum_inline_size); + self.table_width = containing_block_for_children.size.inline; // > The assignable table width is the used width of the table minus the total horizontal // > border spacing (if any). This is the width that we will be able to allocate to the @@ -759,7 +744,7 @@ impl<'a> TableLayout<'a> { // This is the amount that we will use to resolve percentages in the padding of cells. // It matches what Gecko and Blink do, though they disagree when there is a big caption. self.basis_for_cell_padding_percentage = - used_width_of_table - self.table.border_spacing().inline * 2; + self.table_width - self.table.border_spacing().inline * 2; } /// Distribute width to columns, performing step 2.4 of table layout from @@ -1568,13 +1553,8 @@ impl<'a> TableLayout<'a> { table_writing_mode, containing_block_for_table.size.inline, ); - let grid_min_max = self.compute_grid_min_max(layout_context, table_writing_mode); - let caption_minimum_inline_size = self.compute_caption_minimum_inline_size(layout_context); - self.compute_table_width( - containing_block_for_children, - grid_min_max, - caption_minimum_inline_size, - ); + self.compute_measures(layout_context, table_writing_mode); + self.compute_table_width(containing_block_for_children); // The table wrapper is the one that has the CSS properties for the grid's border and padding. This // weirdness is difficult to express in Servo's layout system. We have the wrapper size itself as if @@ -1698,7 +1678,11 @@ impl<'a> TableLayout<'a> { .size .to_logical(table_writing_mode) .block; - table_layout.content_inline_size_for_table = Some(logical_grid_content_rect.size.inline); + if logical_grid_content_rect.size.inline < self.table_width { + // This can happen when collapsing columns + table_layout.content_inline_size_for_table = + Some(logical_grid_content_rect.size.inline); + } let grid_fragment = Fragment::Box(ArcRefCell::new(grid_fragment)); positioning_context.adjust_static_position_of_hoisted_fragments( @@ -2716,7 +2700,8 @@ impl ComputeInlineContentSizes for Table { writing_mode, Au::zero(), ); - let mut table_content_sizes = layout.compute_grid_min_max(layout_context, writing_mode); + layout.compute_measures(layout_context, writing_mode); + let mut table_content_sizes = layout.compute_grid_min_max(); let mut caption_minimum_inline_size = layout.compute_caption_minimum_inline_size(layout_context); |