Make LineItems a token stream on the root (#30608)

Flattening the LineItem tree into a token stream will allow for handling
the case where an unbreakable line segment spans multiple inline boxes
which might have different hierarchies. This change also fixes the
handling of the second anonymous fragment of a block-in-inline-split.

Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
Martin Robinson 2023-10-25 17:54:44 +02:00 committed by GitHub
parent c9d25963a7
commit 95e32f8372
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 127 additions and 163 deletions

View file

@ -475,8 +475,8 @@ where
self.ongoing_inline_boxes_stack.push(InlineBox { self.ongoing_inline_boxes_stack.push(InlineBox {
base_fragment_info: info.into(), base_fragment_info: info.into(),
style: info.style.clone(), style: info.style.clone(),
first_fragment: true, is_first_fragment: true,
last_fragment: false, is_last_fragment: false,
children: vec![], children: vec![],
}); });
@ -498,7 +498,7 @@ where
.ongoing_inline_boxes_stack .ongoing_inline_boxes_stack
.pop() .pop()
.expect("no ongoing inline level box found"); .expect("no ongoing inline level box found");
inline_box.last_fragment = true; inline_box.is_last_fragment = true;
ArcRefCell::new(InlineLevelBox::InlineBox(inline_box)) ArcRefCell::new(InlineLevelBox::InlineBox(inline_box))
} else { } else {
self.ongoing_inline_formatting_context.ends_with_whitespace = false; self.ongoing_inline_formatting_context.ends_with_whitespace = false;
@ -537,13 +537,13 @@ where
let fragmented = InlineBox { let fragmented = InlineBox {
base_fragment_info: ongoing.base_fragment_info, base_fragment_info: ongoing.base_fragment_info,
style: ongoing.style.clone(), style: ongoing.style.clone(),
first_fragment: ongoing.first_fragment, is_first_fragment: ongoing.is_first_fragment,
// The fragmented boxes before the block level element // The fragmented boxes before the block level element
// are obviously not the last fragment. // are obviously not the last fragment.
last_fragment: false, is_last_fragment: false,
children: std::mem::take(&mut ongoing.children), children: std::mem::take(&mut ongoing.children),
}; };
ongoing.first_fragment = false; ongoing.is_first_fragment = false;
fragmented fragmented
}); });

View file

