Allow pre-wrap whitespace to hang at the end of the line (#31681)

* Allow pre-wrap whitespace to hang at the end of the line

* Use bitflags
This commit is contained in:
Oriol Brufau 2024-03-15 17:12:41 +01:00 committed by GitHub
parent ac24cd6139
commit 39f660f520
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 54 additions and 64 deletions

View file

@ -72,6 +72,7 @@ use std::cell::OnceCell;
use std::mem; use std::mem;
use app_units::Au; use app_units::Au;
use bitflags::bitflags;
use gfx::font::FontMetrics; use gfx::font::FontMetrics;
use gfx::text::glyph::GlyphStore; use gfx::text::glyph::GlyphStore;
use serde::Serialize; use serde::Serialize;
@ -1215,7 +1216,11 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
.current_inline_container_state() .current_inline_container_state()
.strut_block_sizes .strut_block_sizes
.clone(); .clone();
self.update_unbreakable_segment_for_new_content(&strut_size, Length::zero(), false); self.update_unbreakable_segment_for_new_content(
&strut_size,
Length::zero(),
SegmentContentFlags::empty(),
);
} }
self.had_inflow_content = true; self.had_inflow_content = true;
@ -1243,12 +1248,11 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
font_index: usize, font_index: usize,
) { ) {
let inline_advance = Length::from(glyph_store.total_advance()); let inline_advance = Length::from(glyph_store.total_advance());
let preserve_spaces = text_run let flags = if glyph_store.is_whitespace() {
.parent_style SegmentContentFlags::from(text_run.parent_style.get_inherited_text().white_space)
.get_inherited_text() } else {
.white_space SegmentContentFlags::empty()
.preserve_spaces(); };
let is_collapsible_whitespace = glyph_store.is_whitespace() && !preserve_spaces;
// If the metrics of this font don't match the default font, we are likely using a fallback // If the metrics of this font don't match the default font, we are likely using a fallback
// font and need to adjust the line size to account for a potentially different font. // font and need to adjust the line size to account for a potentially different font.
@ -1270,7 +1274,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
container_state.get_block_size_contribution(vertical_align, &font_metrics); container_state.get_block_size_contribution(vertical_align, &font_metrics);
block_size.adjust_for_baseline_offset(container_state.baseline_offset); block_size.adjust_for_baseline_offset(container_state.baseline_offset);
block_size block_size
} else if quirks_mode && !is_collapsible_whitespace { } else if quirks_mode && !flags.is_collapsible_whitespace() {
// Normally, the strut is incorporated into the nested block size. In quirks mode though // Normally, the strut is incorporated into the nested block size. In quirks mode though
// if we find any text that isn't collapsed whitespace, we need to incorporate the strut. // if we find any text that isn't collapsed whitespace, we need to incorporate the strut.
// TODO(mrobinson): This isn't quite right for situations where collapsible white space // TODO(mrobinson): This isn't quite right for situations where collapsible white space
@ -1281,11 +1285,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
} else { } else {
LineBlockSizes::zero() LineBlockSizes::zero()
}; };
self.update_unbreakable_segment_for_new_content( self.update_unbreakable_segment_for_new_content(&strut_size, inline_advance, flags);
&strut_size,
inline_advance,
is_collapsible_whitespace,
);
match self.current_line_segment.line_items.last_mut() { match self.current_line_segment.line_items.last_mut() {
Some(LineItem::TextRun(line_item)) if ifc_font_info.key == line_item.font_key => { Some(LineItem::TextRun(line_item)) if ifc_font_info.key == line_item.font_key => {
@ -1309,14 +1309,16 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
&mut self, &mut self,
block_sizes_of_content: &LineBlockSizes, block_sizes_of_content: &LineBlockSizes,
inline_size: Length, inline_size: Length,
is_collapsible_whitespace: bool, flags: SegmentContentFlags,
) { ) {
if !is_collapsible_whitespace { if flags.is_collapsible_whitespace() || flags.is_wrappable_whitespace() {
self.current_line_segment.trailing_whitespace_size = inline_size;
} else {
self.current_line_segment.trailing_whitespace_size = Length::zero(); self.current_line_segment.trailing_whitespace_size = Length::zero();
}
if !flags.is_collapsible_whitespace() {
self.current_line_segment.has_content = true; self.current_line_segment.has_content = true;
self.had_inflow_content = true; self.had_inflow_content = true;
} else {
self.current_line_segment.trailing_whitespace_size = inline_size;
} }
// This may or may not include the size of the strut depending on the quirks mode setting. // This may or may not include the size of the strut depending on the quirks mode setting.
@ -1442,6 +1444,36 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
} }
} }
bitflags! {
pub struct SegmentContentFlags: u8 {
const COLLAPSIBLE_WHITESPACE = 0b00000001;
const WRAPPABLE_WHITESPACE = 0b00000010;
}
}
impl SegmentContentFlags {
fn is_collapsible_whitespace(&self) -> bool {
self.contains(Self::COLLAPSIBLE_WHITESPACE)
}
fn is_wrappable_whitespace(&self) -> bool {
self.contains(Self::WRAPPABLE_WHITESPACE)
}
}
impl From<WhiteSpace> for SegmentContentFlags {
fn from(white_space: WhiteSpace) -> Self {
let mut flags = Self::empty();
if !white_space.preserve_spaces() {
flags.insert(Self::COLLAPSIBLE_WHITESPACE);
}
if white_space.allow_wrap() {
flags.insert(Self::WRAPPABLE_WHITESPACE);
}
flags
}
}
enum InlineFormattingContextIterItem<'a> { enum InlineFormattingContextIterItem<'a> {
Item(&'a mut InlineLevelBox), Item(&'a mut InlineLevelBox),
EndInlineBox, EndInlineBox,
@ -2088,7 +2120,11 @@ impl IndependentFormattingContext {
let (block_sizes, baseline_offset_in_parent) = let (block_sizes, baseline_offset_in_parent) =
self.get_block_sizes_and_baseline_offset(ifc, size.block, baseline_offset); self.get_block_sizes_and_baseline_offset(ifc, size.block, baseline_offset);
ifc.update_unbreakable_segment_for_new_content(&block_sizes, size.inline, false); ifc.update_unbreakable_segment_for_new_content(
&block_sizes,
size.inline,
SegmentContentFlags::empty(),
);
ifc.push_line_item_to_unbreakable_segment(LineItem::Atomic(AtomicLineItem { ifc.push_line_item_to_unbreakable_segment(LineItem::Atomic(AtomicLineItem {
fragment, fragment,
size, size,

View file

@ -1,2 +0,0 @@
[hyphens-auto-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[control-chars-00D.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[eol-spaces-bidi-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-003.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-004.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-005.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-006.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-007.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-015.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-align-left-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-align-start-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-leading-spaces-012.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[pre-wrap-tab-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-003.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-004.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-005.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-006.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[textarea-pre-wrap-007.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[white-space-pre-wrap.htm]
expected: FAIL