diff options
author | Oriol Brufau <obrufau@igalia.com> | 2024-09-12 01:50:45 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-11 23:50:45 +0000 |
commit | d9be9d6bd464c664e7ddad86937a9aa54a6c7baf (patch) | |
tree | 350b21d5917f60db261aae34c0e9716db818c7db /components/layout_2020/flow | |
parent | 777fb81260ed10e016370dcd83fc750367e97535 (diff) | |
download | servo-d9be9d6bd464c664e7ddad86937a9aa54a6c7baf.tar.gz servo-d9be9d6bd464c664e7ddad86937a9aa54a6c7baf.zip |
Handle all `white-space` values when intrinsically sizing an IFC (#33343)
There were various cases like `text-wrap-mode: nowrap` and
`white-space-collapse: break-spaces` that weren't handled well.
Fixes #33335
flexbox_flex-formatting-interop.html fails now because we don't support
`table-layout: fixed`.
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components/layout_2020/flow')
-rw-r--r-- | components/layout_2020/flow/inline/mod.rs | 68 | ||||
-rw-r--r-- | components/layout_2020/flow/inline/text_run.rs | 15 |
2 files changed, 52 insertions, 31 deletions
diff --git a/components/layout_2020/flow/inline/mod.rs b/components/layout_2020/flow/inline/mod.rs index 8653a3dff8c..22fc1a84173 100644 --- a/components/layout_2020/flow/inline/mod.rs +++ b/components/layout_2020/flow/inline/mod.rs @@ -2275,11 +2275,14 @@ struct ContentSizesComputation<'layout_data> { containing_block: &'layout_data IndefiniteContainingBlock<'layout_data>, paragraph: ContentSizes, current_line: ContentSizes, - /// Size for whitepsace pending to be added to this line. - pending_whitespace: Au, + /// Size for whitespace pending to be added to this line. + pending_whitespace: ContentSizes, + /// Whether or not the current line has seen any content (excluding collapsed whitespace), + /// when sizing under a min-content constraint. + had_content_yet_for_min_content: bool, /// Whether or not the current line has seen any content (excluding collapsed whitespace), /// when sizing under a max-content constraint. - had_content_yet: bool, + had_content_yet_for_max_content: bool, /// Stack of ending padding, margin, and border to add to the length /// when an inline box finishes. ending_inline_pbm_stack: Vec<Au>, @@ -2345,6 +2348,8 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { for run in segment.runs.iter() { let advance = run.glyph_store.total_advance(); + let style_text = text_run.parent_style.get_inherited_text(); + let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap; if run.glyph_store.is_whitespace() { // If this run is a forced line break, we *must* break the line @@ -2354,31 +2359,38 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { self.current_line = ContentSizes::zero(); continue; } - - let style_text = text_run.parent_style.get_inherited_text(); - if style_text.white_space_collapse != WhiteSpaceCollapse::Preserve { - // TODO: need to handle TextWrapMode::Nowrap. - self.line_break_opportunity(); - // Discard any leading whitespace in the line. This will always be trimmed. - if self.had_content_yet { - // Wait to take into account other whitespace until we see more content. - // Whitespace at the end of the line will always be trimmed. - self.pending_whitespace += advance; + if !matches!( + 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_max_content { + self.pending_whitespace.max_content += advance; } continue; } - if style_text.text_wrap_mode == TextWrapMode::Wrap { + if can_wrap { + self.pending_whitespace.max_content += advance; self.commit_pending_whitespace(); self.line_break_opportunity(); - self.current_line.max_content += advance; - self.had_content_yet = true; continue; } } self.commit_pending_whitespace(); self.add_inline_size(advance); - self.had_content_yet = true; + + // Typically whitespace glyphs are placed in a separate store, + // but for `white-space: break-spaces` we place the first whitespace + // with the preceding text. That prevents a line break before that + // first space, but we still need to allow a line break after it. + if can_wrap && run.glyph_store.ends_with_whitespace() { + self.line_break_opportunity(); + } } } }, @@ -2405,7 +2417,6 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { self.commit_pending_whitespace(); self.current_line += outer; - self.had_content_yet = true; }, _ => {}, } @@ -2417,9 +2428,14 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { } fn line_break_opportunity(&mut self) { + // Clear the pending whitespace, assuming that at the end of the line + // it needs to either hang or be removed. If that isn't the case, + // `commit_pending_whitespace()` should be called first. + self.pending_whitespace.min_content = Au::zero(); self.paragraph.min_content = std::cmp::max(self.paragraph.min_content, self.current_line.min_content); self.current_line.min_content = Au::zero(); + self.had_content_yet_for_min_content = false; } fn forced_line_break(&mut self) { @@ -2427,15 +2443,14 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { self.paragraph.max_content = std::cmp::max(self.paragraph.max_content, self.current_line.max_content); self.current_line.max_content = Au::zero(); - self.had_content_yet = false; + self.had_content_yet_for_min_content = false; + self.had_content_yet_for_max_content = false; } fn commit_pending_whitespace(&mut self) { - // Only add the pending whitespace to the max-content size, because for the min-content - // we should wrap lines wherever is possible, so wrappable spaces shouldn't increase - // the length of the line (they will just be removed or hang at the end of the line). - self.current_line.max_content += self.pending_whitespace; - self.pending_whitespace = Au::zero(); + self.current_line += mem::take(&mut self.pending_whitespace); + self.had_content_yet_for_min_content = true; + self.had_content_yet_for_max_content = true; } /// Compute the [`ContentSizes`] of the given [`InlineFormattingContext`]. @@ -2449,8 +2464,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { containing_block, paragraph: ContentSizes::zero(), current_line: ContentSizes::zero(), - pending_whitespace: Au::zero(), - had_content_yet: false, + pending_whitespace: ContentSizes::zero(), + had_content_yet_for_min_content: false, + had_content_yet_for_max_content: false, ending_inline_pbm_stack: Vec::new(), } .traverse(inline_formatting_context) diff --git a/components/layout_2020/flow/inline/text_run.rs b/components/layout_2020/flow/inline/text_run.rs index e9b9385d23f..250a7591248 100644 --- a/components/layout_2020/flow/inline/text_run.rs +++ b/components/layout_2020/flow/inline/text_run.rs @@ -217,6 +217,8 @@ impl TextRunSegment { continue; } + let mut options = *shaping_options; + // Extend the slice to the next UAX#14 line break opportunity. let mut slice = last_slice.end..*break_index; let word = &formatting_context_text[slice.clone()]; @@ -247,6 +249,9 @@ impl TextRunSegment { !can_break_anywhere { whitespace.start += first_white_space_character.len_utf8(); + options + .flags + .insert(ShapingFlags::ENDS_WITH_WHITESPACE_SHAPING_FLAG); } slice.end = whitespace.start; @@ -267,17 +272,17 @@ impl TextRunSegment { // Push the non-whitespace part of the range. if !slice.is_empty() { - self.shape_and_push_range(&slice, formatting_context_text, &font, shaping_options); + self.shape_and_push_range(&slice, formatting_context_text, &font, &options); } if whitespace.is_empty() { continue; } - let mut options = *shaping_options; - options - .flags - .insert(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG); + options.flags.insert( + ShapingFlags::IS_WHITESPACE_SHAPING_FLAG | + ShapingFlags::ENDS_WITH_WHITESPACE_SHAPING_FLAG, + ); // If `white-space-collapse: break-spaces` is active, insert a line breaking opportunity // between each white space character in the white space that we trimmed off. |