From c11670b0674bf34854cad05d782569de62e16e35 Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Sat, 13 Sep 2025 14:20:39 +0800 Subject: [PATCH] layout: Take `text-indent` into account in min/max-content inline sizes (#39230) The min-content and max-content inline sizes of an inline formatting contentext need to take `text-indent` into account. Note it can be set to a negative amount, so the `ContentSizesComputation` logic needs some tweaks to handle it well. Testing: Fixes various WPT tests Signed-off-by: Oriol Brufau --- components/layout/flow/inline/mod.rs | 65 +++++++++++-------- .../text/text-indent-applies-to-003.xht.ini | 2 - .../text/text-indent-intrinsic-001.xht.ini | 2 - .../text/text-indent-intrinsic-002.xht.ini | 2 - .../text/text-indent-intrinsic-003.xht.ini | 2 - .../text/text-indent-intrinsic-004.xht.ini | 2 - .../text-indent-min-max-content-001.html.ini | 2 - .../calc-text-indent-intrinsic-1.html.ini | 2 - 8 files changed, 38 insertions(+), 41 deletions(-) delete mode 100644 tests/wpt/meta/css/CSS2/text/text-indent-applies-to-003.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-001.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-002.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-003.xht.ini delete mode 100644 tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-004.xht.ini delete mode 100644 tests/wpt/meta/css/css-text/text-indent/text-indent-min-max-content-001.html.ini delete mode 100644 tests/wpt/meta/css/css-values/calc-text-indent-intrinsic-1.html.ini diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index 134af57e0cd..fef5a74d598 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -129,7 +129,7 @@ use crate::layout_box_base::LayoutBoxBase; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; -use crate::{ConstraintSpace, ContainingBlock, SharedStyle}; +use crate::{ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, SharedStyle}; // From gfxFontConstants.h in Firefox. static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; @@ -1725,6 +1725,21 @@ impl InlineFormattingContext { *self.shared_inline_styles.selected.borrow_mut() = node.selected_style(); } + pub(crate) fn inline_start_for_first_line( + &self, + containing_block: IndefiniteContainingBlock, + ) -> Au { + if !self.has_first_formatted_line { + return Au::zero(); + } + containing_block + .style + .get_inherited_text() + .text_indent + .length + .to_used_value(containing_block.size.inline.unwrap_or_default()) + } + pub(super) fn layout( &self, layout_context: &LayoutContext, @@ -1733,17 +1748,6 @@ impl InlineFormattingContext { sequential_layout_state: Option<&mut SequentialLayoutState>, collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin, ) -> CacheableLayoutResult { - let first_line_inline_start = if self.has_first_formatted_line { - containing_block - .style - .get_inherited_text() - .text_indent - .length - .to_used_value(containing_block.size.inline) - } else { - Au::zero() - }; - // Clear any cached inline fragments from previous layouts. for inline_box in self.inline_boxes.iter() { inline_box.borrow().base.clear_fragments(); @@ -1775,7 +1779,7 @@ impl InlineFormattingContext { ifc: self, fragments: Vec::new(), current_line: LineUnderConstruction::new(LogicalVec2 { - inline: first_line_inline_start, + inline: self.inline_start_for_first_line(containing_block.into()), block: Au::zero(), }), root_nesting_level: InlineContainerState::new( @@ -2371,6 +2375,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { mut self, inline_formatting_context: &InlineFormattingContext, ) -> InlineContentSizesResult { + self.add_inline_size( + inline_formatting_context.inline_start_for_first_line(self.constraint_space.into()), + ); for inline_item in inline_formatting_context.inline_items.iter() { self.process_item(&inline_item.borrow(), inline_formatting_context); } @@ -2430,7 +2437,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { // TODO: This should take account whether or not the first and last character prevent // linebreaks after atomics as in layout. - if can_wrap && segment.break_at_start { + if can_wrap && segment.break_at_start && self.had_content_yet_for_min_content { self.line_break_opportunity() } @@ -2447,10 +2454,12 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { style_text.white_space_collapse, WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces ) { - if can_wrap { - self.line_break_opportunity(); - } else if self.had_content_yet_for_min_content { - self.pending_whitespace.min_content += advance; + if self.had_content_yet_for_min_content { + if can_wrap { + self.line_break_opportunity(); + } else { + self.pending_whitespace.min_content += advance; + } } if self.had_content_yet_for_max_content { self.pending_whitespace.max_content += advance; @@ -2479,13 +2488,6 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { } }, InlineItem::Atomic(atomic, offset_in_text, _level) => { - // TODO: need to handle TextWrapMode::Nowrap. - if !inline_formatting_context - .previous_character_prevents_soft_wrap_opportunity(*offset_in_text) - { - self.line_break_opportunity(); - } - let InlineContentSizesResult { sizes: outer, depends_on_block_constraints, @@ -2497,14 +2499,23 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { ); self.depends_on_block_constraints |= depends_on_block_constraints; - if !inline_formatting_context - .next_character_prevents_soft_wrap_opportunity(*offset_in_text) + // TODO: need to handle TextWrapMode::Nowrap. + if self.had_content_yet_for_min_content && + !inline_formatting_context + .previous_character_prevents_soft_wrap_opportunity(*offset_in_text) { self.line_break_opportunity(); } self.commit_pending_whitespace(); self.current_line += outer; + + // TODO: need to handle TextWrapMode::Nowrap. + if !inline_formatting_context + .next_character_prevents_soft_wrap_opportunity(*offset_in_text) + { + self.line_break_opportunity(); + } }, _ => {}, } diff --git a/tests/wpt/meta/css/CSS2/text/text-indent-applies-to-003.xht.ini b/tests/wpt/meta/css/CSS2/text/text-indent-applies-to-003.xht.ini deleted file mode 100644 index e5e48e9d9ee..00000000000 --- a/tests/wpt/meta/css/CSS2/text/text-indent-applies-to-003.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[text-indent-applies-to-003.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-001.xht.ini b/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-001.xht.ini deleted file mode 100644 index c2191a4d2b0..00000000000 --- a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-001.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[text-indent-intrinsic-001.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-002.xht.ini b/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-002.xht.ini deleted file mode 100644 index dc6d60b0a1e..00000000000 --- a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-002.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[text-indent-intrinsic-002.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-003.xht.ini b/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-003.xht.ini deleted file mode 100644 index 147d3c0b485..00000000000 --- a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-003.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[text-indent-intrinsic-003.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-004.xht.ini b/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-004.xht.ini deleted file mode 100644 index d7f0f80471d..00000000000 --- a/tests/wpt/meta/css/CSS2/text/text-indent-intrinsic-004.xht.ini +++ /dev/null @@ -1,2 +0,0 @@ -[text-indent-intrinsic-004.xht] - expected: FAIL diff --git a/tests/wpt/meta/css/css-text/text-indent/text-indent-min-max-content-001.html.ini b/tests/wpt/meta/css/css-text/text-indent/text-indent-min-max-content-001.html.ini deleted file mode 100644 index 079e8009d6e..00000000000 --- a/tests/wpt/meta/css/css-text/text-indent/text-indent-min-max-content-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[text-indent-min-max-content-001.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-values/calc-text-indent-intrinsic-1.html.ini b/tests/wpt/meta/css/css-values/calc-text-indent-intrinsic-1.html.ini deleted file mode 100644 index d7100617c74..00000000000 --- a/tests/wpt/meta/css/css-values/calc-text-indent-intrinsic-1.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[calc-text-indent-intrinsic-1.html] - expected: FAIL