Make ComputedValuesExt expose keywords for the sizing properties (#33558)

This will allow callers to start obeying `min-content`, `max-content`,
`fit-content` and `stretch` in follow-up patches.
The old functionality is kept as deprecated methods that we should
eventually remove.
This patch has very little impact on the existing behavior, just some
very minimal implementation of the keywords for css tables.

This also overhauls fixed-layout-2.html since:
 - It had code that wasn't doing anything
 - It had wrong expecations in prose
 - The logic seemed broken in general
 - All browsers were failing one testcase

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Oriol Brufau 2024-09-27 10:16:07 -07:00 committed by GitHub
parent c7ef974968
commit 057dd1e9eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 419 additions and 268 deletions

View file

@ -936,10 +936,10 @@ impl FlexContainer {
.padding_border_margin(containing_block_for_container);
let max_box_size = self
.style
.content_max_box_size(containing_block_for_container, &pbm);
.content_max_box_size_deprecated(containing_block_for_container, &pbm);
let min_box_size = self
.style
.content_min_box_size(containing_block_for_container, &pbm)
.content_min_box_size_deprecated(containing_block_for_container, &pbm)
.auto_is(Au::zero);
let max_box_size = self.config.flex_axis.vec2_to_flex_relative(max_box_size);
@ -1012,15 +1012,15 @@ impl<'a> FlexItem<'a> {
let pbm = box_.style().padding_border_margin(containing_block);
let content_box_size = box_
.style()
.content_box_size(containing_block, &pbm)
.content_box_size_deprecated(containing_block, &pbm)
.map(|v| v.map(Au::from));
let max_size = box_
.style()
.content_max_box_size(containing_block, &pbm)
.content_max_box_size_deprecated(containing_block, &pbm)
.map(|v| v.map(Au::from));
let min_size = box_
.style()
.content_min_box_size(containing_block, &pbm)
.content_min_box_size_deprecated(containing_block, &pbm)
.map(|v| v.map(Au::from));
let margin_auto_is_zero = flex_context.sides_to_flex_relative(pbm.margin.auto_is(Au::zero));
@ -2005,7 +2005,7 @@ impl FlexItemBox {
cross_axis_is_item_block_axis(container_is_horizontal, item_is_horizontal, flex_axis);
let (content_box_size, content_min_size, content_max_size, pbm) =
style.content_box_sizes_and_padding_border_margin(containing_block);
style.content_box_sizes_and_padding_border_margin_deprecated(containing_block);
let padding = main_start_cross_start.sides_to_flex_relative(pbm.padding);
let border = main_start_cross_start.sides_to_flex_relative(pbm.border);
let margin = main_start_cross_start.sides_to_flex_relative(pbm.margin);

View file

@ -915,13 +915,13 @@ impl FloatBox {
// https://drafts.csswg.org/css2/#float-width
let style = non_replaced.style.clone();
let box_size = style
.content_box_size(containing_block, &pbm)
.content_box_size_deprecated(containing_block, &pbm)
.map(|v| v.map(Au::from));
let max_box_size = style
.content_max_box_size(containing_block, &pbm)
.content_max_box_size_deprecated(containing_block, &pbm)
.map(|v| v.map(Au::from));
let min_box_size = style
.content_min_box_size(containing_block, &pbm)
.content_min_box_size_deprecated(containing_block, &pbm)
.map(|v| v.map(Au::from))
.auto_is(Au::zero);

View file

@ -1929,15 +1929,15 @@ impl IndependentFormattingContext {
IndependentFormattingContext::NonReplaced(non_replaced) => {
let box_size = non_replaced
.style
.content_box_size(layout.containing_block, &pbm)
.content_box_size_deprecated(layout.containing_block, &pbm)
.map(|v| v.map(Au::from));
let max_box_size = non_replaced
.style
.content_max_box_size(layout.containing_block, &pbm)
.content_max_box_size_deprecated(layout.containing_block, &pbm)
.map(|v| v.map(Au::from));
let min_box_size = non_replaced
.style
.content_min_box_size(layout.containing_block, &pbm)
.content_min_box_size_deprecated(layout.containing_block, &pbm)
.map(|v| v.map(Au::from))
.auto_is(Au::zero);
let block_size = box_size

View file

@ -135,10 +135,10 @@ impl BlockLevelBox {
}
let min_size = style
.content_min_box_size(containing_block, &pbm)
.content_min_box_size_deprecated(containing_block, &pbm)
.auto_is(Au::zero);
let max_size = style.content_max_box_size(containing_block, &pbm);
let prefered_size = style.content_box_size(containing_block, &pbm);
let max_size = style.content_max_box_size_deprecated(containing_block, &pbm);
let prefered_size = style.content_box_size_deprecated(containing_block, &pbm);
let inline_size = prefered_size
.inline
.auto_is(|| {
@ -1048,11 +1048,15 @@ impl NonReplacedFormattingContext {
sequential_layout_state: &mut SequentialLayoutState,
) -> BoxFragment {
let pbm = self.style.padding_border_margin(containing_block);
let box_size = self.style.content_box_size(containing_block, &pbm);
let max_box_size = self.style.content_max_box_size(containing_block, &pbm);
let box_size = self
.style
.content_box_size_deprecated(containing_block, &pbm);
let max_box_size = self
.style
.content_max_box_size_deprecated(containing_block, &pbm);
let min_box_size = self
.style
.content_min_box_size(containing_block, &pbm)
.content_min_box_size_deprecated(containing_block, &pbm)
.auto_is(Au::zero);
let block_size = box_size.block.map(|block_size| {
block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
@ -1424,10 +1428,10 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
style: &'a Arc<ComputedValues>,
) -> ContainingBlockPaddingAndBorder<'a> {
let pbm = style.padding_border_margin(containing_block);
let box_size = style.content_box_size(containing_block, &pbm);
let max_box_size = style.content_max_box_size(containing_block, &pbm);
let box_size = style.content_box_size_deprecated(containing_block, &pbm);
let max_box_size = style.content_max_box_size_deprecated(containing_block, &pbm);
let min_box_size = style
.content_min_box_size(containing_block, &pbm)
.content_min_box_size_deprecated(containing_block, &pbm)
.auto_is(Au::zero);
// https://drafts.csswg.org/css2/#the-width-property

View file

@ -9,7 +9,9 @@ use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
use app_units::Au;
use serde::Serialize;
use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
use style::values::computed::{CSSPixelLength, LengthPercentage};
use style::values::computed::{
CSSPixelLength, LengthPercentage, MaxSize as StyleMaxSize, Size as StyleSize,
};
use style::values::generics::length::GenericLengthPercentageOrAuto as AutoOr;
use style::Zero;
use style_traits::CSSPixel;
@ -145,89 +147,6 @@ impl<T: Clone> LogicalVec2<AutoOr<T>> {
}
}
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
pub(crate) fn percentages_relative_to(
&self,
containing_block: &ContainingBlock,
) -> LogicalVec2<AuOrAuto> {
LogicalVec2 {
inline: self
.inline
.map(|value| value.to_used_value(containing_block.inline_size)),
block: {
self.block
.non_auto()
.and_then(|value| {
value.maybe_to_used_value(containing_block.block_size.non_auto())
})
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage)
},
}
}
}
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
pub(crate) fn percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Au>,
) -> LogicalVec2<AuOrAuto> {
LogicalVec2 {
inline: self.inline.map(|value| value.to_used_value(basis.inline)),
block: self.block.map(|value| value.to_used_value(basis.block)),
}
}
}
impl LogicalVec2<LengthPercentageOrAuto<'_>> {
pub(crate) fn maybe_percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Option<Au>>,
) -> LogicalVec2<AuOrAuto> {
LogicalVec2 {
inline: self
.inline
.non_auto()
.and_then(|value| value.maybe_to_used_value(basis.inline))
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage),
block: self
.block
.non_auto()
.and_then(|value| value.maybe_to_used_value(basis.block))
.map_or(AuOrAuto::Auto, AuOrAuto::LengthPercentage),
}
}
}
impl LogicalVec2<Option<&'_ LengthPercentage>> {
pub(crate) fn percentages_relative_to(
&self,
containing_block: &ContainingBlock,
) -> LogicalVec2<Option<Au>> {
LogicalVec2 {
inline: self
.inline
.map(|lp| lp.to_used_value(containing_block.inline_size)),
block: self
.block
.and_then(|lp| lp.maybe_to_used_value(containing_block.block_size.non_auto())),
}
}
}
impl LogicalVec2<Option<&'_ LengthPercentage>> {
pub(crate) fn maybe_percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Option<Au>>,
) -> LogicalVec2<Option<Au>> {
LogicalVec2 {
inline: self
.inline
.and_then(|v| v.maybe_to_used_value(basis.inline)),
block: self.block.and_then(|v| v.maybe_to_used_value(basis.block)),
}
}
}
impl<T: Zero> LogicalRect<T> {
pub fn zero() -> Self {
Self {
@ -701,3 +620,144 @@ impl ToLogicalWithContainingBlock<LogicalRect<Au>> for PhysicalRect<Au> {
}
}
}
/// The possible keywords accepted by the sizing properties.
/// <https://drafts.csswg.org/css-sizing/#sizing-properties>
#[derive(Clone)]
pub(crate) enum SizeKeyword {
/// Represents an `auto` value for the preferred and minimum size properties,
/// or `none` for the maximum size properties.
/// <https://drafts.csswg.org/css-sizing/#valdef-width-auto>
/// <https://drafts.csswg.org/css-sizing/#valdef-max-width-none>
Initial,
/// <https://drafts.csswg.org/css-sizing/#valdef-width-min-content>
MinContent,
/// <https://drafts.csswg.org/css-sizing/#valdef-width-max-content>
MaxContent,
/// <https://drafts.csswg.org/css-sizing-4/#valdef-width-fit-content>
FitContent,
/// <https://drafts.csswg.org/css-sizing-4/#valdef-width-stretch>
Stretch,
}
/// The possible values accepted by the sizing properties,
/// with numeric `<length-percentage>` resolved as a `T`.
/// <https://drafts.csswg.org/css-sizing/#sizing-properties>
#[derive(Clone)]
pub(crate) enum Size<T> {
Keyword(SizeKeyword),
Numeric(T),
}
impl<T> Default for Size<T> {
#[inline]
fn default() -> Self {
Self::Keyword(SizeKeyword::Initial)
}
}
impl<T: Clone> Size<T> {
#[inline]
pub(crate) fn is_keyword(&self) -> bool {
matches!(self, Self::Keyword(_))
}
#[inline]
pub(crate) fn to_numeric(&self) -> Option<T> {
match self {
Self::Keyword(_) => None,
Self::Numeric(numeric) => Some(numeric).cloned(),
}
}
#[inline]
pub(crate) fn to_auto_or(&self) -> AutoOr<T> {
self.to_numeric()
.map_or(AutoOr::Auto, AutoOr::LengthPercentage)
}
#[inline]
pub fn map<U>(&self, f: impl FnOnce(T) -> U) -> Size<U> {
match self {
Size::Keyword(keyword) => Size::Keyword(keyword.clone()),
Size::Numeric(numeric) => Size::Numeric(f(numeric.clone())),
}
}
#[inline]
pub fn maybe_map<U>(&self, f: impl FnOnce(T) -> Option<U>) -> Option<Size<U>> {
Some(match self {
Size::Keyword(keyword) => Size::Keyword(keyword.clone()),
Size::Numeric(numeric) => Size::Numeric(f(numeric.clone())?),
})
}
}
impl From<StyleSize> for Size<LengthPercentage> {
fn from(size: StyleSize) -> Self {
match size {
StyleSize::LengthPercentage(length) => Size::Numeric(length.0),
StyleSize::Auto => Size::Keyword(SizeKeyword::Initial),
StyleSize::MinContent => Size::Keyword(SizeKeyword::MinContent),
StyleSize::MaxContent => Size::Keyword(SizeKeyword::MaxContent),
StyleSize::FitContent => Size::Keyword(SizeKeyword::FitContent),
StyleSize::Stretch => Size::Keyword(SizeKeyword::Stretch),
}
}
}
impl From<StyleMaxSize> for Size<LengthPercentage> {
fn from(max_size: StyleMaxSize) -> Self {
match max_size {
StyleMaxSize::LengthPercentage(length) => Size::Numeric(length.0),
StyleMaxSize::None => Size::Keyword(SizeKeyword::Initial),
StyleMaxSize::MinContent => Size::Keyword(SizeKeyword::MinContent),
StyleMaxSize::MaxContent => Size::Keyword(SizeKeyword::MaxContent),
StyleMaxSize::FitContent => Size::Keyword(SizeKeyword::FitContent),
StyleMaxSize::Stretch => Size::Keyword(SizeKeyword::Stretch),
}
}
}
impl LogicalVec2<Size<LengthPercentage>> {
pub(crate) fn percentages_relative_to(
&self,
containing_block: &ContainingBlock,
) -> LogicalVec2<Size<Au>> {
LogicalVec2 {
inline: self
.inline
.map(|lp| lp.to_used_value(containing_block.inline_size)),
block: self
.block
.maybe_map(|lp| lp.maybe_to_used_value(containing_block.block_size.non_auto()))
.unwrap_or_default(),
}
}
pub(crate) fn maybe_percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Option<Au>>,
) -> LogicalVec2<Size<Au>> {
LogicalVec2 {
inline: self
.inline
.maybe_map(|v| v.maybe_to_used_value(basis.inline))
.unwrap_or_default(),
block: self
.block
.maybe_map(|v| v.maybe_to_used_value(basis.block))
.unwrap_or_default(),
}
}
pub(crate) fn percentages_relative_to_basis(
&self,
basis: &LogicalVec2<Au>,
) -> LogicalVec2<Size<Au>> {
LogicalVec2 {
inline: self.inline.map(|value| value.to_used_value(basis.inline)),
block: self.block.map(|value| value.to_used_value(basis.block)),
}
}
}

