Skip to content

Commit

Permalink
Cleanup and implement line-height list item quirk quirk
Browse files Browse the repository at this point in the history
  • Loading branch information
mrobinson committed Dec 20, 2023
1 parent 9f5b5dc commit 98e47d1
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 108 deletions.
2 changes: 1 addition & 1 deletion components/layout_2020/display_list/stacking_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,7 @@ impl BoxFragment {
) -> Option<(ScrollTreeNodeId, wr::ClipChainId, LayoutSize)> {
let overflow_x = self.style.get_box().overflow_x;
let overflow_y = self.style.get_box().overflow_y;
if overflow_x == ComputedOverflow::Visible && overflow_y == ComputedOverflow::Visible {
if !self.style.establishes_scroll_container() {
return None;
}

Expand Down
2 changes: 1 addition & 1 deletion components/layout_2020/flexbox/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ impl FlexLine<'_> {
logical_slides(flex_context, item.padding),
logical_slides(flex_context, item.border),
margin,
None, /* last_inflow_baseline_offset */
None, /* clearance */
// TODO: We should likely propagate baselines from `display: flex`.
None, /* last_inflow_baseline_offset */
collapsed_margin,
Expand Down
217 changes: 116 additions & 101 deletions components/layout_2020/flow/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use style::values::computed::{Length, LengthPercentage};
use style::values::generics::box_::{GenericVerticalAlign, VerticalAlignKeyword};
use style::values::generics::text::LineHeight;
use style::values::specified::text::{TextAlignKeyword, TextDecorationLine};
use style::values::specified::{box_ as stylo, Overflow, TextJustify};
use style::values::specified::{box_ as stylo, TextJustify};
use style::Zero;
use webrender_api::FontInstanceKey;
use xi_unicode::{linebreak_property, LineBreakLeafIter};
Expand Down Expand Up @@ -280,13 +280,11 @@ impl LineBlockSizes {
/// > solutions and CSS 2 does not define the position of the line box's baseline (i.e.,
/// > the position of the strut, see below).
fn find_baseline_offset(&self) -> Length {
// TODO: What stuff goes on when top/bottom group is taller than the baseline-relative group?
//println!("=====> CALCULATING LEADING: {self:?}");
//println!("=====> {leading:?} final: {:?}",
// leading.scale_by(0.5) + self.size_for_baseline_positioning.ascent.into());
match self.baseline_relative_size_for_line_height.as_ref() {
Some(size) => size.ascent.into(),
None => {
// This is the case mentinoned above where there are multiple solutions.
// This code is putting the baseline roughly in the middle of the line.
let leading = self.resolve() -
(self.size_for_baseline_positioning.ascent +
self.size_for_baseline_positioning.descent)
Expand Down Expand Up @@ -758,14 +756,12 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {

let line_had_content =
!fragments.is_empty() || state.positioning_context.len() != positioning_context_length;
//println!(" -> fragments: {:?}", fragments.len());

// If the line doesn't have any fragments, we don't need to add a containing fragment for it.
if !line_had_content {
return;
}

//println!(" FINAL SIZE CONTRIBUTION: {:?}\n\n", effective_block_advance);
self.last_baseline_offset = Some(baseline_offset + block_start_position);
let line_rect = LogicalRect {
// The inline part of this start offset was taken into account when determining
Expand Down Expand Up @@ -1116,75 +1112,70 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
font_metrics: &FontMetrics,
font_key: FontInstanceKey,
) {
let inline_advance = Length::from(glyph_store.total_advance());

let is_non_preserved_whitespace = glyph_store.is_whitespace() &&
!parent_style
.get_inherited_text()
.white_space
.preserve_spaces();
if is_non_preserved_whitespace {
self.current_line_segment.trailing_whitespace_size = inline_advance;
}
self.current_line_segment.justification_opportunities +=
glyph_store.total_justification_opportunities() as usize;

let inline_advance = Length::from(glyph_store.total_advance());
let preserve_spaces = parent_style
.get_inherited_text()
.white_space
.preserve_spaces();
let is_collapsible_whitespace = glyph_store.is_whitespace() && !preserve_spaces;

// 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.
// TODO(mrobinson): This isn't quite right for situations where collapsible white space
// ultimately does not collapse because it is between two other pieces of content.
// TODO(mrobinson): When we have font fallback, this should be calculating the
// block sizes of the fallback font.
let quirks_mode = self.layout_context.style_context.quirks_mode() != QuirksMode::NoQuirks;
let strut_size = if quirks_mode && !is_collapsible_whitespace {
self.current_inline_container_state()
.strut_block_sizes
.clone()
} else {
LineBlockSizes::zero()
};
self.update_unbreakable_segment_for_new_content(
&strut_size,
inline_advance,
is_collapsible_whitespace,
);

match self.current_line_segment.line_items.last_mut() {
Some(LineItem::TextRun(text_run)) => {
debug_assert!(font_key == text_run.font_key);
text_run.text.push(glyph_store);
self.current_line_segment.inline_size += inline_advance;

if !is_non_preserved_whitespace {
self.current_line_segment.has_content = true;
}
return;
},
_ => {},
}

let counts_for_block_size = self.layout_context.style_context.quirks_mode() ==
QuirksMode::NoQuirks ||
!is_non_preserved_whitespace;
let block_sizes = if counts_for_block_size {
// TODO(mrobinson): When we have font fallback, this should be calculating the
// block sizes of the fallback font.
self.current_inline_container_state()
.strut_block_sizes
.clone()
} else {
LineBlockSizes::zero()
};

self.push_content_line_item_to_unbreakable_segment(
inline_advance,
LineItem::TextRun(TextRunLineItem {
text: vec![glyph_store],
base_fragment_info: base_fragment_info.into(),
parent_style: parent_style.clone(),
font_metrics: font_metrics.clone(),
font_key,
text_decoration_line: self.current_inline_container_state().text_decoration_line,
}),
block_sizes,
!is_non_preserved_whitespace,
);
self.push_line_item_to_unbreakable_segment(LineItem::TextRun(TextRunLineItem {
text: vec![glyph_store],
base_fragment_info: base_fragment_info.into(),
parent_style: parent_style.clone(),
font_metrics: font_metrics.clone(),
font_key,
text_decoration_line: self.current_inline_container_state().text_decoration_line,
}));
}

fn push_content_line_item_to_unbreakable_segment(
fn update_unbreakable_segment_for_new_content(
&mut self,
block_sizes_of_content: &LineBlockSizes,
inline_size: Length,
line_item: LineItem,
block_sizes_of_content: LineBlockSizes,
counts_as_content: bool,
is_collapsible_whitespace: bool,
) {
if counts_as_content {
if !is_collapsible_whitespace {
self.current_line_segment.trailing_whitespace_size = Length::zero();
self.current_line_segment.has_content = true;
self.had_inflow_content = true;
} else {
self.current_line_segment.trailing_whitespace_size = inline_size;
}

self.current_line_segment.inline_size += inline_size;

// This may or may not include the size of the strut depending on the quirks mode setting.
let container_max_block_size = &self
.current_inline_container_state()
.nested_block_sizes
Expand All @@ -1194,12 +1185,11 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
.max_assign(container_max_block_size);
self.current_line_segment
.max_block_size
.max_assign(&block_sizes_of_content);
.max_assign(block_sizes_of_content);

self.push_line_item_to_unbreakable_segment(line_item);
self.current_line_segment.inline_size += inline_size;

// We need to update the size of the current segment and also propagate the
// whitespace setting to the current nesting level.
// Propagate the whitespace setting to the current nesting level.
let current_nesting_level = self.current_inline_container_state_mut();
current_nesting_level.has_content = true;
self.propagate_current_nesting_level_white_space_style();
Expand Down Expand Up @@ -1497,6 +1487,7 @@ impl InlineFormattingContext {
Length::zero()
};

let style = containing_block.style;
let mut ifc = InlineFormattingContextState {
positioning_context,
containing_block,
Expand All @@ -1508,11 +1499,11 @@ impl InlineFormattingContext {
block: Length::zero(),
}),
root_nesting_level: InlineContainerState::new(
containing_block.style.to_arc(),
style.to_arc(),
layout_context,
None, /* parent_container */
self.text_decoration_line,
layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks, /* create_strut */
inline_container_needs_strut(style, layout_context, None),
),
linebreaker: None,
inline_box_state_stack: Vec::new(),
Expand Down Expand Up @@ -1655,9 +1646,6 @@ impl InlineContainerState {
nested_block_sizes.max_assign(&strut_block_sizes);
}

//println!(" LINE HEIGHT: {:?}", style.get_inherited_text().line_height);
//println!(" BLOCK SIZE CONTRIBUTION: {:?} NESTED: {:?}", strut_block_sizes, nested_block_sizes);

Self {
style,
has_content: false,
Expand Down Expand Up @@ -1802,22 +1790,15 @@ impl InlineBoxContainerState {
) -> Self {
let style = inline_box.style.clone();
let pbm = style.padding_border_margin(containing_block);
let mut create_strut = layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks;
create_strut = create_strut ||
(style.writing_mode.is_vertical() && !pbm.padding_border_sums.block.is_zero());
create_strut = create_strut ||
(style.writing_mode.is_horizontal() && !pbm.padding_border_sums.inline.is_zero());

let parent_text_decoration_line = parent_container.text_decoration_line;
let base = InlineContainerState::new(
style,
layout_context,
Some(parent_container),
parent_text_decoration_line,
create_strut,
);
let create_strut = inline_container_needs_strut(&style, layout_context, Some(&pbm));
Self {
base,
base: InlineContainerState::new(
style,
layout_context,
Some(parent_container),
parent_container.text_decoration_line,
create_strut,
),
base_fragment_info: inline_box.base_fragment_info,
pbm,
is_last_fragment,
Expand Down Expand Up @@ -1959,13 +1940,10 @@ impl IndependentFormattingContext {
// > in the normal flow, unless it has either no in-flow line boxes or if
// > its overflow property has a computed value other than visible, in which
// > case the baseline is the bottom margin edge.
let baseline = match (
non_replaced.style.get_box().overflow_x,
independent_layout.last_inflow_baseline_offset,
) {
(Overflow::Visible, Some(baseline)) => Some(baseline),
_ => None,
};
let mut baseline = independent_layout.last_inflow_baseline_offset;
if non_replaced.style.establishes_scroll_container() {
baseline.take();
}

(
BoxFragment::new(
Expand Down Expand Up @@ -2001,18 +1979,14 @@ impl IndependentFormattingContext {

let (block_sizes, baseline_offset_in_parent) =
self.get_block_sizes_and_baseline_offset(ifc, size.block, baseline_offset);
ifc.push_content_line_item_to_unbreakable_segment(
size.inline,
LineItem::Atomic(AtomicLineItem {
fragment,
size,
positioning_context: child_positioning_context,
baseline_offset_in_parent,
baseline_offset_in_item: baseline_offset,
}),
block_sizes,
true,
);
ifc.update_unbreakable_segment_for_new_content(&block_sizes, size.inline, false);
ifc.push_line_item_to_unbreakable_segment(LineItem::Atomic(AtomicLineItem {
fragment,
size,
positioning_context: child_positioning_context,
baseline_offset_in_parent,
baseline_offset_in_item: baseline_offset,
}));

// Defer a soft wrap opportunity for when we next process text content.
ifc.have_deferred_soft_wrap_opportunity = true;
Expand All @@ -2031,7 +2005,6 @@ impl IndependentFormattingContext {
size_for_baseline_positioning: BaselineRelativeSize::zero(),
}
} else {
//println!("BASELINE for inline-block: {baseline_offset_in_content_area:?}");
let baseline_relative_size = BaselineRelativeSize {
ascent: baseline_offset_in_content_area,
descent: Au::from_f32_px(block_size.px()) - baseline_offset_in_content_area,
Expand Down Expand Up @@ -2767,8 +2740,6 @@ impl InlineBoxLineItem {
state.line_metrics.block_size - line_height + offset
},
_ => {
//println!("============> BLOCK START POSITION: {:?} + {:?} - {:?}",
// state.line_metrics.baseline_block_offset, self.baseline_offset, space_above_baseline);
state.line_metrics.baseline_block_offset + Length::from(self.baseline_offset) -
space_above_baseline
},
Expand Down Expand Up @@ -2954,3 +2925,47 @@ fn effective_vertical_for_inline_container(
_ => style.clone_vertical_align(),
}
}

/// Whether or not a strut should be created for an inline container. Normally
/// all inline containers get struts. In quirks mode this isn't always the case
/// though.
///
/// From https://quirks.spec.whatwg.org/#the-line-height-calculation-quirk
///
/// > ### § 3.3. The line height calculation quirk
/// > In quirks mode and limited-quirks mode, an inline box that matches the following
/// > conditions, must, for the purpose of line height calculation, act as if the box had a
/// > line-height of zero.
/// >
/// > - The border-top-width, border-bottom-width, padding-top and padding-bottom
/// > properties have a used value of zero and the box has a vertical writing mode, or the
/// > border-right-width, border-left-width, padding-right and padding-left properties have
/// > a used value of zero and the box has a horizontal writing mode.
/// > - It either contains no text or it contains only collapsed whitespace.
/// >
/// > ### § 3.4. The blocks ignore line-height quirk
/// > In quirks mode and limited-quirks mode, for a block container element whose content is
/// > composed of inline-level elements, the element’s line-height must be ignored for the
/// > purpose of calculating the minimal height of line boxes within the element.
///
/// Since we incorporate the size of the strut into the line-height calculation when
/// adding text, we can simply not incorporate the strut at the start of inline box
/// processing. This also works the same for the root of the IFC.
fn inline_container_needs_strut(
style: &ComputedValues,
layout_context: &LayoutContext,
pbm: Option<&PaddingBorderMargin>,
) -> bool {
if layout_context.style_context.quirks_mode() == QuirksMode::NoQuirks {
return true;
}

// This is not in a standard yet, but all browsers disable this quirk for list items.
// See https://github.com/whatwg/quirks/issues/38.
if style.get_box().display.is_list_item() {
return true;
}

pbm.map(|pbm| !pbm.padding_border_sums.inline.is_zero())
.unwrap_or(false)
}

0 comments on commit 98e47d1

Please sign in to comment.