mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
layout: Limit content_inline_size_for_table
override to collapsed columns (#35209)
A box is usually sized by the formatting context in which it participates. However, tables have some special sizing behaviors that we implemented with a `content_inline_size_for_table` override. However, breaking the assumptions of the formatting context isn't great. It was also bad for performance that we could try to layout a table among floats even though it wouldn't en up fitting because of a larger min-content size. Therefore, this changes the logic so that formatting contexts use some special sizing for tables, and then tables only override that amount when there are collapsed columns. Eventually, we should try to remove that case too, see https://github.com/w3c/csswg-drafts/issues/11408 Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
88d01f6303
commit
e2bb772669
6 changed files with 146 additions and 143 deletions
|
@ -10,6 +10,7 @@ use inline::InlineFormattingContext;
|
||||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
use style::computed_values::clear::T as StyleClear;
|
use style::computed_values::clear::T as StyleClear;
|
||||||
|
use style::logical_geometry::Direction;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::servo::selector_parser::PseudoElement;
|
use style::servo::selector_parser::PseudoElement;
|
||||||
use style::values::computed::Size as StyleSize;
|
use style::values::computed::Size as StyleSize;
|
||||||
|
@ -167,10 +168,12 @@ impl BlockLevelBox {
|
||||||
.sizes
|
.sizes
|
||||||
};
|
};
|
||||||
let inline_size = content_box_sizes.inline.resolve(
|
let inline_size = content_box_sizes.inline.resolve(
|
||||||
|
Direction::Inline,
|
||||||
Size::Stretch,
|
Size::Stretch,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
available_inline_size,
|
available_inline_size,
|
||||||
get_inline_content_sizes,
|
get_inline_content_sizes,
|
||||||
|
false, /* is_table */
|
||||||
);
|
);
|
||||||
|
|
||||||
let containing_block_for_children = ContainingBlock {
|
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(
|
let block_size = block_sizes.resolve(
|
||||||
|
Direction::Block,
|
||||||
Size::FitContent,
|
Size::FitContent,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
available_block_size.unwrap_or(content_block_size),
|
available_block_size.unwrap_or(content_block_size),
|
||||||
|| content_block_size.into(),
|
|| content_block_size.into(),
|
||||||
|
false, /* is_table */
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
||||||
|
@ -1105,18 +1110,17 @@ impl IndependentNonReplacedContents {
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (block_size, inline_size) = match layout.content_inline_size_for_table {
|
let inline_size = layout
|
||||||
Some(inline_size) => (layout.content_block_size, inline_size),
|
.content_inline_size_for_table
|
||||||
None => {
|
.unwrap_or(containing_block_for_children.size.inline);
|
||||||
let block_size = block_sizes.resolve(
|
let block_size = block_sizes.resolve(
|
||||||
Size::FitContent,
|
Direction::Block,
|
||||||
Au::zero(),
|
Size::FitContent,
|
||||||
available_block_size.unwrap_or(layout.content_block_size),
|
Au::zero(),
|
||||||
|| layout.content_block_size.into(),
|
available_block_size.unwrap_or(layout.content_block_size),
|
||||||
);
|
|| layout.content_block_size.into(),
|
||||||
(block_size, containing_block_for_children.size.inline)
|
layout_style.is_table(),
|
||||||
},
|
);
|
||||||
};
|
|
||||||
|
|
||||||
let ResolvedMargins {
|
let ResolvedMargins {
|
||||||
margin,
|
margin,
|
||||||
|
@ -1236,26 +1240,31 @@ impl IndependentNonReplacedContents {
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: the automatic inline size should take `justify-self` into account.
|
// 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
|
Size::FitContent
|
||||||
} else {
|
} else {
|
||||||
Size::Stretch
|
Size::Stretch
|
||||||
};
|
};
|
||||||
let compute_inline_size = |stretch_size| {
|
let compute_inline_size = |stretch_size| {
|
||||||
content_box_sizes.inline.resolve(
|
content_box_sizes.inline.resolve(
|
||||||
|
Direction::Inline,
|
||||||
automatic_inline_size,
|
automatic_inline_size,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
stretch_size,
|
stretch_size,
|
||||||
get_inline_content_sizes,
|
get_inline_content_sizes,
|
||||||
|
is_table,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let compute_block_size = |layout: &IndependentLayout| {
|
let compute_block_size = |layout: &IndependentLayout| {
|
||||||
content_box_sizes.block.resolve(
|
content_box_sizes.block.resolve(
|
||||||
|
Direction::Block,
|
||||||
Size::FitContent,
|
Size::FitContent,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
available_block_size.unwrap_or(layout.content_block_size),
|
available_block_size.unwrap_or(layout.content_block_size),
|
||||||
|| layout.content_block_size.into(),
|
|| layout.content_block_size.into(),
|
||||||
|
is_table,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1286,17 +1295,10 @@ impl IndependentNonReplacedContents {
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(inline_size) = layout.content_inline_size_for_table {
|
content_size = LogicalVec2 {
|
||||||
content_size = LogicalVec2 {
|
block: compute_block_size(&layout),
|
||||||
block: layout.content_block_size,
|
inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
|
||||||
inline: inline_size,
|
};
|
||||||
};
|
|
||||||
} else {
|
|
||||||
content_size = LogicalVec2 {
|
|
||||||
block: compute_block_size(&layout),
|
|
||||||
inline: inline_size,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut placement = PlacementAmongFloats::new(
|
let mut placement = PlacementAmongFloats::new(
|
||||||
&sequential_layout_state.floats,
|
&sequential_layout_state.floats,
|
||||||
|
@ -1356,29 +1358,20 @@ impl IndependentNonReplacedContents {
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(inline_size) = layout.content_inline_size_for_table {
|
let inline_size = 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
|
// This is a table that ended up being smaller than predicted because of
|
||||||
// up until after trying to place it. If the table doesn't fit into this
|
// collapsed columns. Note we don't backtrack to consider areas that we
|
||||||
// positioning rectangle due to incompatibility in the inline axis,
|
// previously thought weren't big enough.
|
||||||
// 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.
|
|
||||||
// TODO: Should `minimum_size_of_block.inline` be zero for tables?
|
// TODO: Should `minimum_size_of_block.inline` be zero for tables?
|
||||||
let outer_inline_size = inline_size + pbm.padding_border_sums.inline;
|
debug_assert!(inline_size < proposed_inline_size);
|
||||||
if outer_inline_size > placement_rect.size.inline {
|
inline_size
|
||||||
positioning_context.truncate(&positioning_context_length);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
content_size = LogicalVec2 {
|
|
||||||
block: layout.content_block_size,
|
|
||||||
inline: inline_size,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
content_size = LogicalVec2 {
|
proposed_inline_size
|
||||||
block: compute_block_size(&layout),
|
};
|
||||||
inline: 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
|
// 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
|
// 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.
|
// 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
|
Size::FitContent
|
||||||
} else {
|
} else {
|
||||||
Size::Stretch
|
Size::Stretch
|
||||||
};
|
};
|
||||||
let inline_size = content_box_sizes.inline.resolve(
|
let inline_size = content_box_sizes.inline.resolve(
|
||||||
|
Direction::Inline,
|
||||||
automatic_inline_size,
|
automatic_inline_size,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
available_inline_size,
|
available_inline_size,
|
||||||
get_inline_content_sizes,
|
get_inline_content_sizes,
|
||||||
|
is_table,
|
||||||
);
|
);
|
||||||
|
|
||||||
let containing_block_for_children = ContainingBlock {
|
let containing_block_for_children = ContainingBlock {
|
||||||
|
@ -2153,9 +2149,9 @@ impl IndependentFormattingContext {
|
||||||
) -> IndependentLayoutResult {
|
) -> IndependentLayoutResult {
|
||||||
let style = self.style();
|
let style = self.style();
|
||||||
let container_writing_mode = containing_block.style.writing_mode;
|
let container_writing_mode = containing_block.style.writing_mode;
|
||||||
let content_box_sizes_and_pbm = self
|
let layout_style = self.layout_style();
|
||||||
.layout_style()
|
let content_box_sizes_and_pbm =
|
||||||
.content_box_sizes_and_padding_border_margin(&containing_block.into());
|
layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
|
||||||
let pbm = &content_box_sizes_and_pbm.pbm;
|
let pbm = &content_box_sizes_and_pbm.pbm;
|
||||||
let margin = pbm.margin.auto_is(Au::zero);
|
let margin = pbm.margin.auto_is(Au::zero);
|
||||||
let pbm_sums = pbm.padding + pbm.border + margin;
|
let pbm_sums = pbm.padding + pbm.border + margin;
|
||||||
|
@ -2200,11 +2196,14 @@ impl IndependentFormattingContext {
|
||||||
.sizes
|
.sizes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let is_table = layout_style.is_table();
|
||||||
let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
|
let inline_size = content_box_sizes_and_pbm.content_box_sizes.inline.resolve(
|
||||||
|
Direction::Inline,
|
||||||
Size::FitContent,
|
Size::FitContent,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
available_inline_size,
|
available_inline_size,
|
||||||
get_content_size,
|
get_content_size,
|
||||||
|
is_table,
|
||||||
);
|
);
|
||||||
|
|
||||||
let containing_block_for_children = ContainingBlock {
|
let containing_block_for_children = ContainingBlock {
|
||||||
|
@ -2226,21 +2225,17 @@ impl IndependentFormattingContext {
|
||||||
&containing_block_for_children,
|
&containing_block_for_children,
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
let (inline_size, block_size) = match independent_layout
|
let inline_size = independent_layout
|
||||||
.content_inline_size_for_table
|
.content_inline_size_for_table
|
||||||
{
|
.unwrap_or(inline_size);
|
||||||
Some(inline) => (inline, independent_layout.content_block_size),
|
let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve(
|
||||||
None => {
|
Direction::Block,
|
||||||
// https://drafts.csswg.org/css2/visudet.html#block-root-margin
|
Size::FitContent,
|
||||||
let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve(
|
Au::zero(),
|
||||||
Size::FitContent,
|
available_block_size.unwrap_or(independent_layout.content_block_size),
|
||||||
Au::zero(),
|
|| independent_layout.content_block_size.into(),
|
||||||
available_block_size.unwrap_or(independent_layout.content_block_size),
|
is_table,
|
||||||
|| independent_layout.content_block_size.into(),
|
);
|
||||||
);
|
|
||||||
(inline_size, block_size)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
let content_size = LogicalVec2 {
|
let content_size = LogicalVec2 {
|
||||||
block: block_size,
|
block: block_size,
|
||||||
|
|
|
@ -74,9 +74,9 @@ pub(crate) struct IndependentLayout {
|
||||||
/// <https://drafts.csswg.org/css2/visudet.html#root-height>
|
/// <https://drafts.csswg.org/css2/visudet.html#root-height>
|
||||||
pub content_block_size: Au,
|
pub content_block_size: Au,
|
||||||
|
|
||||||
/// The contents of a table may force it to become wider than what we would expect
|
/// If a table has collapsed columns, it can become smaller than what the parent
|
||||||
/// from 'width' and 'min-width'. It can also become smaller due to collapsed columns.
|
/// formatting context decided. This is the resulting inline content size.
|
||||||
/// This is the resulting inline content size, or None for non-table layouts.
|
/// This is None for non-table layouts and for tables without collapsed columns.
|
||||||
pub content_inline_size_for_table: Option<Au>,
|
pub content_inline_size_for_table: Option<Au>,
|
||||||
|
|
||||||
/// The offset of the last inflow baseline of this layout in the content area, if
|
/// The offset of the last inflow baseline of this layout in the content area, if
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::fmt;
|
||||||
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
|
use style::logical_geometry::{BlockFlowDirection, Direction, InlineBaseDirection, WritingMode};
|
||||||
use style::values::computed::{
|
use style::values::computed::{
|
||||||
CSSPixelLength, LengthPercentage, MaxSize as StyleMaxSize, Percentage, Size as StyleSize,
|
CSSPixelLength, LengthPercentage, MaxSize as StyleMaxSize, Percentage, Size as StyleSize,
|
||||||
};
|
};
|
||||||
|
@ -912,16 +912,20 @@ impl Sizes {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn resolve(
|
pub(crate) fn resolve(
|
||||||
&self,
|
&self,
|
||||||
|
axis: Direction,
|
||||||
automatic_size: Size<Au>,
|
automatic_size: Size<Au>,
|
||||||
automatic_minimum_size: Au,
|
automatic_minimum_size: Au,
|
||||||
stretch_size: Au,
|
stretch_size: Au,
|
||||||
get_content_size: impl FnOnce() -> ContentSizes,
|
get_content_size: impl FnOnce() -> ContentSizes,
|
||||||
|
is_table: bool,
|
||||||
) -> Au {
|
) -> Au {
|
||||||
let (preferred, min, max) = self.resolve_each(
|
let (preferred, min, max) = self.resolve_each(
|
||||||
|
axis,
|
||||||
automatic_size,
|
automatic_size,
|
||||||
automatic_minimum_size,
|
automatic_minimum_size,
|
||||||
stretch_size,
|
stretch_size,
|
||||||
get_content_size,
|
get_content_size,
|
||||||
|
is_table,
|
||||||
);
|
);
|
||||||
preferred.clamp_between_extremums(min, max)
|
preferred.clamp_between_extremums(min, max)
|
||||||
}
|
}
|
||||||
|
@ -930,22 +934,42 @@ impl Sizes {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn resolve_each(
|
pub(crate) fn resolve_each(
|
||||||
&self,
|
&self,
|
||||||
|
axis: Direction,
|
||||||
automatic_size: Size<Au>,
|
automatic_size: Size<Au>,
|
||||||
automatic_minimum_size: Au,
|
automatic_minimum_size: Au,
|
||||||
stretch_size: Au,
|
stretch_size: Au,
|
||||||
get_content_size: impl FnOnce() -> ContentSizes,
|
get_content_size: impl FnOnce() -> ContentSizes,
|
||||||
|
is_table: bool,
|
||||||
) -> (Au, Au, Option<Au>) {
|
) -> (Au, Au, Option<Au>) {
|
||||||
// The provided `get_content_size` is a FnOnce but we may need its result multiple times.
|
// 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.
|
// A LazyCell will only invoke it once if needed, and then reuse the result.
|
||||||
let content_size = LazyCell::new(get_content_size);
|
let content_size = LazyCell::new(get_content_size);
|
||||||
(
|
|
||||||
self.preferred
|
if is_table && axis == Direction::Block {
|
||||||
.resolve(automatic_size, stretch_size, &content_size),
|
// The intrinsic block size of a table already takes sizing properties into account,
|
||||||
self.min
|
// but it can be a smaller amount if there are collapsed rows.
|
||||||
.resolve_non_initial(stretch_size, &content_size)
|
// Therefore, disregard sizing properties and just defer to the intrinsic size.
|
||||||
.unwrap_or(automatic_minimum_size),
|
// This is being discussed in https://github.com/w3c/csswg-drafts/issues/11408
|
||||||
self.max.resolve_non_initial(stretch_size, &content_size),
|
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`].
|
/// Tries to extrinsically resolve the three sizes into a single [`SizeConstraint`].
|
||||||
|
|
|
@ -8,9 +8,9 @@ use app_units::Au;
|
||||||
use rayon::iter::IntoParallelRefMutIterator;
|
use rayon::iter::IntoParallelRefMutIterator;
|
||||||
use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
|
use rayon::prelude::{IndexedParallelIterator, ParallelIterator};
|
||||||
use style::computed_values::position::T as Position;
|
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::properties::ComputedValues;
|
||||||
use style::values::specified::align::{AlignFlags, AxisDirection};
|
use style::values::specified::align::AlignFlags;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
|
@ -493,6 +493,7 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut inline_axis_solver = AbsoluteAxisSolver {
|
let mut inline_axis_solver = AbsoluteAxisSolver {
|
||||||
|
axis: Direction::Inline,
|
||||||
containing_size: cbis,
|
containing_size: cbis,
|
||||||
padding_border_sum: pbm.padding_border_sums.inline,
|
padding_border_sum: pbm.padding_border_sums.inline,
|
||||||
computed_margin_start: pbm.margin.inline_start,
|
computed_margin_start: pbm.margin.inline_start,
|
||||||
|
@ -500,7 +501,7 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
computed_sizes: content_box_sizes.inline,
|
computed_sizes: content_box_sizes.inline,
|
||||||
avoid_negative_margin_start: true,
|
avoid_negative_margin_start: true,
|
||||||
box_offsets: inline_box_offsets,
|
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,
|
alignment: inline_alignment,
|
||||||
flip_anchor: shared_fragment.original_parent_writing_mode.is_bidi_ltr() !=
|
flip_anchor: shared_fragment.original_parent_writing_mode.is_bidi_ltr() !=
|
||||||
containing_block_writing_mode.is_bidi_ltr(),
|
containing_block_writing_mode.is_bidi_ltr(),
|
||||||
|
@ -518,6 +519,7 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
false => shared_fragment.resolved_alignment.block,
|
false => shared_fragment.resolved_alignment.block,
|
||||||
};
|
};
|
||||||
let mut block_axis_solver = AbsoluteAxisSolver {
|
let mut block_axis_solver = AbsoluteAxisSolver {
|
||||||
|
axis: Direction::Block,
|
||||||
containing_size: cbbs,
|
containing_size: cbbs,
|
||||||
padding_border_sum: pbm.padding_border_sums.block,
|
padding_border_sum: pbm.padding_border_sums.block,
|
||||||
computed_margin_start: pbm.margin.block_start,
|
computed_margin_start: pbm.margin.block_start,
|
||||||
|
@ -525,7 +527,7 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
computed_sizes: content_box_sizes.block,
|
computed_sizes: content_box_sizes.block,
|
||||||
avoid_negative_margin_start: false,
|
avoid_negative_margin_start: false,
|
||||||
box_offsets: block_box_offsets,
|
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,
|
alignment: block_alignment,
|
||||||
flip_anchor: false,
|
flip_anchor: false,
|
||||||
is_table,
|
is_table,
|
||||||
|
@ -621,29 +623,22 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
containing_block,
|
containing_block,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (block_size, inline_size) =
|
let inline_size = if let Some(inline_size) =
|
||||||
match independent_layout.content_inline_size_for_table {
|
independent_layout.content_inline_size_for_table
|
||||||
Some(table_inline_size) => {
|
{
|
||||||
// Tables can override their sizes regardless of the sizing properties,
|
// Tables can become narrower than predicted due to collapsed columns,
|
||||||
// so we may need to solve again to update margins.
|
// so we need to solve again to update margins.
|
||||||
if inline_size != table_inline_size {
|
inline_axis_solver.override_size(inline_size);
|
||||||
inline_axis_solver.override_size(table_inline_size);
|
inline_axis = inline_axis_solver.solve_tentatively();
|
||||||
inline_axis = inline_axis_solver.solve_tentatively();
|
inline_size
|
||||||
}
|
} else {
|
||||||
let table_block_size = independent_layout.content_block_size;
|
inline_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();
|
// Now we can properly solve the block size.
|
||||||
}
|
block_axis = block_axis_solver
|
||||||
(table_block_size, table_inline_size)
|
.solve(Some(|| independent_layout.content_block_size.into()));
|
||||||
},
|
let block_size = block_axis.size.to_definite().unwrap();
|
||||||
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)
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
content_size = LogicalVec2 {
|
content_size = LogicalVec2 {
|
||||||
inline: inline_size,
|
inline: inline_size,
|
||||||
|
@ -664,14 +659,12 @@ impl HoistedAbsolutelyPositionedBox {
|
||||||
let pb = pbm.padding + pbm.border;
|
let pb = pbm.padding + pbm.border;
|
||||||
let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
|
let margin_rect_size = content_size + pbm.padding_border_sums + margin.sum();
|
||||||
let inline_origin = inline_axis_solver.origin_for_margin_box(
|
let inline_origin = inline_axis_solver.origin_for_margin_box(
|
||||||
AxisDirection::Inline,
|
|
||||||
margin_rect_size.inline,
|
margin_rect_size.inline,
|
||||||
style.writing_mode,
|
style.writing_mode,
|
||||||
shared_fragment.original_parent_writing_mode,
|
shared_fragment.original_parent_writing_mode,
|
||||||
containing_block_writing_mode,
|
containing_block_writing_mode,
|
||||||
);
|
);
|
||||||
let block_origin = block_axis_solver.origin_for_margin_box(
|
let block_origin = block_axis_solver.origin_for_margin_box(
|
||||||
AxisDirection::Block,
|
|
||||||
margin_rect_size.block,
|
margin_rect_size.block,
|
||||||
style.writing_mode,
|
style.writing_mode,
|
||||||
shared_fragment.original_parent_writing_mode,
|
shared_fragment.original_parent_writing_mode,
|
||||||
|
@ -726,13 +719,13 @@ struct RectAxis {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogicalRect<Au> {
|
impl LogicalRect<Au> {
|
||||||
fn get_axis(&self, axis: AxisDirection) -> RectAxis {
|
fn get_axis(&self, axis: Direction) -> RectAxis {
|
||||||
match axis {
|
match axis {
|
||||||
AxisDirection::Block => RectAxis {
|
Direction::Block => RectAxis {
|
||||||
origin: self.start_corner.block,
|
origin: self.start_corner.block,
|
||||||
length: self.size.block,
|
length: self.size.block,
|
||||||
},
|
},
|
||||||
AxisDirection::Inline => RectAxis {
|
Direction::Inline => RectAxis {
|
||||||
origin: self.start_corner.inline,
|
origin: self.start_corner.inline,
|
||||||
length: self.size.inline,
|
length: self.size.inline,
|
||||||
},
|
},
|
||||||
|
@ -769,6 +762,7 @@ struct AxisResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AbsoluteAxisSolver<'a> {
|
struct AbsoluteAxisSolver<'a> {
|
||||||
|
axis: Direction,
|
||||||
containing_size: Au,
|
containing_size: Au,
|
||||||
padding_border_sum: Au,
|
padding_border_sum: Au,
|
||||||
computed_margin_start: AuOrAuto,
|
computed_margin_start: AuOrAuto,
|
||||||
|
@ -825,10 +819,12 @@ impl AbsoluteAxisSolver<'_> {
|
||||||
let stretch_size = stretch_size.max(Au::zero());
|
let stretch_size = stretch_size.max(Au::zero());
|
||||||
if let Some(get_content_size) = get_content_size {
|
if let Some(get_content_size) = get_content_size {
|
||||||
SizeConstraint::Definite(self.computed_sizes.resolve(
|
SizeConstraint::Definite(self.computed_sizes.resolve(
|
||||||
|
self.axis,
|
||||||
initial_behavior,
|
initial_behavior,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
stretch_size,
|
stretch_size,
|
||||||
get_content_size,
|
get_content_size,
|
||||||
|
self.is_table,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
self.computed_sizes.resolve_extrinsic(
|
self.computed_sizes.resolve_extrinsic(
|
||||||
|
@ -906,7 +902,6 @@ impl AbsoluteAxisSolver<'_> {
|
||||||
|
|
||||||
fn origin_for_margin_box(
|
fn origin_for_margin_box(
|
||||||
&self,
|
&self,
|
||||||
axis: AxisDirection,
|
|
||||||
size: Au,
|
size: Au,
|
||||||
self_writing_mode: WritingMode,
|
self_writing_mode: WritingMode,
|
||||||
original_parent_writing_mode: WritingMode,
|
original_parent_writing_mode: WritingMode,
|
||||||
|
@ -958,7 +953,7 @@ impl AbsoluteAxisSolver<'_> {
|
||||||
"Mixed horizontal and vertical writing modes are not supported yet"
|
"Mixed horizontal and vertical writing modes are not supported yet"
|
||||||
);
|
);
|
||||||
let self_value_matches_container = || {
|
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()
|
self_writing_mode.is_bidi_ltr() == alignment_container_writing_mode.is_bidi_ltr()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -533,10 +533,12 @@ impl ReplacedContents {
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
let (preferred_inline, min_inline, max_inline) = sizes.inline.resolve_each(
|
let (preferred_inline, min_inline, max_inline) = sizes.inline.resolve_each(
|
||||||
|
Direction::Inline,
|
||||||
automatic_size.inline,
|
automatic_size.inline,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
inline_stretch_size,
|
inline_stretch_size,
|
||||||
get_inline_content_size,
|
get_inline_content_size,
|
||||||
|
false, /* is_table */
|
||||||
);
|
);
|
||||||
let inline_size = preferred_inline.clamp_between_extremums(min_inline, max_inline);
|
let inline_size = preferred_inline.clamp_between_extremums(min_inline, max_inline);
|
||||||
|
|
||||||
|
@ -560,10 +562,12 @@ impl ReplacedContents {
|
||||||
.into()
|
.into()
|
||||||
});
|
});
|
||||||
let block_size = sizes.block.resolve(
|
let block_size = sizes.block.resolve(
|
||||||
|
Direction::Block,
|
||||||
automatic_size.block,
|
automatic_size.block,
|
||||||
Au::zero(),
|
Au::zero(),
|
||||||
block_stretch_size.unwrap_or_else(|| block_content_size.max_content),
|
block_stretch_size.unwrap_or_else(|| block_content_size.max_content),
|
||||||
|| *block_content_size,
|
|| *block_content_size,
|
||||||
|
false, /* is_table */
|
||||||
);
|
);
|
||||||
|
|
||||||
LogicalVec2 {
|
LogicalVec2 {
|
||||||
|
|
|
@ -630,16 +630,14 @@ impl<'a> TableLayout<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the GRIDMIN and GRIDMAX.
|
fn compute_measures(&mut self, layout_context: &LayoutContext, writing_mode: WritingMode) {
|
||||||
fn compute_grid_min_max(
|
|
||||||
&mut self,
|
|
||||||
layout_context: &LayoutContext,
|
|
||||||
writing_mode: WritingMode,
|
|
||||||
) -> ContentSizes {
|
|
||||||
self.compute_track_constrainedness_and_has_originating_cells(writing_mode);
|
self.compute_track_constrainedness_and_has_originating_cells(writing_mode);
|
||||||
self.compute_cell_measures(layout_context, writing_mode);
|
self.compute_cell_measures(layout_context, writing_mode);
|
||||||
self.compute_column_measures(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:
|
// https://drafts.csswg.org/css-tables/#gridmin:
|
||||||
// > The row/column-grid width minimum (GRIDMIN) width is the sum of the min-content width of
|
// > 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.
|
// > all the columns plus cell spacing or borders.
|
||||||
|
@ -731,25 +729,12 @@ impl<'a> TableLayout<'a> {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_table_width(
|
fn compute_table_width(&mut self, containing_block_for_children: &ContainingBlock) {
|
||||||
&mut self,
|
// This assumes that the parent formatting context computed the correct inline size
|
||||||
containing_block_for_children: &ContainingBlock,
|
// of the table, by enforcing its min-content size as a minimum.
|
||||||
grid_min_max: ContentSizes,
|
// This should be roughly equivalent to what the spec calls "used width of a table".
|
||||||
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
|
|
||||||
// https://drafts.csswg.org/css-tables/#used-width-of-table
|
// https://drafts.csswg.org/css-tables/#used-width-of-table
|
||||||
let resolved_table_width = containing_block_for_children.size.inline;
|
self.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);
|
|
||||||
|
|
||||||
// > The assignable table width is the used width of the table minus the total horizontal
|
// > 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
|
// > 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.
|
// 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.
|
// It matches what Gecko and Blink do, though they disagree when there is a big caption.
|
||||||
self.basis_for_cell_padding_percentage =
|
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
|
/// Distribute width to columns, performing step 2.4 of table layout from
|
||||||
|
@ -1568,13 +1553,8 @@ impl<'a> TableLayout<'a> {
|
||||||
table_writing_mode,
|
table_writing_mode,
|
||||||
containing_block_for_table.size.inline,
|
containing_block_for_table.size.inline,
|
||||||
);
|
);
|
||||||
let grid_min_max = self.compute_grid_min_max(layout_context, table_writing_mode);
|
self.compute_measures(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);
|
||||||
self.compute_table_width(
|
|
||||||
containing_block_for_children,
|
|
||||||
grid_min_max,
|
|
||||||
caption_minimum_inline_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
// The table wrapper is the one that has the CSS properties for the grid's border and padding. This
|
// 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
|
// 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
|
.size
|
||||||
.to_logical(table_writing_mode)
|
.to_logical(table_writing_mode)
|
||||||
.block;
|
.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));
|
let grid_fragment = Fragment::Box(ArcRefCell::new(grid_fragment));
|
||||||
positioning_context.adjust_static_position_of_hoisted_fragments(
|
positioning_context.adjust_static_position_of_hoisted_fragments(
|
||||||
|
@ -2716,7 +2700,8 @@ impl ComputeInlineContentSizes for Table {
|
||||||
writing_mode,
|
writing_mode,
|
||||||
Au::zero(),
|
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 =
|
let mut caption_minimum_inline_size =
|
||||||
layout.compute_caption_minimum_inline_size(layout_context);
|
layout.compute_caption_minimum_inline_size(layout_context);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue