layout: Allow layouts to customize their used style (#35012)

Some layouts like table need some style overrides. We were handling this
in `ComputedValuesExt`, but it was messy, unreliable and too limited.

For example, we were assuming that a style with `display: table` would
belong to a table wrapper box or table grid box. However, certain HTML
elements can ignore their `display` value and generate a different kind
of box. I think we aren't doing that yet, but we will need this.

Also, resolving the used border of a table needs layout information,
which we don't have in `ComputedValuesExt`. This patch will allow to
improve border collapsing in a follow-up.

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2025-01-16 08:54:47 -08:00 committed by GitHub
parent 7e7792dfbd
commit 60dc3b26fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 368 additions and 238 deletions

View file

@ -42,7 +42,8 @@ use crate::sizing::{
ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult, IntrinsicSizingMode,
};
use crate::style_ext::{
AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBMDeprecated, PaddingBorderMargin,
AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBMDeprecated, LayoutStyle,
PaddingBorderMargin,
};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
@ -1052,7 +1053,7 @@ impl FlexContainer {
containing_block_for_container: &ContainingBlock,
) -> (FlexRelativeVec2<Au>, FlexRelativeVec2<Option<Au>>, bool) {
let sizes: ContentBoxSizesAndPBMDeprecated = self
.style
.layout_style()
.content_box_sizes_and_padding_border_margin(&containing_block_for_container.into())
.into();
@ -1071,6 +1072,11 @@ impl FlexContainer {
sizes.depends_on_block_constraints,
)
}
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.style)
}
}
/// Align all flex lines per `align-content` according to
@ -1141,7 +1147,8 @@ impl<'a> FlexItem<'a> {
pbm,
depends_on_block_constraints,
} = box_
.style()
.independent_formatting_context
.layout_style()
.content_box_sizes_and_padding_border_margin(&containing_block.into())
.into();
@ -2281,7 +2288,9 @@ impl FlexItemBox {
content_max_box_size,
pbm,
..
} = style
} = self
.independent_formatting_context
.layout_style()
.content_box_sizes_and_padding_border_margin(containing_block)
.into();
let preferred_aspect_ratio = self

View file

@ -15,7 +15,7 @@ use crate::context::LayoutContext;
use crate::dom::NodeExt;
use crate::dom_traversal::NodeAndStyleInfo;
use crate::fragment_tree::BaseFragmentInfo;
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
use crate::style_ext::{LayoutStyle, PaddingBorderMargin};
use crate::ContainingBlock;
#[derive(Debug)]
@ -52,6 +52,11 @@ impl InlineBox {
..*self
}
}
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.style)
}
}
#[derive(Debug, Default)]
@ -214,7 +219,9 @@ impl InlineBoxContainerState {
font_metrics: Option<&FontMetrics>,
) -> Self {
let style = inline_box.style.clone();
let pbm = style.padding_border_margin(containing_block);
let pbm = inline_box
.layout_style()
.padding_border_margin(containing_block);
let mut flags = InlineContainerStateFlags::empty();
if inline_container_needs_strut(&style, layout_context, Some(&pbm)) {

View file

@ -2244,11 +2244,11 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
let inline_box = inline_box.borrow();
let zero = Au::zero();
let writing_mode = self.constraint_space.writing_mode;
let padding = inline_box
.style
let layout_style = inline_box.layout_style();
let padding = layout_style
.padding(writing_mode)
.percentages_relative_to(zero);
let border = inline_box.style.border_width(writing_mode);
let border = layout_style.border_width(writing_mode);
let margin = inline_box
.style
.margin(writing_mode)

View file

@ -38,7 +38,7 @@ use crate::layout_box_base::LayoutBoxBase;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::replaced::ReplacedContents;
use crate::sizing::{self, ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, ContentBoxSizesAndPBM, PaddingBorderMargin};
use crate::style_ext::{ContentBoxSizesAndPBM, LayoutStyle, PaddingBorderMargin};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock,
SizeConstraint,
@ -105,19 +105,22 @@ impl BlockLevelBox {
collected_margin: &mut CollapsedMargin,
containing_block: &ContainingBlock,
) -> bool {
let style = match self {
BlockLevelBox::SameFormattingContextBlock { base, .. } => &base.style,
let layout_style = match self {
BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
contents.layout_style(base)
},
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) |
BlockLevelBox::OutOfFlowFloatBox(_) => return true,
BlockLevelBox::OutsideMarker(_) => return false,
BlockLevelBox::Independent(ref context) => {
// FIXME: If the element doesn't fit next to floats, it will get clearance.
// In that case this should be returning false.
context.style()
context.layout_style()
},
};
// FIXME: This should only return false when 'clear' causes clearance.
let style = layout_style.style();
if style.get_box().clear != StyleClear::None {
return false;
}
@ -126,7 +129,7 @@ impl BlockLevelBox {
content_box_sizes,
pbm,
..
} = style.content_box_sizes_and_padding_border_margin(&containing_block.into());
} = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
let margin = pbm.margin.auto_is(Au::zero);
collected_margin.adjoin_assign(&CollapsedMargin::new(margin.block_start));
@ -310,9 +313,11 @@ impl OutsideMarker {
// TODO: This is the wrong containing block, as it should be the containing block of the
// parent of this list item. What this means in practice is that the writing mode could be
// wrong and padding defined as a percentage will be resolved incorrectly.
let pbm_of_list_item = self
.list_item_style()
.padding_border_margin(containing_block);
//
// TODO: This should use the LayoutStyle of the list item, not the default one. Currently
// they are the same, but this could change in the future.
let pbm_of_list_item =
LayoutStyle::Default(self.list_item_style()).padding_border_margin(containing_block);
let content_rect = LogicalRect {
start_corner: LogicalVec2 {
inline: -max_inline_size -
@ -387,6 +392,11 @@ impl BlockFormattingContext {
detailed_layout_info: None,
}
}
#[inline]
pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
LayoutStyle::Default(&base.style)
}
}
/// Finds the min/max-content inline size of the block-level children of a block container.
@ -425,7 +435,7 @@ fn compute_inline_content_sizes_for_block_level_boxes(
},
BlockLevelBox::SameFormattingContextBlock { base, contents, .. } => {
let inline_content_sizes_result = sizing::outer_inline(
&base.style,
&contents.layout_style(base),
containing_block,
&LogicalVec2::zero(),
false, /* auto_block_size_stretches_to_containing_block */
@ -568,6 +578,11 @@ impl BlockContainer {
),
}
}
#[inline]
pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
LayoutStyle::Default(&base.style)
}
}
impl ComputeInlineContentSizes for BlockContainer {
@ -824,6 +839,7 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
collapsible_with_parent_start_margin: Option<CollapsibleWithParentStartMargin>,
) -> BoxFragment {
let style = &base.style;
let layout_style = contents.layout_style(base);
let containing_block_writing_mode = containing_block.style.writing_mode;
let get_inline_content_sizes = |constraint_space: &ConstraintSpace| {
base.inline_content_sizes(layout_context, constraint_space, contents)
@ -837,7 +853,7 @@ fn layout_in_flow_non_replaced_block_level_same_formatting_context(
available_block_size,
} = solve_containing_block_padding_and_border_for_in_flow_box(
containing_block,
style,
&layout_style,
get_inline_content_sizes,
false, /* is_table */
);
@ -1071,6 +1087,7 @@ impl IndependentNonReplacedContents {
base.inline_content_sizes(layout_context, constraint_space, self)
.sizes
};
let layout_style = self.layout_style(base);
let ContainingBlockPaddingAndBorder {
containing_block: containing_block_for_children,
pbm,
@ -1079,7 +1096,7 @@ impl IndependentNonReplacedContents {
available_block_size,
} = solve_containing_block_padding_and_border_for_in_flow_box(
containing_block,
&base.style,
&layout_style,
get_inline_content_sizes,
self.is_table(),
);
@ -1163,7 +1180,9 @@ impl IndependentNonReplacedContents {
content_box_sizes,
pbm,
depends_on_block_constraints,
} = style.content_box_sizes_and_padding_border_margin(&containing_block.into());
} = self
.layout_style(base)
.content_box_sizes_and_padding_border_margin(&containing_block.into());
let (margin_block_start, margin_block_end) =
solve_block_margins_for_in_flow_block_level(&pbm);
@ -1468,8 +1487,8 @@ impl ReplacedContents {
containing_block: &ContainingBlock,
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
) -> BoxFragment {
let content_box_sizes_and_pbm = base
.style
let content_box_sizes_and_pbm = self
.layout_style(base)
.content_box_sizes_and_padding_border_margin(&containing_block.into());
let pbm = &content_box_sizes_and_pbm.pbm;
let content_size = self.used_size_as_if_inline_element(
@ -1618,10 +1637,11 @@ struct ResolvedMargins {
/// inline size could then be incorrect.
fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
containing_block: &ContainingBlock<'_>,
style: &'a Arc<ComputedValues>,
layout_style: &'a LayoutStyle,
get_inline_content_sizes: impl FnOnce(&ConstraintSpace) -> ContentSizes,
is_table: bool,
) -> ContainingBlockPaddingAndBorder<'a> {
let style = layout_style.style();
if matches!(style.pseudo(), Some(PseudoElement::ServoAnonymousBox)) {
// <https://drafts.csswg.org/css2/#anonymous-block-level>
// > Anonymous block boxes are ignored when resolving percentage values that would
@ -1650,7 +1670,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
content_box_sizes,
pbm,
depends_on_block_constraints,
} = style.content_box_sizes_and_padding_border_margin(&containing_block.into());
} = layout_style.content_box_sizes_and_padding_border_margin(&containing_block.into());
let margin = pbm.margin.auto_is(Au::zero);
let pbm_sums = pbm.padding + pbm.border + margin;
@ -2140,8 +2160,9 @@ impl IndependentFormattingContext {
) -> IndependentLayoutResult {
let style = self.style();
let container_writing_mode = containing_block.style.writing_mode;
let content_box_sizes_and_pbm =
style.content_box_sizes_and_padding_border_margin(&containing_block.into());
let content_box_sizes_and_pbm = self
.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;

View file

@ -21,7 +21,7 @@ use crate::layout_box_base::LayoutBoxBase;
use crate::positioned::PositioningContext;
use crate::replaced::ReplacedContents;
use crate::sizing::{self, ComputeInlineContentSizes, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, DisplayInside};
use crate::style_ext::{AspectRatio, DisplayInside, LayoutStyle};
use crate::table::Table;
use crate::taffy::TaffyContainer;
use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, LogicalVec2};
@ -217,7 +217,7 @@ impl IndependentFormattingContext {
auto_block_size_stretches_to_containing_block: bool,
) -> InlineContentSizesResult {
sizing::outer_inline(
self.style(),
&self.layout_style(),
containing_block,
auto_minimum,
auto_block_size_stretches_to_containing_block,
@ -242,6 +242,18 @@ impl IndependentFormattingContext {
},
}
}
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
match &self.contents {
IndependentFormattingContextContents::NonReplaced(content) => {
content.layout_style(&self.base)
},
IndependentFormattingContextContents::Replaced(content) => {
content.layout_style(&self.base)
},
}
}
}
impl IndependentNonReplacedContents {
@ -279,6 +291,16 @@ impl IndependentNonReplacedContents {
}
}
#[inline]
pub(crate) fn layout_style<'a>(&'a self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
match self {
IndependentNonReplacedContents::Flow(fc) => fc.layout_style(base),
IndependentNonReplacedContents::Flex(fc) => fc.layout_style(),
IndependentNonReplacedContents::Grid(fc) => fc.layout_style(),
IndependentNonReplacedContents::Table(fc) => fc.layout_style(),
}
}
#[inline]
pub(crate) fn preferred_aspect_ratio(&self) -> Option<AspectRatio> {
// TODO: support preferred aspect ratios on non-replaced boxes.

View file

@ -467,7 +467,9 @@ impl HoistedAbsolutelyPositionedBox {
content_box_sizes,
pbm,
..
} = style.content_box_sizes_and_padding_border_margin(&containing_block.into());
} = context
.layout_style()
.content_box_sizes_and_padding_border_margin(&containing_block.into());
let containing_block = &containing_block.into();
let is_table = context.is_table();

