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

@ -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)),
}
}
}