Resolve cyclic margin and padding percentages against zero (#30085)

From https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution

> For the min size properties, as well as for margins and paddings
> (and gutters), a cyclic percentage is resolved against zero
> for determining intrinsic size contributions.
This commit is contained in:
Oriol Brufau 2023-08-10 13:38:44 +02:00 committed by GitHub
parent 8dceb8e412
commit c264993da8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 79 deletions

View file

@ -27,7 +27,7 @@ use servo_arc::Arc;
use style::computed_values::white_space::T as WhiteSpace; use style::computed_values::white_space::T as WhiteSpace;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, LengthPercentage, Percentage}; use style::values::computed::Length;
use style::values::generics::text::LineHeight; use style::values::generics::text::LineHeight;
use style::values::specified::text::TextAlignKeyword; use style::values::specified::text::TextAlignKeyword;
use style::values::specified::text::TextDecorationLine; use style::values::specified::text::TextDecorationLine;
@ -251,7 +251,6 @@ impl InlineFormattingContext {
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
paragraph: ContentSizes, paragraph: ContentSizes,
current_line: ContentSizes, current_line: ContentSizes,
current_line_percentages: Percentage,
/// Size for whitepsace pending to be added to this line. /// Size for whitepsace pending to be added to this line.
pending_whitespace: Length, pending_whitespace: Length,
/// Whether or not this IFC has seen any non-whitespace content. /// Whether or not this IFC has seen any non-whitespace content.
@ -274,11 +273,15 @@ impl InlineFormattingContext {
macro_rules! add { macro_rules! add {
($condition: ident, $side: ident) => { ($condition: ident, $side: ident) => {
if inline_box.$condition { if inline_box.$condition {
self.add_lengthpercentage(padding.$side); // For margins and paddings, a cyclic percentage is resolved against zero
self.add_length(border.$side); // for determining intrinsic size contributions.
// https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
let zero = Length::zero();
let mut length = padding.$side.percentage_relative_to(zero) + border.$side;
if let Some(lp) = margin.$side.non_auto() { if let Some(lp) = margin.$side.non_auto() {
self.add_lengthpercentage(lp) length += lp.percentage_relative_to(zero)
} }
self.add_length(length);
} }
}; };
} }
@ -320,7 +323,7 @@ impl InlineFormattingContext {
} }
}, },
InlineLevelBox::Atomic(atomic) => { InlineLevelBox::Atomic(atomic) => {
let (outer, pc) = atomic.outer_inline_content_sizes_and_percentages( let outer = atomic.outer_inline_content_sizes(
self.layout_context, self.layout_context,
self.containing_block_writing_mode, self.containing_block_writing_mode,
); );
@ -328,7 +331,6 @@ impl InlineFormattingContext {
self.current_line.min_content += self.current_line.min_content +=
self.pending_whitespace + outer.min_content; self.pending_whitespace + outer.min_content;
self.current_line.max_content += outer.max_content; self.current_line.max_content += outer.max_content;
self.current_line_percentages += pc;
self.pending_whitespace = Length::zero(); self.pending_whitespace = Length::zero();
self.had_non_whitespace_content_yet = true; self.had_non_whitespace_content_yet = true;
}, },
@ -338,15 +340,6 @@ impl InlineFormattingContext {
} }
} }
fn add_lengthpercentage(&mut self, lp: &LengthPercentage) {
if let Some(l) = lp.to_length() {
self.add_length(l);
}
if let Some(p) = lp.to_percentage() {
self.current_line_percentages += p;
}
}
fn add_length(&mut self, l: Length) { fn add_length(&mut self, l: Length) {
self.current_line.min_content += l; self.current_line.min_content += l;
self.current_line.max_content += l; self.current_line.max_content += l;
@ -360,8 +353,6 @@ impl InlineFormattingContext {
fn forced_line_break(&mut self) { fn forced_line_break(&mut self) {
self.line_break_opportunity(); self.line_break_opportunity();
self.current_line
.adjust_for_pbm_percentages(take(&mut self.current_line_percentages));
self.paragraph self.paragraph
.max_content .max_content
.max_assign(take(&mut self.current_line.max_content)); .max_assign(take(&mut self.current_line.max_content));
@ -375,7 +366,6 @@ impl InlineFormattingContext {
containing_block_writing_mode, containing_block_writing_mode,
paragraph: ContentSizes::zero(), paragraph: ContentSizes::zero(),
current_line: ContentSizes::zero(), current_line: ContentSizes::zero(),
current_line_percentages: Percentage::zero(),
pending_whitespace: Length::zero(), pending_whitespace: Length::zero(),
had_non_whitespace_content_yet: false, had_non_whitespace_content_yet: false,
linebreaker: None, linebreaker: None,

View file

@ -17,7 +17,7 @@ use servo_arc::Arc;
use std::convert::TryInto; use std::convert::TryInto;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, Percentage}; use style::values::computed::Length;
use style::values::specified::text::TextDecorationLine; use style::values::specified::text::TextDecorationLine;
/// https://drafts.csswg.org/css-display/#independent-formatting-context /// https://drafts.csswg.org/css-display/#independent-formatting-context
@ -151,25 +151,12 @@ impl IndependentFormattingContext {
layout_context: &LayoutContext, layout_context: &LayoutContext,
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
) -> ContentSizes { ) -> ContentSizes {
let (mut outer, percentages) = self.outer_inline_content_sizes_and_percentages(
layout_context,
containing_block_writing_mode,
);
outer.adjust_for_pbm_percentages(percentages);
outer
}
pub fn outer_inline_content_sizes_and_percentages(
&mut self,
layout_context: &LayoutContext,
containing_block_writing_mode: WritingMode,
) -> (ContentSizes, Percentage) {
match self { match self {
Self::NonReplaced(non_replaced) => { Self::NonReplaced(non_replaced) => {
let style = &non_replaced.style; let style = &non_replaced.style;
let content_sizes = &mut non_replaced.content_sizes; let content_sizes = &mut non_replaced.content_sizes;
let contents = &non_replaced.contents; let contents = &non_replaced.contents;
sizing::outer_inline_and_percentages(&style, containing_block_writing_mode, || { sizing::outer_inline(&style, containing_block_writing_mode, || {
content_sizes content_sizes
.get_or_insert_with(|| { .get_or_insert_with(|| {
contents.inline_content_sizes(layout_context, style.writing_mode) contents.inline_content_sizes(layout_context, style.writing_mode)
@ -177,11 +164,11 @@ impl IndependentFormattingContext {
.clone() .clone()
}) })
}, },
Self::Replaced(replaced) => sizing::outer_inline_and_percentages( Self::Replaced(replaced) => {
&replaced.style, sizing::outer_inline(&replaced.style, containing_block_writing_mode, || {
containing_block_writing_mode, replaced.contents.inline_content_sizes(&replaced.style)
|| replaced.contents.inline_content_sizes(&replaced.style), })
), },
} }
} }
} }

View file

@ -8,7 +8,7 @@ use crate::style_ext::ComputedValuesExt;
use style::logical_geometry::WritingMode; use style::logical_geometry::WritingMode;
use style::properties::longhands::box_sizing::computed_value::T as BoxSizing; use style::properties::longhands::box_sizing::computed_value::T as BoxSizing;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::values::computed::{Length, LengthPercentage, Percentage}; use style::values::computed::Length;
use style::Zero; use style::Zero;
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
@ -46,19 +46,6 @@ impl ContentSizes {
max_content: self.max_content + other.max_content, max_content: self.max_content + other.max_content,
} }
} }
/// Relevant to outer intrinsic inline sizes, for percentages from padding and margin.
pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) {
// " Note that this may yield an infinite result, but undefined results
// (zero divided by zero) must be treated as zero. "
if self.max_content.px() == 0. {
// Avoid a potential `NaN`.
// Zero is already the result we want regardless of `denominator`.
} else {
let denominator = (1. - percentages.0).max(0.);
self.max_content = Length::new(self.max_content.px() / denominator);
}
}
} }
impl ContentSizes { impl ContentSizes {
@ -73,34 +60,23 @@ pub(crate) fn outer_inline(
containing_block_writing_mode: WritingMode, containing_block_writing_mode: WritingMode,
get_content_size: impl FnOnce() -> ContentSizes, get_content_size: impl FnOnce() -> ContentSizes,
) -> ContentSizes { ) -> ContentSizes {
let (mut outer, percentages) =
outer_inline_and_percentages(style, containing_block_writing_mode, get_content_size);
outer.adjust_for_pbm_percentages(percentages);
outer
}
pub(crate) fn outer_inline_and_percentages(
style: &ComputedValues,
containing_block_writing_mode: WritingMode,
get_content_size: impl FnOnce() -> ContentSizes,
) -> (ContentSizes, Percentage) {
let padding = style.padding(containing_block_writing_mode); let padding = style.padding(containing_block_writing_mode);
let border = style.border_width(containing_block_writing_mode); let border = style.border_width(containing_block_writing_mode);
let margin = style.margin(containing_block_writing_mode); let margin = style.margin(containing_block_writing_mode);
let mut pbm_percentages = Percentage::zero(); // For margins and paddings, a cyclic percentage is resolved against zero
let mut decompose = |x: &LengthPercentage| { // for determining intrinsic size contributions.
pbm_percentages += x.to_percentage().unwrap_or_else(Zero::zero); // https://drafts.csswg.org/css-sizing-3/#min-percentage-contribution
x.to_length().unwrap_or_else(Zero::zero) let zero = Length::zero();
}; let pb_lengths = border.inline_sum() +
let pb_lengths = padding.inline_start.percentage_relative_to(zero) +
border.inline_sum() + decompose(padding.inline_start) + decompose(padding.inline_end); padding.inline_end.percentage_relative_to(zero);
let mut m_lengths = Length::zero(); let mut m_lengths = zero;
if let Some(m) = margin.inline_start.non_auto() { if let Some(m) = margin.inline_start.non_auto() {
m_lengths += decompose(m) m_lengths += m.percentage_relative_to(zero)
} }
if let Some(m) = margin.inline_end.non_auto() { if let Some(m) = margin.inline_end.non_auto() {
m_lengths += decompose(m) m_lengths += m.percentage_relative_to(zero)
} }
let box_sizing = style.get_position().box_sizing; let box_sizing = style.get_position().box_sizing;
@ -114,7 +90,7 @@ pub(crate) fn outer_inline_and_percentages(
.min_box_size(containing_block_writing_mode) .min_box_size(containing_block_writing_mode)
.inline .inline
// Percentages for 'min-width' are treated as zero // Percentages for 'min-width' are treated as zero
.percentage_relative_to(Length::zero()) .percentage_relative_to(zero)
// FIXME: 'auto' is not zero in Flexbox // FIXME: 'auto' is not zero in Flexbox
.auto_is(Length::zero); .auto_is(Length::zero);
let max_inline_size = style let max_inline_size = style
@ -145,6 +121,5 @@ pub(crate) fn outer_inline_and_percentages(
}), }),
}; };
let outer = border_box_sizes.map(|s| s + m_lengths); border_box_sizes.map(|s| s + m_lengths)
(outer, pbm_percentages)
} }

View file

@ -1,2 +0,0 @@
[intrinsic-size-with-negative-margins.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[flexbox_align-items-stretch-3.html]
expected: FAIL