View file

@ -31,8 +31,9 @@ use crate::context::LayoutContext;
use crate::dom::NodeExt;
use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment};
use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize, Size, Sizes};
use crate::layout_box_base::LayoutBoxBase;
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM};
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
use crate::{ConstraintSpace, ContainingBlock, SizeConstraint};
#[derive(Debug)]
@ -567,6 +568,11 @@ impl ReplacedContents {
block: block_size,
}
}
#[inline]
pub(crate) fn layout_style<'a>(&self, base: &'a LayoutBoxBase) -> LayoutStyle<'a> {
LayoutStyle::Default(&base.style)
}
}
impl ComputeInlineContentSizes for ReplacedContents {

View file

@ -8,13 +8,12 @@ use std::cell::LazyCell;
use std::ops::{Add, AddAssign};
use app_units::Au;
use style::properties::ComputedValues;
use style::values::computed::LengthPercentage;
use style::Zero;
use crate::context::LayoutContext;
use crate::geom::Size;
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM};
use crate::style_ext::{AspectRatio, Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, LayoutStyle};
use crate::{ConstraintSpace, IndefiniteContainingBlock, LogicalVec2};
#[derive(PartialEq)]
@ -111,7 +110,7 @@ impl From<Au> for ContentSizes {
#[allow(clippy::too_many_arguments)]
pub(crate) fn outer_inline(
style: &ComputedValues,
layout_style: &LayoutStyle,
containing_block: &IndefiniteContainingBlock,
auto_minimum: &LogicalVec2<Au>,
auto_block_size_stretches_to_containing_block: bool,
@ -125,12 +124,13 @@ pub(crate) fn outer_inline(
content_box_sizes,
pbm,
mut depends_on_block_constraints,
} = style.content_box_sizes_and_padding_border_margin(containing_block);
} = layout_style.content_box_sizes_and_padding_border_margin(containing_block);
let margin = pbm.margin.map(|v| v.auto_is(Au::zero));
let pbm_sums = LogicalVec2 {
block: pbm.padding_border_sums.block + margin.block_sum(),
inline: pbm.padding_border_sums.inline + margin.inline_sum(),
};
let style = layout_style.style();
let content_size = LazyCell::new(|| {
let constraint_space = if establishes_containing_block {
let available_block_size = containing_block

View file

@ -278,23 +278,10 @@ pub(crate) trait ComputedValuesExt {
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> ContentBoxSizesAndPBM;
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin;
fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin;
fn padding(&self, containing_block_writing_mode: WritingMode)
-> LogicalSides<LengthPercentage>;
fn border_style_color(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<BorderStyleColor>;
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Au>;
fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>>;
fn margin(
&self,
@ -445,129 +432,6 @@ impl ComputedValuesExt for ComputedValues {
}
}
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> ContentBoxSizesAndPBM {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
// If max size properties or preferred size properties are set to a value containing
// indefinite percentages, we treat the entire value as the initial value of the property.
// However, for min size properties, as well as for margins and paddings,
// we instead resolve indefinite percentages against zero.
let containing_block_size = containing_block.size.map(|value| value.non_auto());
let containing_block_size_auto_is_zero =
containing_block_size.map(|value| value.unwrap_or_else(Au::zero));
let writing_mode = containing_block.writing_mode;
let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
writing_mode,
containing_block.size.inline.auto_is(Au::zero),
);
let box_size = self.box_size(writing_mode);
let min_size = self.min_box_size(writing_mode);
let max_size = self.max_box_size(writing_mode);
let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
match size {
// fit-content is like clamp(min-content, stretch, max-content), but currently
// min-content and max-content have the same behavior in the block axis,
// so there is no dependency on block constraints.
// TODO: for flex and grid layout, min-content and max-content should be different.
// TODO: We are assuming that Size::Initial doesn't stretch. However, it may actually
// stretch flex and grid items depending on the CSS Align properties, in that case
// the caller needs to take care of it.
Size::Stretch => true,
Size::Numeric(length_percentage) => length_percentage.has_percentage(),
_ => false,
}
};
let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
depends_on_block_constraints(&min_size.block) ||
depends_on_block_constraints(&max_size.block) ||
self.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
let box_size = box_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_box_size = self
.content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from));
let min_size = min_size.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
let content_min_box_size = self
.content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from));
let max_size = max_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_max_box_size = self
.content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from));
ContentBoxSizesAndPBM {
content_box_sizes: LogicalVec2 {
block: Sizes::new(
content_box_size.block,
content_min_box_size.block,
content_max_box_size.block,
),
inline: Sizes::new(
content_box_size.inline,
content_min_box_size.inline,
content_max_box_size.inline,
),
},
pbm,
depends_on_block_constraints,
}
}
fn padding_border_margin(&self, containing_block: &ContainingBlock) -> PaddingBorderMargin {
self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
containing_block.style.writing_mode,
containing_block.size.inline,
)
}
fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin {
let padding = self
.padding(writing_mode)
.percentages_relative_to(containing_block_inline_size);
let border = self.border_width(writing_mode);
let margin = self
.margin(writing_mode)
.percentages_relative_to(containing_block_inline_size);
PaddingBorderMargin {
padding_border_sums: LogicalVec2 {
inline: padding.inline_sum() + border.inline_sum(),
block: padding.block_sum() + border.block_sum(),
},
padding,
border,
margin,
}
}
fn padding(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<LengthPercentage> {
if self.get_box().display.inside() == stylo::DisplayInside::Table &&
self.get_inherited_table().border_collapse == BorderCollapse::Collapse
{
// https://drafts.csswg.org/css-tables/#collapsed-style-overrides
// > The padding of the table-root is ignored (as if it was set to 0px).
return LogicalSides::zero();
}
let padding = self.get_padding().clone();
LogicalSides::from_physical(
&PhysicalSides::new(
padding.padding_top.0,
padding.padding_right.0,
padding.padding_bottom.0,
padding.padding_left.0,
),
containing_block_writing_mode,
)
}
fn border_style_color(
&self,
containing_block_writing_mode: WritingMode,
@ -578,36 +442,6 @@ impl ComputedValuesExt for ComputedValues {
)
}
fn border_width(&self, containing_block_writing_mode: WritingMode) -> LogicalSides<Au> {
let border = self.get_border();
if self.get_box().display.inside() == stylo::DisplayInside::Table &&
!matches!(self.pseudo(), Some(PseudoElement::ServoTableGrid)) &&
self.get_inherited_table().border_collapse == BorderCollapse::Collapse
{
// For tables in collapsed-borders mode we halve the border widths, because
// > in this model, the width of the table includes half the table border.
// https://www.w3.org/TR/CSS22/tables.html#collapsing-borders
return LogicalSides::from_physical(
&PhysicalSides::new(
border.border_top_width / 2,
border.border_right_width / 2,
border.border_bottom_width / 2,
border.border_left_width / 2,
),
containing_block_writing_mode,
);
}
LogicalSides::from_physical(
&PhysicalSides::new(
border.border_top_width,
border.border_right_width,
border.border_bottom_width,
border.border_left_width,
),
containing_block_writing_mode,
)
}
fn physical_margin(&self) -> PhysicalSides<LengthPercentageOrAuto<'_>> {
fn convert(inset: &Margin) -> LengthPercentageOrAuto<'_> {
match inset {
@ -969,6 +803,183 @@ impl ComputedValuesExt for ComputedValues {
}
}
pub(crate) enum LayoutStyle<'a> {
Default(&'a ComputedValues),
Table(&'a ComputedValues),
}
impl LayoutStyle<'_> {
#[inline]
pub(crate) fn style(&self) -> &ComputedValues {
match self {
Self::Default(style) => style,
Self::Table(style) => style,
}
}
pub(crate) fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> ContentBoxSizesAndPBM {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
// If max size properties or preferred size properties are set to a value containing
// indefinite percentages, we treat the entire value as the initial value of the property.
// However, for min size properties, as well as for margins and paddings,
// we instead resolve indefinite percentages against zero.
let containing_block_size = containing_block.size.map(|value| value.non_auto());
let containing_block_size_auto_is_zero =
containing_block_size.map(|value| value.unwrap_or_else(Au::zero));
let writing_mode = containing_block.writing_mode;
let pbm = self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
writing_mode,
containing_block.size.inline.auto_is(Au::zero),
);
let style = self.style();
let box_size = style.box_size(writing_mode);
let min_size = style.min_box_size(writing_mode);
let max_size = style.max_box_size(writing_mode);
let depends_on_block_constraints = |size: &Size<LengthPercentage>| {
match size {
// fit-content is like clamp(min-content, stretch, max-content), but currently
// min-content and max-content have the same behavior in the block axis,
// so there is no dependency on block constraints.
// TODO: for flex and grid layout, min-content and max-content should be different.
// TODO: We are assuming that Size::Initial doesn't stretch. However, it may actually
// stretch flex and grid items depending on the CSS Align properties, in that case
// the caller needs to take care of it.
Size::Stretch => true,
Size::Numeric(length_percentage) => length_percentage.has_percentage(),
_ => false,
}
};
let depends_on_block_constraints = depends_on_block_constraints(&box_size.block) ||
depends_on_block_constraints(&min_size.block) ||
depends_on_block_constraints(&max_size.block) ||
style.depends_on_block_constraints_due_to_relative_positioning(writing_mode);
let box_size = box_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_box_size = style
.content_box_size_for_box_size(box_size, &pbm)
.map(|v| v.map(Au::from));
let min_size = min_size.percentages_relative_to_basis(&containing_block_size_auto_is_zero);
let content_min_box_size = style
.content_min_box_size_for_min_size(min_size, &pbm)
.map(|v| v.map(Au::from));
let max_size = max_size.maybe_percentages_relative_to_basis(&containing_block_size);
let content_max_box_size = style
.content_max_box_size_for_max_size(max_size, &pbm)
.map(|v| v.map(Au::from));
ContentBoxSizesAndPBM {
content_box_sizes: LogicalVec2 {
block: Sizes::new(
content_box_size.block,
content_min_box_size.block,
content_max_box_size.block,
),
inline: Sizes::new(
content_box_size.inline,
content_min_box_size.inline,
content_max_box_size.inline,
),
},
pbm,
depends_on_block_constraints,
}
}
pub(crate) fn padding_border_margin(
&self,
containing_block: &ContainingBlock,
) -> PaddingBorderMargin {
self.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
containing_block.style.writing_mode,
containing_block.size.inline,
)
}
pub(crate) fn padding_border_margin_with_writing_mode_and_containing_block_inline_size(
&self,
writing_mode: WritingMode,
containing_block_inline_size: Au,
) -> PaddingBorderMargin {
let padding = self
.padding(writing_mode)
.percentages_relative_to(containing_block_inline_size);
let style = self.style();
let border = self.border_width(writing_mode);
let margin = style
.margin(writing_mode)
.percentages_relative_to(containing_block_inline_size);
PaddingBorderMargin {
padding_border_sums: LogicalVec2 {
inline: padding.inline_sum() + border.inline_sum(),
block: padding.block_sum() + border.block_sum(),
},
padding,
border,
margin,
}
}
pub(crate) fn padding(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<LengthPercentage> {
let style = self.style();
if matches!(self, Self::Table(_)) &&
style.get_inherited_table().border_collapse == BorderCollapse::Collapse
{
// https://drafts.csswg.org/css-tables/#collapsed-style-overrides
// > The padding of the table-root is ignored (as if it was set to 0px).
return LogicalSides::zero();
}
let padding = self.style().get_padding().clone();
LogicalSides::from_physical(
&PhysicalSides::new(
padding.padding_top.0,
padding.padding_right.0,
padding.padding_bottom.0,
padding.padding_left.0,
),
containing_block_writing_mode,
)
}
pub(crate) fn border_width(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalSides<Au> {
let style = self.style();
let border = style.get_border();
if matches!(self, Self::Table(_)) &&
style.get_inherited_table().border_collapse == BorderCollapse::Collapse
{
// For tables in collapsed-borders mode we halve the border widths, because
// > in this model, the width of the table includes half the table border.
// https://www.w3.org/TR/CSS22/tables.html#collapsing-borders
return LogicalSides::from_physical(
&PhysicalSides::new(
border.border_top_width / 2,
border.border_right_width / 2,
border.border_bottom_width / 2,
border.border_left_width / 2,
),
containing_block_writing_mode,
);
}
LogicalSides::from_physical(
&PhysicalSides::new(
border.border_top_width,
border.border_right_width,
border.border_bottom_width,
border.border_left_width,
),
containing_block_writing_mode,
)
}
}
impl From<stylo::Display> for Display {
fn from(packed: stylo::Display) -> Self {
let outside = packed.outside();

View file

@ -38,7 +38,9 @@ use crate::geom::{
};
use crate::positioned::{relative_adjustement, PositioningContext, PositioningContextLength};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{BorderStyleColor, Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::style_ext::{
BorderStyleColor, Clamp, ComputedValuesExt, LayoutStyle, PaddingBorderMargin,
};
use crate::table::{SpecificTableOrTableCellInfo, TableSlotCoordinates};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, IndefiniteContainingBlock, WritingMode,
@ -121,9 +123,12 @@ impl CollapsedBorder {
Self { style_color, width }
}
fn from_style(style: &ComputedValues, writing_mode: WritingMode) -> LogicalSides<Self> {
let border_style_color = style.border_style_color(writing_mode);
let border_width = style.border_width(writing_mode);
fn from_layout_style(
layout_style: &LayoutStyle,
writing_mode: WritingMode,
) -> LogicalSides<Self> {
let border_style_color = layout_style.style().border_style_color(writing_mode);
let border_width = layout_style.border_width(writing_mode);
LogicalSides {
inline_start: Self::new(border_style_color.inline_start, border_width.inline_start),
inline_end: Self::new(border_style_color.inline_end, border_width.inline_end),
@ -285,12 +290,10 @@ impl<'a> TableLayout<'a> {
_ => continue,
};
let padding = cell
.base
.style
let layout_style = cell.layout_style();
let padding = layout_style
.padding(writing_mode)
.percentages_relative_to(Au::zero());
let border = self
.get_collapsed_border_widths_for_area(LogicalSides {
inline_start: column_index,
@ -298,7 +301,7 @@ impl<'a> TableLayout<'a> {
block_start: row_index,
block_end: row_index + cell.rowspan,
})
.unwrap_or_else(|| cell.base.style.border_width(writing_mode));
.unwrap_or_else(|| layout_style.border_width(writing_mode));
let padding_border_sums = LogicalVec2 {
inline: padding.inline_sum() + border.inline_sum(),
@ -1217,16 +1220,14 @@ impl<'a> TableLayout<'a> {
self.get_collapsed_border_style_colors_for_area(area),
self.table.style.writing_mode,
);
let layout_style = cell.layout_style();
let border = self
.get_collapsed_border_widths_for_area(area)
.unwrap_or_else(|| {
cell.base
.style
layout_style
.border_width(containing_block_for_table.style.writing_mode)
});
let padding: LogicalSides<Au> = cell
.base
.style
let padding: LogicalSides<Au> = layout_style
.padding(containing_block_for_table.style.writing_mode)
.percentages_relative_to(self.basis_for_cell_padding_percentage);
let inline_border_padding_sum = border.inline_sum() + padding.inline_sum();
@ -1651,9 +1652,8 @@ impl<'a> TableLayout<'a> {
containing_block_for_table: &ContainingBlock,
) -> IndependentLayout {
let table_writing_mode = containing_block_for_children.style.writing_mode;
self.pbm = self
.table
.style
let layout_style = self.table.layout_style();
self.pbm = layout_style
.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
table_writing_mode,
containing_block_for_table.size.inline,
@ -1690,9 +1690,7 @@ impl<'a> TableLayout<'a> {
let offset_from_wrapper = -self.pbm.padding - self.pbm.border;
let mut current_block_offset = offset_from_wrapper.block_start;
let depends_on_block_constraints = self
.table
.style
let depends_on_block_constraints = layout_style
.content_box_sizes_and_padding_border_margin(&containing_block_for_table.into())
.depends_on_block_constraints;
@ -2234,8 +2232,8 @@ impl<'a> TableLayout<'a> {
};
let mut apply_border =
|style: &ComputedValues, block: &Range<usize>, inline: &Range<usize>| {
let border = CollapsedBorder::from_style(style, writing_mode);
|layout_style: &LayoutStyle, block: &Range<usize>, inline: &Range<usize>| {
let border = CollapsedBorder::from_layout_style(layout_style, writing_mode);
collapsed_borders.block[block.start].max_assign(&border.block_start, inline);
collapsed_borders.block[block.end].max_assign(&border.block_end, inline);
collapsed_borders.inline[inline.start].max_assign(&border.inline_start, block);
@ -2243,18 +2241,34 @@ impl<'a> TableLayout<'a> {
};
let all_rows = 0..self.table.size.height;
let all_columns = 0..self.table.size.width;
apply_border(&self.table.grid_style, &all_rows, &all_columns);
apply_border(&self.table.layout_style_for_grid(), &all_rows, &all_columns);
for column_group in &self.table.column_groups {
apply_border(&column_group.style, &all_rows, &column_group.track_range);
apply_border(
&column_group.layout_style(),
&all_rows,
&column_group.track_range,
);
}
for (column_index, column) in self.table.columns.iter().enumerate() {
apply_border(&column.style, &all_rows, &(column_index..column_index + 1));
apply_border(
&column.layout_style(),
&all_rows,
&(column_index..column_index + 1),
);
}
for row_group in &self.table.row_groups {
apply_border(&row_group.style, &row_group.track_range, &all_columns);
apply_border(
&row_group.layout_style(),
&row_group.track_range,
&all_columns,
);
}
for (row_index, row) in self.table.rows.iter().enumerate() {
apply_border(&row.style, &(row_index..row_index + 1), &all_columns);
apply_border(
&row.layout_style(),
&(row_index..row_index + 1),
&all_columns,
);
}
for row_index in 0..self.table.size.height {
for column_index in 0..self.table.size.width {
@ -2264,7 +2278,7 @@ impl<'a> TableLayout<'a> {
};
apply_border(
&cell.base.style,
&cell.layout_style(),
&(row_index..row_index + cell.rowspan),
&(column_index..column_index + cell.colspan),
);
@ -2752,9 +2766,9 @@ impl ComputeInlineContentSizes for Table {
constraint_space: &ConstraintSpace,
) -> InlineContentSizesResult {
let writing_mode = constraint_space.writing_mode;
let layout_style = self.layout_style();
let mut layout = TableLayout::new(self);
layout.pbm = self
.style
layout.pbm = layout_style
.padding_border_margin_with_writing_mode_and_containing_block_inline_size(
writing_mode,
Au::zero(),
@ -2769,11 +2783,10 @@ impl ComputeInlineContentSizes for Table {
// Padding and border should apply to the table grid, but they will be taken into
// account when computing the inline content sizes of the table wrapper (our parent), so
// this code removes their contribution from the inline content size of the caption.
let padding = self
.style
let padding = layout_style
.padding(writing_mode)
.percentages_relative_to(Au::zero());
let border = self.style.border_width(writing_mode);
let border = layout_style.border_width(writing_mode);
caption_minimum_inline_size -= padding.inline_sum() + border.inline_sum();
table_content_sizes
.min_content
@ -2790,7 +2803,38 @@ impl ComputeInlineContentSizes for Table {
}
}
impl Table {
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Table(&self.style)
}
#[inline]
pub(crate) fn layout_style_for_grid(&self) -> LayoutStyle {
LayoutStyle::Default(&self.grid_style)
}
}
impl TableTrack {
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.style)
}
}
impl TableTrackGroup {
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.style)
}
}
impl TableSlotCell {
#[inline]
fn layout_style(&self) -> LayoutStyle {
self.contents.layout_style(&self.base)
}
fn effective_vertical_align(&self) -> VerticalAlignKeyword {
match self.base.style.clone_vertical_align() {
VerticalAlign::Keyword(VerticalAlignKeyword::Top) => VerticalAlignKeyword::Top,

View file

@ -27,7 +27,7 @@ use crate::geom::{
};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::ComputedValuesExt;
use crate::style_ext::LayoutStyle;
use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize};
const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
@ -136,7 +136,9 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
let style = independent_context.style();
// Adjust known_dimensions from border box to content box
let pbm = style.padding_border_margin(containing_block);
let pbm = independent_context
.layout_style()
.padding_border_margin(containing_block);
let pb_sum = pbm.padding_border_sums.map(|v| v.to_f32_px());
let margin_sum = pbm.margin.auto_is(Au::zero).sum().map(|v| v.to_f32_px());
let content_box_inset = pb_sum + margin_sum;
@ -387,7 +389,8 @@ impl ComputeInlineContentSizes for TaffyContainer {
_ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
};
let pb_sums = style
let pb_sums = self
.layout_style()
.padding_border_margin(containing_block)
.padding_border_sums;
@ -428,7 +431,7 @@ impl TaffyContainer {
let container_style = &content_box_size_override.style;
let align_items = container_style.clone_align_items();
let justify_items = container_style.clone_justify_items();
let pbm = container_style.padding_border_margin(containing_block);
let pbm = self.layout_style().padding_border_margin(containing_block);
let known_dimensions = taffy::Size {
width: Some(
@ -625,4 +628,9 @@ impl TaffyContainer {
detailed_layout_info: container_ctx.detailed_layout_info,
}
}
#[inline]
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.style)
}
}