View file

@ -66,7 +66,7 @@ impl<'a> IndefiniteContainingBlock<'a> {
auto_minimum: &LogicalVec2<Au>,
) -> Self {
let (content_box_size, content_min_size, content_max_size, _) =
style.content_box_sizes_and_padding_border_margin(self);
style.content_box_sizes_and_padding_border_margin_deprecated(self);
let block_size = content_box_size.block.map(|v| {
v.clamp_between_extremums(
content_min_size.block.auto_is(|| auto_minimum.block),

View file

@ -476,7 +476,7 @@ impl HoistedAbsolutelyPositionedBox {
},
IndependentFormattingContext::NonReplaced(non_replaced) => non_replaced
.style
.content_box_size(&indefinite_containing_block, &pbm),
.content_box_size_deprecated(&indefinite_containing_block, &pbm),
};
let shared_fragment = self.fragment.borrow();
@ -561,10 +561,10 @@ impl HoistedAbsolutelyPositionedBox {
// https://drafts.csswg.org/css2/#min-max-heights
let min_size = non_replaced
.style
.content_min_box_size(&indefinite_containing_block, &pbm)
.content_min_box_size_deprecated(&indefinite_containing_block, &pbm)
.map(|t| t.map(Au::from).auto_is(Au::zero));
let max_size = style
.content_max_box_size(&containing_block.into(), &pbm)
.content_max_box_size_deprecated(&containing_block.into(), &pbm)
.map(|t| t.map(Au::from));
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width

View file

@ -427,15 +427,15 @@ impl ReplacedContent {
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Au> {
let box_size = style
.content_box_size(containing_block, pbm)
.content_box_size_deprecated(containing_block, pbm)
// We need to clamp to zero here to obtain the proper aspect
// ratio when box-sizing is border-box and the inner box size
// would otherwise be negative.
.map(|value| value.map(|value| value.max(Au::zero())));
let min_box_size = style
.content_min_box_size(containing_block, pbm)
.content_min_box_size_deprecated(containing_block, pbm)
.auto_is(Au::zero);
let max_box_size = style.content_max_box_size(containing_block, pbm);
let max_box_size = style.content_max_box_size_deprecated(containing_block, pbm);
self.used_size_as_if_inline_element_from_content_box_sizes(
containing_block,
style,

View file

@ -121,7 +121,7 @@ pub(crate) fn outer_inline(
get_content_size: impl FnOnce(&IndefiniteContainingBlock) -> ContentSizes,
) -> ContentSizes {
let (content_box_size, content_min_size, content_max_size, pbm) =
style.content_box_sizes_and_padding_border_margin(containing_block);
style.content_box_sizes_and_padding_border_margin_deprecated(containing_block);
let content_min_size = LogicalVec2 {
inline: content_min_size.inline.auto_is(|| auto_minimum.inline),
block: content_min_size.block.auto_is(|| auto_minimum.block),

View file

@ -16,11 +16,8 @@ use style::properties::ComputedValues;
use style::servo::selector_parser::PseudoElement;
use style::values::computed::basic_shape::ClipPath;
use style::values::computed::image::Image as ComputedImageLayer;
use style::values::computed::{
AlignItems, BorderStyle, LengthPercentage, NonNegativeLengthPercentage, Size,
};
use style::values::computed::{AlignItems, BorderStyle, LengthPercentage};
use style::values::generics::box_::Perspective;
use style::values::generics::length::MaxSize;
use style::values::generics::position::{GenericAspectRatio, PreferredRatio};
use style::values::specified::align::AlignFlags;
use style::values::specified::{box_ as stylo, Overflow};
@ -32,7 +29,7 @@ use crate::dom_traversal::Contents;
use crate::fragment_tree::FragmentFlags;
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides, PhysicalSize,
PhysicalVec,
PhysicalVec, Size,
};
use crate::{ContainingBlock, IndefiniteContainingBlock};
@ -190,48 +187,72 @@ pub(crate) trait ComputedValuesExt {
fn box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<LengthPercentageOrAuto<'_>>;
) -> LogicalVec2<Size<LengthPercentage>>;
fn min_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<LengthPercentageOrAuto<'_>>;
) -> LogicalVec2<Size<LengthPercentage>>;
fn max_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Option<&LengthPercentage>>;
) -> LogicalVec2<Size<LengthPercentage>>;
fn content_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto>;
fn content_box_size_for_box_size(
&self,
box_size: LogicalVec2<AuOrAuto>,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto>;
) -> LogicalVec2<Size<Au>>;
fn content_min_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_min_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto>;
fn content_min_box_size_for_min_size(
&self,
box_size: LogicalVec2<AuOrAuto>,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto>;
) -> LogicalVec2<Size<Au>>;
fn content_max_box_size(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>>;
fn content_max_box_size_deprecated(
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>>;
fn content_max_box_size_for_max_size(
&self,
box_size: LogicalVec2<Option<Au>>,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>>;
) -> LogicalVec2<Size<Au>>;
fn content_box_sizes_and_padding_border_margin(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
PaddingBorderMargin,
);
fn content_box_sizes_and_padding_border_margin_deprecated(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
@ -309,12 +330,12 @@ impl ComputedValuesExt for ComputedValues {
fn box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<LengthPercentageOrAuto<'_>> {
) -> LogicalVec2<Size<LengthPercentage>> {
let position = self.get_position();
LogicalVec2::from_physical_size(
&PhysicalSize::new(
size_to_length(&position.width),
size_to_length(&position.height),
position.clone_width().into(),
position.clone_height().into(),
),
containing_block_writing_mode,
)
@ -323,12 +344,12 @@ impl ComputedValuesExt for ComputedValues {
fn min_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<LengthPercentageOrAuto<'_>> {
) -> LogicalVec2<Size<LengthPercentage>> {
let position = self.get_position();
LogicalVec2::from_physical_size(
&PhysicalSize::new(
size_to_length(&position.min_width),
size_to_length(&position.min_height),
position.clone_min_width().into(),
position.clone_min_height().into(),
),
containing_block_writing_mode,
)
@ -337,20 +358,13 @@ impl ComputedValuesExt for ComputedValues {
fn max_box_size(
&self,
containing_block_writing_mode: WritingMode,
) -> LogicalVec2<Option<&LengthPercentage>> {
fn unwrap(max_size: &MaxSize<NonNegativeLengthPercentage>) -> Option<&LengthPercentage> {
match max_size {
MaxSize::LengthPercentage(length) => Some(&length.0),
MaxSize::None |
MaxSize::MinContent |
MaxSize::MaxContent |
MaxSize::FitContent |
MaxSize::Stretch => None,
}
}
) -> LogicalVec2<Size<LengthPercentage>> {
let position = self.get_position();
LogicalVec2::from_physical_size(
&PhysicalSize::new(unwrap(&position.max_width), unwrap(&position.max_height)),
&PhysicalSize::new(
position.clone_max_width().into(),
position.clone_max_height().into(),
),
containing_block_writing_mode,
)
}
@ -359,18 +373,27 @@ impl ComputedValuesExt for ComputedValues {
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> {
) -> LogicalVec2<Size<Au>> {
let box_size = self
.box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block);
self.content_box_size_for_box_size(box_size, pbm)
}
fn content_box_size_for_box_size(
fn content_box_size_deprecated(
&self,
box_size: LogicalVec2<AuOrAuto>,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> {
self.content_box_size(containing_block, pbm)
.map(Size::to_auto_or)
}
fn content_box_size_for_box_size(
&self,
box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
match self.get_position().box_sizing {
BoxSizing::ContentBox => box_size,
BoxSizing::BorderBox => LogicalVec2 {
@ -390,18 +413,27 @@ impl ComputedValuesExt for ComputedValues {
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> {
) -> LogicalVec2<Size<Au>> {
let box_size = self
.min_box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block);
self.content_min_box_size_for_min_size(box_size, pbm)
}
fn content_min_box_size_for_min_size(
fn content_min_box_size_deprecated(
&self,
min_box_size: LogicalVec2<AuOrAuto>,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<AuOrAuto> {
self.content_min_box_size(containing_block, pbm)
.map(Size::to_auto_or)
}
fn content_min_box_size_for_min_size(
&self,
min_box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
match self.get_position().box_sizing {
BoxSizing::ContentBox => min_box_size,
BoxSizing::BorderBox => LogicalVec2 {
@ -420,7 +452,7 @@ impl ComputedValuesExt for ComputedValues {
&self,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>> {
) -> LogicalVec2<Size<Au>> {
let max_box_size = self
.max_box_size(containing_block.style.writing_mode)
.percentages_relative_to(containing_block);
@ -428,11 +460,20 @@ impl ComputedValuesExt for ComputedValues {
self.content_max_box_size_for_max_size(max_box_size, pbm)
}
fn content_max_box_size_for_max_size(
fn content_max_box_size_deprecated(
&self,
max_box_size: LogicalVec2<Option<Au>>,
containing_block: &ContainingBlock,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Option<Au>> {
self.content_max_box_size(containing_block, pbm)
.map(Size::to_numeric)
}
fn content_max_box_size_for_max_size(
&self,
max_box_size: LogicalVec2<Size<Au>>,
pbm: &PaddingBorderMargin,
) -> LogicalVec2<Size<Au>> {
match self.get_position().box_sizing {
BoxSizing::ContentBox => max_box_size,
BoxSizing::BorderBox => {
@ -454,9 +495,9 @@ impl ComputedValuesExt for ComputedValues {
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
LogicalVec2<Size<Au>>,
PaddingBorderMargin,
) {
// <https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution>
@ -493,6 +534,25 @@ impl ComputedValuesExt for ComputedValues {
(content_box_size, content_min_size, content_max_size, pbm)
}
fn content_box_sizes_and_padding_border_margin_deprecated(
&self,
containing_block: &IndefiniteContainingBlock,
) -> (
LogicalVec2<AuOrAuto>,
LogicalVec2<AuOrAuto>,
LogicalVec2<Option<Au>>,
PaddingBorderMargin,
) {
let (content_box_size, content_min_size, content_max_size, pbm) =
self.content_box_sizes_and_padding_border_margin(containing_block);
(
content_box_size.map(Size::to_auto_or),
content_min_size.map(Size::to_auto_or),
content_max_size.map(Size::to_numeric),
pbm,
)
}
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,
@ -998,15 +1058,6 @@ impl From<stylo::Display> for Display {
}
}
fn size_to_length(size: &Size) -> LengthPercentageOrAuto {
match size {
Size::LengthPercentage(length) => LengthPercentageOrAuto::LengthPercentage(&length.0),
Size::Auto | Size::MinContent | Size::MaxContent | Size::FitContent | Size::Stretch => {
LengthPercentageOrAuto::Auto
},
}
}
pub(crate) trait Clamp: Sized {
fn clamp_below_max(self, max: Option<Self>) -> Self;
fn clamp_between_extremums(self, min: Self, max: Option<Self>) -> Self;

View file

@ -33,8 +33,8 @@ use crate::fragment_tree::{
PositioningFragment,
};
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint,
PhysicalRect, PhysicalSides, ToLogical, ToLogicalWithContainingBlock,
AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides,
Size, SizeKeyword, ToLogical, ToLogicalWithContainingBlock,
};
use crate::positioned::{relative_adjustement, PositioningContext, PositioningContextLength};
use crate::sizing::ContentSizes;
@ -245,9 +245,15 @@ impl<'a> TableLayout<'a> {
layout_context: &LayoutContext,
writing_mode: WritingMode,
) {
// It's not clear whether `inline-size: stretch` allows fixed table mode or not,
// we align with Gecko and Blink.
// <https://github.com/w3c/csswg-drafts/issues/10937>.
let is_in_fixed_mode = self.table.style.get_table().clone_table_layout() ==
TableLayoutMode::Fixed &&
!self.table.style.box_size(writing_mode).inline.is_auto();
!matches!(
self.table.style.box_size(writing_mode).inline,
Size::Keyword(SizeKeyword::Initial | SizeKeyword::MaxContent)
);
let row_measures = vec![LogicalVec2::zero(); self.table.size.width];
self.cell_measures = vec![row_measures; self.table.size.height];
@ -369,8 +375,8 @@ impl<'a> TableLayout<'a> {
self.rows = vec![RowLayout::default(); self.table.size.height];
self.columns = vec![ColumnLayout::default(); self.table.size.width];
let is_length = |size: &LengthPercentageOrAuto| {
size.non_auto().is_some_and(|size| !size.has_percentage())
let is_length = |size: &Size<ComputedLengthPercentage>| {
size.to_numeric().is_some_and(|size| !size.has_percentage())
};
for column_index in 0..self.table.size.width {
@ -837,13 +843,13 @@ impl<'a> TableLayout<'a> {
// This diverges a little from the specification, but should be equivalent
// (other than using the stretch-fit size instead of the containing block width).
let used_width_of_table = match style
.content_box_size(containing_block_for_table, &self.pbm)
.content_box_size_deprecated(containing_block_for_table, &self.pbm)
.inline
{
LengthPercentage(_) => resolved_table_width.max(grid_min_max.min_content),
Auto => {
let min_width: Au = style
.content_min_box_size(containing_block_for_table, &self.pbm)
.content_min_box_size_deprecated(containing_block_for_table, &self.pbm)
.inline
.auto_is(Au::zero);
resolved_table_width
@ -1587,12 +1593,12 @@ impl<'a> TableLayout<'a> {
// the resulting length, properly clamped between min-block-size and max-block-size.
let style = &self.table.style;
let table_height_from_style = match style
.content_box_size(containing_block_for_table, &self.pbm)
.content_box_size_deprecated(containing_block_for_table, &self.pbm)
.block
{
LengthPercentage(_) => containing_block_for_children.block_size,
Auto => style
.content_min_box_size(containing_block_for_table, &self.pbm)
.content_min_box_size_deprecated(containing_block_for_table, &self.pbm)
.block
.map(Au::from),
}
@ -2711,7 +2717,7 @@ impl Table {
.style
.box_size(writing_mode)
.block
.non_auto()
.to_numeric()
.and_then(|size| size.to_length())
.map_or_else(Au::zero, Au::from);
let percentage_contribution =
@ -2841,12 +2847,13 @@ fn get_size_percentage_contribution_from_style(
let max_size = style.max_box_size(writing_mode);
let get_contribution_for_axis =
|size: LengthPercentageOrAuto<'_>, max_size: Option<&ComputedLengthPercentage>| {
|size: Size<ComputedLengthPercentage>, max_size: Size<ComputedLengthPercentage>| {
let size_percentage = size
.non_auto()
.to_numeric()
.and_then(|length_percentage| length_percentage.to_percentage())
.unwrap_or(Percentage(0.));
let max_size_percentage = max_size
.to_numeric()
.and_then(|length_percentage| length_percentage.to_percentage())
.unwrap_or(Percentage(f32::INFINITY));
Percentage(size_percentage.0.min(max_size_percentage.0))
@ -2871,22 +2878,22 @@ fn get_outer_sizes_from_style(
block: size.block.max(padding_border_sums.block),
},
};
let get_size_for_axis = |size: &LengthPercentageOrAuto<'_>| {
size.non_auto()
.and_then(|size| size.to_length())
.map_or_else(Au::zero, Au::from)
};
let get_max_size_for_axis = |size: &Option<&ComputedLengthPercentage>| {
size.and_then(|length_percentage| length_percentage.to_length())
.map_or(MAX_AU, Au::from)
let get_size_for_axis = |size: &Size<ComputedLengthPercentage>| {
// TODO(#32853): This treats all sizing keywords as `auto`.
size.to_numeric()
.and_then(|length_percentage| length_percentage.to_length())
.map(Au::from)
};
let size = style.box_size(writing_mode);
let min_size = style.min_box_size(writing_mode);
let max_size = style.max_box_size(writing_mode);
(
outer_size(size.map(get_size_for_axis)),
outer_size(style.min_box_size(writing_mode).map(get_size_for_axis)),
outer_size(style.max_box_size(writing_mode).map(get_max_size_for_axis)),
size.inline.is_auto(),
outer_size(size.map(|v| get_size_for_axis(v).unwrap_or(Au(0)))),
outer_size(min_size.map(|v| get_size_for_axis(v).unwrap_or(Au(0)))),
outer_size(max_size.map(|v| get_size_for_axis(v).unwrap_or(MAX_AU))),
// TODO(#32853): This treats all sizing keywords as `auto`.
size.inline.is_keyword(),
)
}

View file

@ -565151,7 +565151,7 @@
]
],
"fixed-layout-2.html": [
"dcbabb1ff7460ce563c3f127b9215e7b1211434b",
"3b7f65feccf578e7ead60186bf48517d750428be",
[
null,
{}

View file

@ -1,7 +1,4 @@
[table-width-redistribution-fixed.html]
[table 3]
expected: FAIL
[table 15]
expected: FAIL

View file

@ -1,84 +1,116 @@
<!doctype html>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<link rel='stylesheet' href='./support/base.css' />
<!DOCTYPE html>
<title>table-layout:fixed with various widths</title>
<link rel="author" title="Oriol Brufau" href="obrufau@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-tables-3/#in-fixed-mode">
<main>
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/10937">
<link rel="stylesheet" href="./support/base.css">
<h1>Fixed Layout</h1>
<p>Checks whether fixed layout is implemented properly (width is not definite)</p>
<hr/>
<p>This should be a 100px-wide blue square:</p>
<p>Table-layout:fixed does not apply to width:auto tables</p>
<x-table style="table-layout: fixed; border-spacing: 0px">
<x-tr>
<x-td style="padding: 0; background: blue; height: 100px;"><div style="width: 100px"></div></x-td>
<x-td style="padding: 0"></x-td>
</x-tr>
</x-table>
<hr/>
<p>This should be a 100px-wide blue square:</p>
<p>Table-layout:fixed does not apply to width:max-content tables</p>
<x-table style="table-layout: fixed; width: max-content; border-spacing: 0px">
<x-tr>
<x-td style="padding: 0; background: blue; height: 100px;"><div style="width: 100px"></div></x-td>
<x-td style="padding: 0"></x-td>
</x-tr>
</x-table>
<hr/>
<p>This should be a 100px-wide blue square:</p>
<p>Table-layout:fixed does apply to width:min-content/fit-content tables</p>
<x-table style="table-layout: fixed; width: fit-content; border-spacing: 0px">
<x-tr>
<x-td style="padding: 0; background: blue; height: 50px;"><div style="width: 100px"></div></x-td>
<x-td style="padding: 0"></x-td>
</x-tr>
</x-table>
<x-table style="table-layout: fixed; width: min-content; border-spacing: 0px">
<x-tr>
<x-td style="padding: 0; background: blue; height: 50px;width:100px;"><div style="width: 100px"></div></x-td>
<x-td style="padding: 0;height:50px"><div style="width: 100px"></div></x-td>
</x-tr>
</x-table>
<style>
.wrapper {
width: 0;
}
x-table {
table-layout: fixed;
border-spacing: 0px;
}
x-td:first-child {
padding: 0;
background: cyan;
width: 50px;
height: 50px;
}
x-td + x-td {
padding: 0;
height: 50px;
}
x-td > div {
width: 100px;
}
</style>
<main id="main">
<h1>Fixed Layout</h1>
<p>Checks whether fixed layout is implemented properly</p>
</main>
<template id="template">
<hr>
<p></p>
<p></p>
<div class="wrapper">
<x-table>
<x-tr>
<x-td>
<div></div>
</x-td>
<x-td></x-td>
</x-tr>
</x-table>
</div>
</template>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
while(true) {
var xtd = document.querySelector('x-td[rowspan], x-td[colspan]'); if(!xtd) break;
var td = document.createElement('td'); for(var i = xtd.attributes.length; i--;) { td.setAttribute(xtd.attributes[i].name,xtd.attributes[i].value) }
xtd.parentNode.replaceChild(td,xtd);
let sizeData = {
"10px": true,
"100%": true,
"calc(10px + 100%)": true,
"auto": false,
"min-content": true,
"max-content": false,
"fit-content": true,
"calc-size(any, 10px + 100%)": true,
// These expectations are tentative, see https://github.com/w3c/csswg-drafts/issues/10937
"fit-content(0)": true,
"stretch": true,
// These are non-standard, expect the most popular behavior among the supporting implementations.
"-moz-available": true,
"-webkit-fill-available": true,
"intrinsic": false,
"min-intrinsic": false,
};
function checkSize(size, allowsFixed) {
let fragment = template.content.cloneNode(true);
if (allowsFixed) {
fragment.querySelector("p").textContent = "This should be a 50x50 cyan square:";
fragment.querySelector("p + p").textContent = "Table-layout:fixed does apply to width:" + size + " tables";
} else {
fragment.querySelector("p").textContent = "This should be a 100x50 cyan rectangle:";
fragment.querySelector("p + p").textContent = "Table-layout:fixed does NOT apply to width:" + size + " tables";
}
let table = fragment.querySelector("x-table");
table.style.width = size;
table.querySelector("div").textContent = size;
main.appendChild(fragment);
test(() => {
assert_equals(
getComputedStyle(table).tableLayout,
"fixed",
"The computed value is 'fixed' regardless of whether it applies"
);
if (allowsFixed) {
assert_equals(table.offsetWidth, 50, "Table is in fixed mode");
} else {
assert_equals(table.offsetWidth, 100, "Table is NOT in fixed mode");
}
}, size);
}
generate_tests(assert_equals, [
[
"Table-layout:fixed is not applied when width is auto",
document.querySelector("x-table:nth-of-type(1) > x-tr:first-child > x-td:first-child").offsetWidth,
100
],
[
"Table-layout:fixed reports fixed when width is auto",
getComputedStyle(document.querySelector("x-table:nth-of-type(1)")).tableLayout,
'fixed'
],
[
"Table-layout:fixed is not applied when width is max-content",
document.querySelector("x-table:nth-of-type(2) > x-tr:first-child > x-td:first-child").offsetWidth,
100
],
[
"Table-layout:fixed reports fixed when width is max-content",
getComputedStyle(document.querySelector("x-table:nth-of-type(2)")).tableLayout,
'fixed'
],
[
"Table-layout:fixed is applied when width is min-content",
document.querySelector("x-table:nth-of-type(3) > x-tr:first-child > x-td:first-child").offsetWidth,
document.querySelector("x-table:nth-of-type(4) > x-tr:first-child > x-td:first-child").offsetWidth
]
])
for (let [size, allowsFixed] of Object.entries(sizeData)) {
if (CSS.supports("width", size)) {
checkSize(size, allowsFixed);
// calc-size() should have the same behavior as its basis.
// https://drafts.csswg.org/css-values-5/#calc-size
let calcSize = "calc-size(" + size + ", size)";
if (CSS.supports("width", calcSize)) {
checkSize(calcSize, allowsFixed);
}
}
}
</script>