layout_2020: Add support for hoisting positioned fragments in inline boxes

Add support for tracking containing blocks when doing inline layout.
This requires setting up a PositioningContext for inline boxes when
necessary. Instead of using the PositioningContext helper methods
and we reuse the contexts between line breaks.

Fixes #25279.
This commit is contained in:
Martin Robinson 2020-03-18 13:14:51 +01:00
parent b945de4ea9
commit bd06227a60
8 changed files with 59 additions and 24 deletions

View file

@ -12,7 +12,10 @@ use crate::fragments::{
DebugId, Fragment, TextFragment, DebugId, Fragment, TextFragment,
}; };
use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{relative_adjustement, AbsolutelyPositionedBox, PositioningContext}; use crate::positioned::{
relative_adjustement, AbsolutelyPositionedBox, HoistedAbsolutelyPositionedBox,
PositioningContext,
};
use crate::sizing::ContentSizes; use crate::sizing::ContentSizes;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside}; use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
use crate::ContainingBlock; use crate::ContainingBlock;
@ -64,6 +67,7 @@ struct InlineNestingLevelState<'box_tree> {
fragments_so_far: Vec<Fragment>, fragments_so_far: Vec<Fragment>,
inline_start: Length, inline_start: Length,
max_block_size_of_fragments_so_far: Length, max_block_size_of_fragments_so_far: Length,
positioning_context: Option<PositioningContext>,
} }
struct PartialInlineBoxFragment<'box_tree> { struct PartialInlineBoxFragment<'box_tree> {
@ -86,6 +90,31 @@ struct InlineFormattingContextState<'box_tree, 'a, 'b> {
current_nesting_level: InlineNestingLevelState<'box_tree>, current_nesting_level: InlineNestingLevelState<'box_tree>,
} }
impl<'box_tree, 'a, 'b> InlineFormattingContextState<'box_tree, 'a, 'b> {
fn push_hoisted_box_to_positioning_context(
&mut self,
hoisted_box: HoistedAbsolutelyPositionedBox,
) {
if let Some(context) = self.current_nesting_level.positioning_context.as_mut() {
context.push(hoisted_box);
return;
}
for nesting_level in self.partial_inline_boxes_stack.iter_mut().rev() {
if let Some(context) = nesting_level
.parent_nesting_level
.positioning_context
.as_mut()
{
context.push(hoisted_box);
return;
}
}
self.positioning_context.push(hoisted_box);
}
}
struct Lines { struct Lines {
// One anonymous fragment per line // One anonymous fragment per line
fragments: Vec<Fragment>, fragments: Vec<Fragment>,
@ -226,6 +255,7 @@ impl InlineFormattingContext {
fragments_so_far: Vec::with_capacity(self.inline_level_boxes.len()), fragments_so_far: Vec::with_capacity(self.inline_level_boxes.len()),
inline_start: Length::zero(), inline_start: Length::zero(),
max_block_size_of_fragments_so_far: Length::zero(), max_block_size_of_fragments_so_far: Length::zero(),
positioning_context: None,
}, },
}; };
loop { loop {
@ -257,15 +287,14 @@ impl InlineFormattingContext {
panic!("display:none does not generate an abspos box") panic!("display:none does not generate an abspos box")
}, },
}; };
let hoisted_fragment = let hoisted_box = box_.clone().to_hoisted(initial_start_corner, tree_rank);
box_.clone().to_hoisted(initial_start_corner, tree_rank); let hoisted_fragment_id = hoisted_box.fragment_id;
let hoisted_fragment_id = hoisted_fragment.fragment_id; ifc.push_hoisted_box_to_positioning_context(hoisted_box);
ifc.positioning_context.push(hoisted_fragment); ifc.current_nesting_level.fragments_so_far.push(
ifc.lines Fragment::AbsoluteOrFixedPositioned(AbsoluteOrFixedPositionedFragment(
.fragments hoisted_fragment_id,
.push(Fragment::AbsoluteOrFixedPositioned( )),
AbsoluteOrFixedPositionedFragment(hoisted_fragment_id), );
));
}, },
InlineLevelBox::OutOfFlowFloatBox(_box_) => { InlineLevelBox::OutOfFlowFloatBox(_box_) => {
// TODO // TODO
@ -275,6 +304,7 @@ impl InlineFormattingContext {
// Reached the end of ifc.remaining_boxes // Reached the end of ifc.remaining_boxes
if let Some(mut partial) = ifc.partial_inline_boxes_stack.pop() { if let Some(mut partial) = ifc.partial_inline_boxes_stack.pop() {
partial.finish_layout( partial.finish_layout(
layout_context,
&mut ifc.current_nesting_level, &mut ifc.current_nesting_level,
&mut ifc.inline_position, &mut ifc.inline_position,
false, false,
@ -392,6 +422,7 @@ impl InlineBox {
if style.clone_position().is_relative() { if style.clone_position().is_relative() {
start_corner += &relative_adjustement(&style, ifc.containing_block) start_corner += &relative_adjustement(&style, ifc.containing_block)
} }
let positioning_context = PositioningContext::new_for_style(&style);
PartialInlineBoxFragment { PartialInlineBoxFragment {
tag: self.tag, tag: self.tag,
style, style,
@ -409,6 +440,7 @@ impl InlineBox {
fragments_so_far: Vec::with_capacity(self.children.len()), fragments_so_far: Vec::with_capacity(self.children.len()),
inline_start: ifc.inline_position, inline_start: ifc.inline_position,
max_block_size_of_fragments_so_far: Length::zero(), max_block_size_of_fragments_so_far: Length::zero(),
positioning_context,
}, },
), ),
} }
@ -418,6 +450,7 @@ impl InlineBox {
impl<'box_tree> PartialInlineBoxFragment<'box_tree> { impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
fn finish_layout( fn finish_layout(
&mut self, &mut self,
layout_context: &LayoutContext,
nesting_level: &mut InlineNestingLevelState, nesting_level: &mut InlineNestingLevelState,
inline_position: &mut Length, inline_position: &mut Length,
at_line_break: bool, at_line_break: bool,
@ -459,6 +492,11 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
fragment.border.block_sum() + fragment.border.block_sum() +
fragment.margin.block_sum(), fragment.margin.block_sum(),
); );
if let Some(context) = nesting_level.positioning_context.as_mut() {
context.layout_collected_children(layout_context, &mut fragment);
}
self.parent_nesting_level self.parent_nesting_level
.fragments_so_far .fragments_so_far
.push(Fragment::Box(fragment)); .push(Fragment::Box(fragment));
@ -748,7 +786,12 @@ impl TextRun {
ifc.current_nesting_level.inline_start = Length::zero(); ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level; let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() { for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
partial.finish_layout(nesting_level, &mut ifc.inline_position, true); partial.finish_layout(
layout_context,
nesting_level,
&mut ifc.inline_position,
true,
);
partial.start_corner.inline = Length::zero(); partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero(); partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero(); partial.border.inline_start = Length::zero();

View file

@ -172,7 +172,7 @@ impl PositioningContext {
self.for_nearest_positioned_ancestor.is_some() self.for_nearest_positioned_ancestor.is_some()
} }
fn new_for_style(style: &ComputedValues) -> Option<Self> { pub(crate) fn new_for_style(style: &ComputedValues) -> Option<Self> {
if style.establishes_containing_block_for_all_descendants() { if style.establishes_containing_block_for_all_descendants() {
Some(Self::new_for_containing_block_for_all_descendants()) Some(Self::new_for_containing_block_for_all_descendants())
} else if style.establishes_containing_block() { } else if style.establishes_containing_block() {
@ -243,7 +243,7 @@ impl PositioningContext {
// Lay out the hoisted boxes collected into this `PositioningContext` and add them // Lay out the hoisted boxes collected into this `PositioningContext` and add them
// to the given `BoxFragment`. // to the given `BoxFragment`.
fn layout_collected_children( pub fn layout_collected_children(
&mut self, &mut self,
layout_context: &LayoutContext, layout_context: &LayoutContext,
new_fragment: &mut BoxFragment, new_fragment: &mut BoxFragment,

View file

@ -245,7 +245,9 @@ impl ComputedValuesExt for ComputedValues {
/// Returns true if this style establishes a containing block for all descendants /// Returns true if this style establishes a containing block for all descendants
/// including fixed and absolutely positioned ones. /// including fixed and absolutely positioned ones.
fn establishes_containing_block_for_all_descendants(&self) -> bool { fn establishes_containing_block_for_all_descendants(&self) -> bool {
if self.has_transform_or_perspective() { if self.get_box().display.outside() != stylo::DisplayOutside::Inline &&
self.has_transform_or_perspective()
{
return true; return true;
} }

View file

@ -1,2 +0,0 @@
[abspos-inline-008.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[toogle-abspos-on-relpos-inline-child.html]
expected: FAIL

View file

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

View file

@ -1,2 +0,0 @@
[filtered-inline-is-container.html]
expected: FAIL

View file

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