@ -3,6 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::OnceCell; use std::cell::OnceCell;
use std::vec::IntoIter;
use app_units::Au; use app_units::Au;
use atomic_refcell::AtomicRef; use atomic_refcell::AtomicRef;
@ -32,7 +33,7 @@ use crate::fragment_tree::{
AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin,
FontMetrics, Fragment, HoistedSharedFragment, TextFragment, FontMetrics, Fragment, HoistedSharedFragment, TextFragment,
}; };
use crate::geom::{LengthOrAuto, LogicalRect, LogicalVec2}; use crate::geom::{LogicalRect, LogicalVec2};
use crate::positioned::{ use crate::positioned::{
relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength,
}; };
@ -74,8 +75,8 @@ pub(crate) struct InlineBox {
pub base_fragment_info: BaseFragmentInfo, pub base_fragment_info: BaseFragmentInfo,
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub style: Arc<ComputedValues>, pub style: Arc<ComputedValues>,
pub first_fragment: bool, pub is_first_fragment: bool,
pub last_fragment: bool, pub is_last_fragment: bool,
pub children: Vec<ArcRefCell<InlineLevelBox>>, pub children: Vec<ArcRefCell<InlineLevelBox>>,
} }
@ -126,6 +127,10 @@ struct LineUnderConstruction {
/// Note that when this is not empty, its start corner takes precedence over /// Note that when this is not empty, its start corner takes precedence over
/// [`LineUnderConstruction::start_position`]. /// [`LineUnderConstruction::start_position`].
placement_among_floats: OnceCell<LogicalRect<Length>>, placement_among_floats: OnceCell<LogicalRect<Length>>,
/// The LineItems for the current line under construction that have already
/// been committed to this line.
line_items: Vec<LineItem>,
} }
impl LineUnderConstruction { impl LineUnderConstruction {
@ -138,6 +143,7 @@ impl LineUnderConstruction {
has_content: false, has_content: false,
has_floats_waiting_to_be_placed: false, has_floats_waiting_to_be_placed: false,
placement_among_floats: OnceCell::new(), placement_among_floats: OnceCell::new(),
line_items: Vec::new(),
} }
} }
@ -155,10 +161,6 @@ impl LineUnderConstruction {
} }
struct InlineContainerState { struct InlineContainerState {
/// The [`LineItems`]s we have collected so far for this nesting level for
/// the current line.
line_items_so_far: Vec<LineItem>,
/// Whether or not we have processed any content (an atomic element or text) for /// Whether or not we have processed any content (an atomic element or text) for
/// this inline box on the current line OR any previous line. /// this inline box on the current line OR any previous line.
has_content: bool, has_content: bool,
@ -190,11 +192,13 @@ struct InlineBoxContainerState {
/// The [`PaddingBorderMargin`] of the [`InlineBox`] that this state tracks. /// The [`PaddingBorderMargin`] of the [`InlineBox`] that this state tracks.
pbm: PaddingBorderMargin, pbm: PaddingBorderMargin,
/// Whether or not this inline box has already been part of a previous line. /// Index in the line items of our line item.
/// We need to create at least one Fragment for every inline box, but on following line_item_index: usize,
/// lines, if the inline box is totally empty (such as after a preserved line
/// break), then we don't want to create empty Fragments for it. /// Whether this is the last fragment of this InlineBox. This may not be the case
was_part_of_previous_line: bool, /// if the InlineBox is split due to an inline-box-split and this is not the last
/// of that split.
is_last_fragment: bool,
} }
struct InlineFormattingContextState<'a, 'b> { struct InlineFormattingContextState<'a, 'b> {
@ -208,8 +212,7 @@ struct InlineFormattingContextState<'a, 'b> {
/// are currently laid out at the top-level of each [`InlineFormattingContext`]. /// are currently laid out at the top-level of each [`InlineFormattingContext`].
fragments: Vec<Fragment>, fragments: Vec<Fragment>,
/// Information about the line currently being laid out into [`LineItems`]s. The /// Information about the line currently being laid out into [`LineItems`]s.
/// [`LineItem`]s themselves are stored in the nesting state.
current_line: LineUnderConstruction, current_line: LineUnderConstruction,
/// After a forced line break (for instance from a `<br>` element) we wait to actually /// After a forced line break (for instance from a `<br>` element) we wait to actually
@ -267,9 +270,9 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
self.current_line_max_block_size() self.current_line_max_block_size()
.max(line_item.block_size()), .max(line_item.block_size()),
); );
self.current_line.line_items.push(line_item);
let current_nesting_level = self.current_inline_container_state_mut(); let current_nesting_level = self.current_inline_container_state_mut();
current_nesting_level.line_items_so_far.push(line_item);
current_nesting_level.has_content = true; current_nesting_level.has_content = true;
self.propagate_current_nesting_level_white_space_style(); self.propagate_current_nesting_level_white_space_style();
} }
@ -310,40 +313,47 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
(parent.text_decoration_line, parent.nested_block_size) (parent.text_decoration_line, parent.nested_block_size)
}; };
self.inline_box_state_stack let mut inline_box_state = InlineBoxContainerState::new(
.push(InlineBoxContainerState::new( inline_box,
inline_box, &self.containing_block,
&mut self.current_line.inline_position, text_decoration_of_parent,
&self.containing_block, nested_block_size_of_parent,
text_decoration_of_parent, self.layout_context,
nested_block_size_of_parent, self.current_line.line_items.len(),
self.layout_context, inline_box.is_last_fragment,
)); );
if inline_box.is_first_fragment {
self.current_line.inline_position += inline_box_state.pbm.padding.inline_start +
inline_box_state.pbm.border.inline_start +
inline_box_state
.pbm
.margin
.inline_start
.auto_is(Length::zero);
}
let line_item = inline_box_state
.layout_into_line_item(self.layout_context, inline_box.is_first_fragment);
self.current_line
.line_items
.push(LineItem::InlineBox(line_item));
self.inline_box_state_stack.push(inline_box_state);
} }
/// Finish laying out a particular [`InlineBox`] into line items. This will add the /// Finish laying out a particular [`InlineBox`] into line items. This will add the
/// final [`InlineBoxLineItem`] to the state and pop its state off of /// final [`InlineBoxLineItem`] to the state and pop its state off of
/// [`Self::inline_box_state_stack`]. /// [`Self::inline_box_state_stack`].
fn finish_inline_box(&mut self) { fn finish_inline_box(&mut self) {
let mut inline_box_state = match self.inline_box_state_stack.pop() { let inline_box_state = match self.inline_box_state_stack.pop() {
Some(inline_box_state) => inline_box_state, Some(inline_box_state) => inline_box_state,
None => return, // We are at the root. None => return, // We are at the root.
}; };
// We reached the end of the remaining boxes in this nesting level, so we finish it and self.current_line.line_items.push(LineItem::EndInlineBox);
// start working on the parent nesting level again.
let line_item = inline_box_state.layout_into_line_item(
self.layout_context,
&mut self.current_line.inline_position,
true, /* at_end_of_inline_element */
);
self.current_line self.current_line
.max_block_size .max_block_size
.max_assign(inline_box_state.base.nested_block_size); .max_assign(inline_box_state.base.nested_block_size);
self.current_inline_container_state_mut()
.line_items_so_far
.push(LineItem::InlineBox(line_item));
// If the inline box that we just finished had any content at all, we want to propagate // If the inline box that we just finished had any content at all, we want to propagate
// the `white-space` property of its parent to future inline children. This is because // the `white-space` property of its parent to future inline children. This is because
@ -352,6 +362,19 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
if inline_box_state.base.has_content { if inline_box_state.base.has_content {
self.propagate_current_nesting_level_white_space_style(); self.propagate_current_nesting_level_white_space_style();
} }
if inline_box_state.is_last_fragment {
if let LineItem::InlineBox(ref mut inline_box) =
self.current_line.line_items[inline_box_state.line_item_index]
{
inline_box.is_last_fragment = true;
self.current_line.inline_position += inline_box.pbm.padding.inline_end +
inline_box.pbm.border.inline_end +
inline_box.pbm.margin.inline_end.auto_is(Length::zero);
} else {
warn!("Did not find InlineBox line item where we expected to!");
}
}
} }
/// Finish layout of all inline boxes for the current line. This will gather all /// Finish layout of all inline boxes for the current line. This will gather all
@ -360,28 +383,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
fn finish_current_line_and_reset(&mut self, layout_context: &LayoutContext) { fn finish_current_line_and_reset(&mut self, layout_context: &LayoutContext) {
self.linebreak_before_new_content = false; self.linebreak_before_new_content = false;
let mut line_item_from_child = None; let mut line_items = std::mem::take(&mut self.current_line.line_items);
for inline_box_state in self.inline_box_state_stack.iter_mut().rev() {
if let Some(line_item_from_child) = line_item_from_child {
inline_box_state
.base
.line_items_so_far
.push(LineItem::InlineBox(line_item_from_child));
}
line_item_from_child = Some(inline_box_state.layout_into_line_item(
self.layout_context,
&mut self.current_line.inline_position,
false,
));
}
if let Some(line_item_from_child) = line_item_from_child {
self.root_nesting_level
.line_items_so_far
.push(LineItem::InlineBox(line_item_from_child));
}
let mut line_items = std::mem::take(&mut self.root_nesting_level.line_items_so_far);
// From <https://www.w3.org/TR/css-text-3/#white-space-phase-2>: // From <https://www.w3.org/TR/css-text-3/#white-space-phase-2>:
// > 3. A sequence of collapsible spaces at the end of a line is removed, // > 3. A sequence of collapsible spaces at the end of a line is removed,
@ -429,7 +431,7 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
}; };
let positioning_context_length = state.positioning_context.len(); let positioning_context_length = state.positioning_context.len();
let fragments = layout_line_items(line_items, layout_context, &mut state); let fragments = layout_line_items(&mut line_items.into_iter(), layout_context, &mut state);
let size = LogicalVec2 { let size = LogicalVec2 {
inline: self.containing_block.inline_size, inline: self.containing_block.inline_size,
@ -466,6 +468,14 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
inline: Length::zero(), inline: Length::zero(),
block: block_end_position, block: block_end_position,
}); });
// Add line items for the current inline box stack to the new line.
for inline_box_state in self.inline_box_state_stack.iter_mut() {
inline_box_state.line_item_index = self.current_line.line_items.len();
self.current_line.line_items.push(LineItem::InlineBox(
inline_box_state.layout_into_line_item(self.layout_context, false),
));
}
} }
/// Given the amount of whitespace trimmed from the line and taking into consideration /// Given the amount of whitespace trimmed from the line and taking into consideration
@ -735,9 +745,9 @@ impl InlineFormattingContext {
}; };
} }
add!(first_fragment, inline_start); add!(is_first_fragment, inline_start);
self.traverse(&inline_box.children); self.traverse(&inline_box.children);
add!(last_fragment, inline_end); add!(is_last_fragment, inline_end);
}, },
InlineLevelBox::TextRun(text_run) => { InlineLevelBox::TextRun(text_run) => {
let result = text_run let result = text_run
@ -873,7 +883,6 @@ impl InlineFormattingContext {
white_space: containing_block.style.get_inherited_text().white_space, white_space: containing_block.style.get_inherited_text().white_space,
linebreaker: None, linebreaker: None,
root_nesting_level: InlineContainerState { root_nesting_level: InlineContainerState {
line_items_so_far: Vec::with_capacity(self.inline_level_boxes.len()),
nested_block_size: line_height_from_style(layout_context, &containing_block.style), nested_block_size: line_height_from_style(layout_context, &containing_block.style),
has_content: false, has_content: false,
text_decoration_line: self.text_decoration_line, text_decoration_line: self.text_decoration_line,
@ -912,8 +921,8 @@ impl InlineFormattingContext {
atomic_formatting_context.layout_into_line_items(layout_context, &mut ifc); atomic_formatting_context.layout_into_line_items(layout_context, &mut ifc);
}, },
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => { InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
ifc.current_inline_container_state_mut() ifc.current_line
.line_items_so_far .line_items
.push(LineItem::AbsolutelyPositioned( .push(LineItem::AbsolutelyPositioned(
AbsolutelyPositionedLineItem { AbsolutelyPositionedLineItem {
absolutely_positioned_box: box_.clone(), absolutely_positioned_box: box_.clone(),
@ -982,29 +991,17 @@ impl InlineFormattingContext {
impl InlineBoxContainerState { impl InlineBoxContainerState {
fn new( fn new(
inline_box: &InlineBox, inline_box: &InlineBox,
inline_position: &mut Length,
containing_block: &ContainingBlock, containing_block: &ContainingBlock,
text_decoration_of_parent: TextDecorationLine, text_decoration_of_parent: TextDecorationLine,
nested_block_size_of_parent: Length, nested_block_size_of_parent: Length,
layout_context: &LayoutContext, layout_context: &LayoutContext,
initial_index_in_line_items: usize,
is_last_fragment: bool,
) -> Self { ) -> Self {
let style = inline_box.style.clone(); let style = inline_box.style.clone();
let mut pbm = style.padding_border_margin(containing_block);
if inline_box.first_fragment {
*inline_position += pbm.padding.inline_start +
pbm.border.inline_start +
pbm.margin.inline_start.auto_is(Length::zero);
} else {
pbm.padding.inline_start = Length::zero();
pbm.border.inline_start = Length::zero();
pbm.margin.inline_start = LengthOrAuto::zero();
}
let text_decoration_line = text_decoration_of_parent | style.clone_text_decoration_line(); let text_decoration_line = text_decoration_of_parent | style.clone_text_decoration_line();
Self { Self {
base: InlineContainerState { base: InlineContainerState {
line_items_so_far: Vec::with_capacity(inline_box.children.len()),
has_content: false, has_content: false,
text_decoration_line, text_decoration_line,
nested_block_size: nested_block_size_of_parent nested_block_size: nested_block_size_of_parent
@ -1012,50 +1009,25 @@ impl InlineBoxContainerState {
}, },
style, style,
base_fragment_info: inline_box.base_fragment_info, base_fragment_info: inline_box.base_fragment_info,
pbm, pbm: inline_box.style.padding_border_margin(containing_block),
was_part_of_previous_line: false, line_item_index: initial_index_in_line_items,
is_last_fragment,
} }
} }
fn layout_into_line_item( fn layout_into_line_item(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
inline_position: &mut Length, is_first_fragment: bool,
at_end_of_inline_element: bool,
) -> InlineBoxLineItem { ) -> InlineBoxLineItem {
// If we are finishing in order to fragment this InlineBox into multiple lines, do InlineBoxLineItem {
// not add end margins, borders, and padding.
let mut pbm = self.pbm.clone();
if !at_end_of_inline_element {
pbm.padding.inline_end = Length::zero();
pbm.border.inline_end = Length::zero();
pbm.margin.inline_end = LengthOrAuto::zero();
} else {
*inline_position += self.pbm.padding.inline_end +
self.pbm.border.inline_end +
self.pbm.margin.inline_end.auto_is(Length::zero)
}
let new_line_item = InlineBoxLineItem {
base_fragment_info: self.base_fragment_info, base_fragment_info: self.base_fragment_info,
style: self.style.clone(), style: self.style.clone(),
block_size: line_gap_from_style(layout_context, &self.style), block_size: line_gap_from_style(layout_context, &self.style),
pbm, pbm: self.pbm.clone(),
children: std::mem::take(&mut self.base.line_items_so_far), is_first_fragment,
always_make_fragment: !self.was_part_of_previous_line, is_last_fragment: false,
}; }
// This InlineBox now has at least one Fragment that corresponds to it, so
// if subsequent lines can ignore it if it is empty on those lines.
self.was_part_of_previous_line = true;
// If this partial / inline box appears on any subsequent lines, it should not
// have any start margin, border, or padding.
self.pbm.padding.inline_start = Length::zero();
self.pbm.border.inline_start = Length::zero();
self.pbm.margin.inline_start = LengthOrAuto::zero();
new_line_item
} }
} }
@ -1508,8 +1480,8 @@ impl FloatBox {
.replace_placement_among_floats(new_placement); .replace_placement_among_floats(new_placement);
} }
ifc.current_inline_container_state_mut() ifc.current_line
.line_items_so_far .line_items
.push(LineItem::Float(FloatLineItem { .push(LineItem::Float(FloatLineItem {
fragment, fragment,
needs_placement: needs_placement_later, needs_placement: needs_placement_later,
@ -1583,12 +1555,12 @@ struct LineItemLayoutState<'a> {
} }
fn layout_line_items( fn layout_line_items(
line_items: Vec<LineItem>, iterator: &mut IntoIter<LineItem>,
layout_context: &LayoutContext, layout_context: &LayoutContext,
state: &mut LineItemLayoutState, state: &mut LineItemLayoutState,
) -> Vec<Fragment> { ) -> Vec<Fragment> {
let mut fragments = vec![]; let mut fragments = vec![];
for item in line_items.into_iter() { while let Some(item) = iterator.next() {
match item { match item {
LineItem::TextRun(text_line_item) => { LineItem::TextRun(text_line_item) => {
if let Some(fragment) = text_line_item.layout(state) { if let Some(fragment) = text_line_item.layout(state) {
@ -1596,10 +1568,11 @@ fn layout_line_items(
} }
}, },
LineItem::InlineBox(box_line_item) => { LineItem::InlineBox(box_line_item) => {
if let Some(fragment) = box_line_item.layout(layout_context, state) { if let Some(fragment) = box_line_item.layout(iterator, layout_context, state) {
fragments.push(Fragment::Box(fragment)) fragments.push(Fragment::Box(fragment))
} }
}, },
LineItem::EndInlineBox => break,
LineItem::Atomic(atomic_line_item) => { LineItem::Atomic(atomic_line_item) => {
fragments.push(Fragment::Box(atomic_line_item.layout(state))); fragments.push(Fragment::Box(atomic_line_item.layout(state)));
}, },
@ -1619,9 +1592,6 @@ fn layout_line_items(
fn place_pending_floats(ifc: &mut InlineFormattingContextState, line_items: &mut Vec<LineItem>) { fn place_pending_floats(ifc: &mut InlineFormattingContextState, line_items: &mut Vec<LineItem>) {
for item in line_items.into_iter() { for item in line_items.into_iter() {
match item { match item {
LineItem::InlineBox(box_line_item) => {
place_pending_floats(ifc, &mut box_line_item.children);
},
LineItem::Float(float_line_item) => { LineItem::Float(float_line_item) => {
if float_line_item.needs_placement { if float_line_item.needs_placement {
ifc.place_float_fragment(&mut float_line_item.fragment); ifc.place_float_fragment(&mut float_line_item.fragment);
@ -1635,6 +1605,7 @@ fn place_pending_floats(ifc: &mut InlineFormattingContextState, line_items: &mut
enum LineItem { enum LineItem {
TextRun(TextRunLineItem), TextRun(TextRunLineItem),
InlineBox(InlineBoxLineItem), InlineBox(InlineBoxLineItem),
EndInlineBox,
Atomic(AtomicLineItem), Atomic(AtomicLineItem),
AbsolutelyPositioned(AbsolutelyPositionedLineItem), AbsolutelyPositioned(AbsolutelyPositionedLineItem),
Float(FloatLineItem), Float(FloatLineItem),
@ -1644,14 +1615,8 @@ impl LineItem {
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool { fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Length) -> bool {
match self { match self {
LineItem::TextRun(ref mut item) => item.trim_whitespace_at_end(whitespace_trimmed), LineItem::TextRun(ref mut item) => item.trim_whitespace_at_end(whitespace_trimmed),
LineItem::InlineBox(b) => { LineItem::InlineBox(_) => true,
for child in b.children.iter_mut().rev() { LineItem::EndInlineBox => true,
if !child.trim_whitespace_at_end(whitespace_trimmed) {
return false;
}
}
true
},
LineItem::Atomic(_) => false, LineItem::Atomic(_) => false,
LineItem::AbsolutelyPositioned(_) => true, LineItem::AbsolutelyPositioned(_) => true,
LineItem::Float(_) => true, LineItem::Float(_) => true,
@ -1665,6 +1630,7 @@ impl LineItem {
// TODO(mrobinson): This should get the line height from the font. // TODO(mrobinson): This should get the line height from the font.
Length::zero() Length::zero()
}, },
LineItem::EndInlineBox => Length::zero(),
LineItem::Atomic(atomic) => atomic.size.block, LineItem::Atomic(atomic) => atomic.size.block,
LineItem::AbsolutelyPositioned(_) => Length::zero(), LineItem::AbsolutelyPositioned(_) => Length::zero(),
LineItem::Float(_) => Length::zero(), LineItem::Float(_) => Length::zero(),
@ -1794,21 +1760,40 @@ struct InlineBoxLineItem {
style: Arc<ComputedValues>, style: Arc<ComputedValues>,
pbm: PaddingBorderMargin, pbm: PaddingBorderMargin,
block_size: Length, block_size: Length,
children: Vec<LineItem>,
always_make_fragment: bool, // Whether this is the first fragment for this inline box. This means that it's the
// first potentially split box of a block-in-inline-split (or only if there's no
// split) and also the first appearance of this fragment on any line.
is_first_fragment: bool,
// Whether this is the last fragment for this inline box. This means that it's the
// last potentially split box of a block-in-inline-split (or only if there's no split)
// and also the last appearance of this fragment on any line.
is_last_fragment: bool,
} }
impl InlineBoxLineItem { impl InlineBoxLineItem {
fn layout( fn layout(
self, self,
iterator: &mut IntoIter<LineItem>,
layout_context: &LayoutContext, layout_context: &LayoutContext,
state: &mut LineItemLayoutState, state: &mut LineItemLayoutState,
) -> Option<BoxFragment> { ) -> Option<BoxFragment> {
let style = self.style.clone(); let style = self.style.clone();
let mut padding = self.pbm.padding.clone();
let mut border = self.pbm.border.clone();
let mut margin = self.pbm.margin.auto_is(Length::zero);
let padding = self.pbm.padding.clone(); if !self.is_first_fragment {
let border = self.pbm.border.clone(); padding.inline_start = Length::zero();
let margin = self.pbm.margin.auto_is(Length::zero); border.inline_start = Length::zero();
margin.inline_start = Length::zero();
}
if !self.is_last_fragment {
padding.inline_end = Length::zero();
border.inline_end = Length::zero();
margin.inline_end = Length::zero();
}
let pbm_sums = &(&padding + &border) + &margin; let pbm_sums = &(&padding + &border) + &margin;
state.inline_position += pbm_sums.inline_start; state.inline_position += pbm_sums.inline_start;
@ -1827,13 +1812,14 @@ impl InlineBoxLineItem {
positioning_context: nested_positioning_context, positioning_context: nested_positioning_context,
line_block_start: state.line_block_start, line_block_start: state.line_block_start,
}; };
let fragments = layout_line_items(self.children, layout_context, &mut nested_state);
let fragments = layout_line_items(iterator, layout_context, &mut nested_state);
// If the inline box didn't have any content at all, don't add a Fragment for it. // If the inline box didn't have any content at all, don't add a Fragment for it.
let box_has_padding_border_or_margin = pbm_sums.inline_sum() > Length::zero(); let box_has_padding_border_or_margin = pbm_sums.inline_sum() > Length::zero();
let box_had_absolutes = let box_had_absolutes =
original_nested_positioning_context_length != nested_state.positioning_context.len(); original_nested_positioning_context_length != nested_state.positioning_context.len();
if !self.always_make_fragment && if !self.is_first_fragment &&
fragments.is_empty() && fragments.is_empty() &&
!box_has_padding_border_or_margin && !box_has_padding_border_or_margin &&
!box_had_absolutes !box_had_absolutes

View file

@ -1,2 +0,0 @@
[block-in-inline-empty-001.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-insert-006-nosplit-ref.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-insert-006-ref.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-insert-006.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-nested-002.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-whitespace-001a.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[block-in-inline-whitespace-001b.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[emptyspan-1.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[split-inner-inline-2.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[whitespace-present-1a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[whitespace-present-1b.html]
expected: FAIL