layout: Simplify PositioningContext by having it hold a single Vec (#36795)

`PositioningContext` held two vectors, one inside an `Option`, to
differentiate between the version used for a containing block for all
descendants (including `position: absolute` and `position: fixed`) or
only for `position: absolute` descendants. This distinction was really
hard to reason about and required a lot of bookkeeping about what kind
of `PositioningContext` a layout box's parent expected. In addition, it
led to a lot of mistakes.

This change simplifies things so that `PositioningContext` only holds a
single vector. When it comes time to lay out hoisted absolutely
positioned
fragments, the code then:
 - lays out all of them (in the case of a `PositioningContext` for all
   descendants), or
 - only lays out the `position: absolute` descendants and preserves the
   `position: fixed` descendants (in the case the `PositioningContext`
   is only for `position: absolute`.), or
- lays out none of them if the `PositioningContext` was created for
  box that did not establish a containing block for absolutes.

It's possible that this way of dealing with hoisted absolutes is a bit
less efficient, but, the number of these descendants is typically quite
small, so it should not be significant. In addition, this decreases the
size in memory of all `PositioningContexts` which are created in more
situations as time goes on.

Testing: There is a new WPT test with this change.
Fixes: #36696.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2025-05-02 14:20:11 +02:00 committed by GitHub
parent e25e63b587
commit 9bc16482a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 203 additions and 323 deletions

View file

@ -49,7 +49,6 @@ use crate::{
struct FlexContext<'a> {
config: FlexContainerConfig,
layout_context: &'a LayoutContext<'a>,
positioning_context: &'a mut PositioningContext,
containing_block: &'a ContainingBlock<'a>, // For items
container_inner_size_constraint: FlexRelativeVec2<SizeConstraint>,
}
@ -657,7 +656,6 @@ impl FlexContainer {
let mut flex_context = FlexContext {
config: self.config.clone(),
layout_context,
positioning_context,
containing_block,
// https://drafts.csswg.org/css-flexbox/#definite-sizes
container_inner_size_constraint: self.config.flex_axis.vec2_to_flex_relative(
@ -1775,16 +1773,7 @@ impl FlexItem<'_> {
) -> Option<FlexItemLayoutResult> {
let containing_block = flex_context.containing_block;
let independent_formatting_context = &self.box_.independent_formatting_context;
let mut positioning_context = independent_formatting_context
.new_positioning_context()
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
flex_context
.positioning_context
.collects_for_nearest_positioned_ancestor(),
)
});
let mut positioning_context = PositioningContext::default();
let item_writing_mode = independent_formatting_context.style().writing_mode;
let item_is_horizontal = item_writing_mode.is_horizontal();
let flex_axis = flex_context.config.flex_axis;
@ -2617,17 +2606,7 @@ impl FlexItemBox {
cross_size_stretches_to_container_size: bool,
intrinsic_sizing_mode: IntrinsicSizingMode,
) -> Au {
let mut positioning_context = self
.independent_formatting_context
.new_positioning_context()
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
flex_context
.positioning_context
.collects_for_nearest_positioned_ancestor(),
)
});
let mut positioning_context = PositioningContext::default();
let style = self.independent_formatting_context.style();
match &self.independent_formatting_context.contents {
IndependentFormattingContextContents::Replaced(replaced) => {

View file

@ -331,7 +331,7 @@ impl LineItemLayout<'_, '_> {
self.calculate_inline_box_block_start(inline_box_state, space_above_baseline);
let positioning_context_or_start_offset_in_parent =
match inline_box.base.new_positioning_context() {
match PositioningContext::new_for_layout_box_base(&inline_box.base) {
Some(positioning_context) => Either::Left(positioning_context),
None => Either::Right(self.current_positioning_context_mut().len()),
};

View file

@ -2004,9 +2004,7 @@ impl IndependentFormattingContext {
bidi_level: Level,
) {
// We need to know the inline size of the atomic before deciding whether to do the line break.
let mut child_positioning_context = self
.new_positioning_context()
.unwrap_or_else(|| PositioningContext::new_for_subtree(true));
let mut child_positioning_context = PositioningContext::default();
let IndependentFloatOrAtomicLayoutResult {
mut fragment,
baselines,

View file

@ -689,16 +689,13 @@ fn layout_block_level_children_in_parallel(
placement_state: &mut PlacementState,
ignore_block_margins_for_stretch: LogicalSides1D<bool>,
) -> Vec<Fragment> {
let collects_for_nearest_positioned_ancestor =
positioning_context.collects_for_nearest_positioned_ancestor();
let mut layout_results: Vec<(Fragment, PositioningContext)> =
Vec::with_capacity(child_boxes.len());
child_boxes
.par_iter()
.map(|child_box| {
let mut child_positioning_context =
PositioningContext::new_for_subtree(collects_for_nearest_positioned_ancestor);
let mut child_positioning_context = PositioningContext::default();
let fragment = child_box.borrow().layout(
layout_context,
&mut child_positioning_context,

View file

@ -385,8 +385,7 @@ impl BoxTree {
style,
};
let mut positioning_context =
PositioningContext::new_for_containing_block_for_all_descendants();
let mut positioning_context = PositioningContext::default();
let independent_layout = self.root.layout(
layout_context,
&mut positioning_context,

View file

@ -295,10 +295,7 @@ impl IndependentNonReplacedContents {
);
}
let mut child_positioning_context = PositioningContext::new_for_subtree(
positioning_context.collects_for_nearest_positioned_ancestor(),
);
let mut child_positioning_context = PositioningContext::default();
let result = self.layout_without_caching(
layout_context,
&mut child_positioning_context,

View file

@ -42,16 +42,6 @@ pub(crate) struct AbsolutelyPositionedBox {
pub context: IndependentFormattingContext,
}
#[derive(Clone, MallocSizeOf)]
pub(crate) struct PositioningContext {
for_nearest_positioned_ancestor: Option<Vec<HoistedAbsolutelyPositionedBox>>,
// For nearest `containing block for all descendants` as defined by the CSS transforms
// spec.
// https://www.w3.org/TR/css-transforms-1/#containing-block-for-all-descendants
for_nearest_containing_block_for_all_descendants: Vec<HoistedAbsolutelyPositionedBox>,
}
#[derive(Clone, MallocSizeOf)]
pub(crate) struct HoistedAbsolutelyPositionedBox {
absolutely_positioned_box: ArcRefCell<AbsolutelyPositionedBox>,
@ -104,55 +94,26 @@ impl AbsolutelyPositionedBox {
}
}
impl IndependentFormattingContext {
#[inline]
pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
self.base.new_positioning_context()
}
}
impl LayoutBoxBase {
#[inline]
pub(crate) fn new_positioning_context(&self) -> Option<PositioningContext> {
PositioningContext::new_for_style(&self.style, &self.base_fragment_info.flags)
}
#[derive(Clone, Default, MallocSizeOf)]
pub(crate) struct PositioningContext {
absolutes: Vec<HoistedAbsolutelyPositionedBox>,
}
impl PositioningContext {
pub(crate) fn new_for_containing_block_for_all_descendants() -> Self {
Self {
for_nearest_positioned_ancestor: None,
for_nearest_containing_block_for_all_descendants: Vec::new(),
}
#[inline]
pub(crate) fn new_for_layout_box_base(layout_box_base: &LayoutBoxBase) -> Option<Self> {
Self::new_for_style_and_fragment_flags(
&layout_box_base.style,
&layout_box_base.base_fragment_info.flags,
)
}
/// Create a [PositioningContext] to use for laying out a subtree. The idea is that
/// when subtree layout is finished, the newly hoisted boxes can be processed
/// (normally adjusting their static insets) and then appended to the parent
/// [PositioningContext].
pub(crate) fn new_for_subtree(collects_for_nearest_positioned_ancestor: bool) -> Self {
Self {
for_nearest_positioned_ancestor: if collects_for_nearest_positioned_ancestor {
Some(Vec::new())
} else {
None
},
for_nearest_containing_block_for_all_descendants: Vec::new(),
}
}
pub(crate) fn collects_for_nearest_positioned_ancestor(&self) -> bool {
self.for_nearest_positioned_ancestor.is_some()
}
fn new_for_style(style: &ComputedValues, flags: &FragmentFlags) -> Option<Self> {
if style.establishes_containing_block_for_all_descendants(*flags) {
Some(Self::new_for_containing_block_for_all_descendants())
} else if style.establishes_containing_block_for_absolute_descendants(*flags) {
Some(Self {
for_nearest_positioned_ancestor: Some(Vec::new()),
for_nearest_containing_block_for_all_descendants: Vec::new(),
})
fn new_for_style_and_fragment_flags(
style: &ComputedValues,
flags: &FragmentFlags,
) -> Option<Self> {
if style.establishes_containing_block_for_absolute_descendants(*flags) {
Some(Self::default())
} else {
None
}
@ -195,20 +156,9 @@ impl PositioningContext {
offset: &PhysicalVec<Au>,
index: PositioningContextLength,
) {
if let Some(hoisted_boxes) = self.for_nearest_positioned_ancestor.as_mut() {
hoisted_boxes
.iter_mut()
.skip(index.for_nearest_positioned_ancestor)
.for_each(|hoisted_fragment| {
hoisted_fragment
.fragment
.borrow_mut()
.adjust_offsets(offset)
})
}
self.for_nearest_containing_block_for_all_descendants
self.absolutes
.iter_mut()
.skip(index.for_nearest_containing_block_for_all_descendants)
.skip(index.0)
.for_each(|hoisted_fragment| {
hoisted_fragment
.fragment
@ -227,19 +177,23 @@ impl PositioningContext {
base: &LayoutBoxBase,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
// Try to create a context, but if one isn't necessary, simply create the fragment
// using the given closure and the current `PositioningContext`.
let mut new_context = match base.new_positioning_context() {
Some(new_context) => new_context,
None => return fragment_layout_fn(self),
};
// If a new `PositioningContext` isn't necessary, simply create the fragment using
// the given closure and the current `PositioningContext`.
let establishes_containing_block_for_absolutes = base
.style
.establishes_containing_block_for_absolute_descendants(base.base_fragment_info.flags);
if !establishes_containing_block_for_absolutes {
return fragment_layout_fn(self);
}
let mut new_context = PositioningContext::default();
let mut new_fragment = fragment_layout_fn(&mut new_context);
new_context.layout_collected_children(layout_context, &mut new_fragment);
// If the new context has any hoisted boxes for the nearest containing block for
// pass them up the tree.
// Lay out all of the absolutely positioned children for this fragment, and, if it
// isn't a containing block for fixed elements, then pass those up to the parent.
new_context.layout_collected_children(layout_context, &mut new_fragment);
self.append(new_context);
if base.style.clone_position() == Position::Relative {
new_fragment.content_rect.origin += relative_adjustement(&base.style, containing_block)
.to_physical_vector(containing_block.style.writing_mode)
@ -248,13 +202,61 @@ impl PositioningContext {
new_fragment
}
fn take_boxes_for_fragment(
&mut self,
new_fragment: &BoxFragment,
boxes_to_layout_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
boxes_to_continue_hoisting_out: &mut Vec<HoistedAbsolutelyPositionedBox>,
) {
debug_assert!(
new_fragment
.style
.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
);
if new_fragment
.style
.establishes_containing_block_for_all_descendants(new_fragment.base.flags)
{
boxes_to_layout_out.append(&mut self.absolutes);
return;
}
// TODO: This could potentially use `extract_if` when that is stabilized.
let (mut boxes_to_layout, mut boxes_to_continue_hoisting) = self
.absolutes
.drain(..)
.partition(|hoisted_box| hoisted_box.position() != Position::Fixed);
boxes_to_layout_out.append(&mut boxes_to_layout);
boxes_to_continue_hoisting_out.append(&mut boxes_to_continue_hoisting);
}
// Lay out the hoisted boxes collected into this `PositioningContext` and add them
// to the given `BoxFragment`.
pub fn layout_collected_children(
pub(crate) fn layout_collected_children(
&mut self,
layout_context: &LayoutContext,
new_fragment: &mut BoxFragment,
) {
if self.absolutes.is_empty() {
return;
}
// Sometimes we create temporary PositioningContexts just to collect hoisted absolutes and
// then these are processed later. In that case and if this fragment doesn't establish a
// containing block for absolutes at all, we just do nothing. All hoisted fragments will
// later be passed up to a parent PositioningContext.
//
// Handling this case here, when the PositioningContext is completely ineffectual other than
// as a temporary container for hoisted boxes, means that callers can execute less conditional
// code.
if !new_fragment
.style
.establishes_containing_block_for_absolute_descendants(new_fragment.base.flags)
{
return;
}
let padding_rect = PhysicalRect::new(
// Ignore the content rects position in its own containing block:
PhysicalPoint::origin(),
@ -268,83 +270,58 @@ impl PositioningContext {
style: &new_fragment.style,
};
let take_hoisted_boxes_pending_layout =
|context: &mut Self| match context.for_nearest_positioned_ancestor.as_mut() {
Some(fragments) => mem::take(fragments),
None => mem::take(&mut context.for_nearest_containing_block_for_all_descendants),
};
let mut fixed_position_boxes_to_hoist = Vec::new();
let mut boxes_to_layout = Vec::new();
self.take_boxes_for_fragment(
new_fragment,
&mut boxes_to_layout,
&mut fixed_position_boxes_to_hoist,
);
// Loop because its possible that we discover (the static position of)
// more absolutely-positioned boxes while doing layout for others.
let mut hoisted_boxes = take_hoisted_boxes_pending_layout(self);
let mut laid_out_child_fragments = Vec::new();
while !hoisted_boxes.is_empty() {
// Laying out a `position: absolute` child (which only establishes a containing block for
// `position: absolute` descendants) can result in more `position: fixed` descendants
// collecting in `self.absolutes`. We need to loop here in order to keep either laying them
// out or putting them into `fixed_position_boxes_to_hoist`. We know there aren't any more
// when `self.absolutes` is empty.
while !boxes_to_layout.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&mut hoisted_boxes,
&mut laid_out_child_fragments,
&mut self.for_nearest_containing_block_for_all_descendants,
std::mem::take(&mut boxes_to_layout),
&mut new_fragment.children,
&mut self.absolutes,
&containing_block,
new_fragment.padding,
);
hoisted_boxes = take_hoisted_boxes_pending_layout(self);
self.take_boxes_for_fragment(
new_fragment,
&mut boxes_to_layout,
&mut fixed_position_boxes_to_hoist,
);
}
new_fragment.children.extend(laid_out_child_fragments);
// We replace here instead of simply preserving these in `take_boxes_for_fragment`
// so that we don't have to continually re-iterate over them when laying out in the
// loop above.
self.absolutes = fixed_position_boxes_to_hoist;
}
pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox) {
if let Some(nearest) = &mut self.for_nearest_positioned_ancestor {
let position = box_
.absolutely_positioned_box
.borrow()
.context
.style()
.clone_position();
match position {
Position::Fixed => {}, // fall through
Position::Absolute => return nearest.push(box_),
Position::Static | Position::Relative | Position::Sticky => unreachable!(),
}
}
self.for_nearest_containing_block_for_all_descendants
.push(box_)
pub(crate) fn push(&mut self, hoisted_box: HoistedAbsolutelyPositionedBox) {
debug_assert!(matches!(
hoisted_box.position(),
Position::Absolute | Position::Fixed
));
self.absolutes.push(hoisted_box);
}
pub(crate) fn is_empty(&self) -> bool {
self.for_nearest_containing_block_for_all_descendants
.is_empty() &&
self.for_nearest_positioned_ancestor
.as_ref()
.is_none_or(|vector| vector.is_empty())
}
pub(crate) fn append(&mut self, other: Self) {
if other.is_empty() {
pub(crate) fn append(&mut self, mut other: Self) {
if other.absolutes.is_empty() {
return;
}
vec_append_owned(
&mut self.for_nearest_containing_block_for_all_descendants,
other.for_nearest_containing_block_for_all_descendants,
);
match (
self.for_nearest_positioned_ancestor.as_mut(),
other.for_nearest_positioned_ancestor,
) {
(Some(us), Some(them)) => vec_append_owned(us, them),
(None, Some(them)) => {
// This is the case where we have laid out the absolute children in a containing
// block for absolutes and we then are passing up the fixed-position descendants
// to the containing block for all descendants.
vec_append_owned(
&mut self.for_nearest_containing_block_for_all_descendants,
them,
);
},
(None, None) => {},
_ => unreachable!(),
if self.absolutes.is_empty() {
self.absolutes = other.absolutes;
} else {
self.absolutes.append(&mut other.absolutes)
}
}
@ -354,19 +331,16 @@ impl PositioningContext {
initial_containing_block: &DefiniteContainingBlock,
fragments: &mut Vec<Fragment>,
) {
debug_assert!(self.for_nearest_positioned_ancestor.is_none());
// Loop because its possible that we discover (the static position of)
// more absolutely-positioned boxes while doing layout for others.
while !self
.for_nearest_containing_block_for_all_descendants
.is_empty()
{
// Laying out a `position: absolute` child (which only establishes a containing block for
// `position: absolute` descendants) can result in more `position: fixed` descendants
// collecting in `self.absolutes`. We need to loop here in order to keep laying them out. We
// know there aren't any more when `self.absolutes` is empty.
while !self.absolutes.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&mut mem::take(&mut self.for_nearest_containing_block_for_all_descendants),
mem::take(&mut self.absolutes),
fragments,
&mut self.for_nearest_containing_block_for_all_descendants,
&mut self.absolutes,
initial_containing_block,
Default::default(),
)
@ -375,58 +349,46 @@ impl PositioningContext {
/// Get the length of this [PositioningContext].
pub(crate) fn len(&self) -> PositioningContextLength {
PositioningContextLength {
for_nearest_positioned_ancestor: self
.for_nearest_positioned_ancestor
.as_ref()
.map_or(0, |vec| vec.len()),
for_nearest_containing_block_for_all_descendants: self
.for_nearest_containing_block_for_all_descendants
.len(),
}
PositioningContextLength(self.absolutes.len())
}
/// Truncate this [PositioningContext] to the given [PositioningContextLength]. This
/// is useful for "unhoisting" boxes in this context and returning it to the state at
/// the time that [`PositioningContext::len()`] was called.
pub(crate) fn truncate(&mut self, length: &PositioningContextLength) {
if let Some(vec) = self.for_nearest_positioned_ancestor.as_mut() {
vec.truncate(length.for_nearest_positioned_ancestor);
}
self.for_nearest_containing_block_for_all_descendants
.truncate(length.for_nearest_containing_block_for_all_descendants);
self.absolutes.truncate(length.0)
}
}
/// A data structure which stores the size of a positioning context.
#[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) struct PositioningContextLength {
/// The number of boxes that will be hoisted the the nearest positioned ancestor for
/// layout.
for_nearest_positioned_ancestor: usize,
/// The number of boxes that will be hoisted the the nearest ancestor which
/// establishes a containing block for all descendants for layout.
for_nearest_containing_block_for_all_descendants: usize,
}
pub(crate) struct PositioningContextLength(usize);
impl Zero for PositioningContextLength {
fn zero() -> Self {
PositioningContextLength {
for_nearest_positioned_ancestor: 0,
for_nearest_containing_block_for_all_descendants: 0,
}
Self(0)
}
fn is_zero(&self) -> bool {
self.for_nearest_positioned_ancestor == 0 &&
self.for_nearest_containing_block_for_all_descendants == 0
self.0.is_zero()
}
}
impl HoistedAbsolutelyPositionedBox {
fn position(&self) -> Position {
let position = self
.absolutely_positioned_box
.borrow()
.context
.style()
.clone_position();
assert!(position == Position::Fixed || position == Position::Absolute);
position
}
pub(crate) fn layout_many(
layout_context: &LayoutContext,
boxes: &mut [Self],
mut boxes: Vec<Self>,
fragments: &mut Vec<Fragment>,
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
containing_block: &DefiniteContainingBlock,
@ -473,7 +435,7 @@ impl HoistedAbsolutelyPositionedBox {
pub(crate) fn layout(
&mut self,
layout_context: &LayoutContext,
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
hoisted_absolutes_from_children: &mut Vec<HoistedAbsolutelyPositionedBox>,
containing_block: &DefiniteContainingBlock,
containing_block_padding: PhysicalSides<Au>,
) -> Fragment {
@ -596,7 +558,7 @@ impl HoistedAbsolutelyPositionedBox {
.sizes
}));
let mut positioning_context = context.new_positioning_context().unwrap();
let mut positioning_context = PositioningContext::default();
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
let fragments;
@ -709,6 +671,10 @@ impl HoistedAbsolutelyPositionedBox {
)
.with_specific_layout_info(specific_layout_info)
};
// This is an absolutely positioned element, which means it also establishes a
// containing block for absolutes. We lay out any absolutely positioned children
// here and pass the rest to `hoisted_absolutes_from_children.`
positioning_context.layout_collected_children(layout_context, &mut new_fragment);
// Any hoisted boxes that remain in this positioning context are going to be hoisted
@ -721,8 +687,7 @@ impl HoistedAbsolutelyPositionedBox {
PositioningContextLength::zero(),
);
for_nearest_containing_block_for_all_descendants
.extend(positioning_context.for_nearest_containing_block_for_all_descendants);
hoisted_absolutes_from_children.extend(positioning_context.absolutes);
let fragment = Fragment::Box(ArcRefCell::new(new_fragment));
context.base.set_fragment(fragment.clone());
@ -1024,14 +989,6 @@ impl AbsoluteAxisSolver<'_> {
}
}
fn vec_append_owned<T>(a: &mut Vec<T>, mut b: Vec<T>) {
if a.is_empty() {
*a = b
} else {
a.append(&mut b)
}
}
/// <https://drafts.csswg.org/css2/visuren.html#relative-positioning>
pub(crate) fn relative_adjustement(
style: &ComputedValues,

View file

@ -1068,7 +1068,6 @@ impl<'a> TableLayout<'a> {
&mut self,
layout_context: &LayoutContext,
containing_block_for_table: &ContainingBlock,
parent_positioning_context: &mut PositioningContext,
) {
self.cells_laid_out = self
.table
@ -1076,30 +1075,6 @@ impl<'a> TableLayout<'a> {
.par_iter()
.enumerate()
.map(|(row_index, row_slots)| {
// When building the PositioningContext for this cell, we want it to have the same
// configuration for whatever PositioningContext the contents are ultimately added to.
let collect_for_nearest_positioned_ancestor = parent_positioning_context
.collects_for_nearest_positioned_ancestor() ||
self.table.rows.get(row_index).is_some_and(|row| {
let row = row.borrow();
let row_group_collects_for_nearest_positioned_ancestor =
row.group_index.is_some_and(|group_index| {
self.table.row_groups[group_index]
.borrow()
.base
.style
.establishes_containing_block_for_absolute_descendants(
FragmentFlags::empty(),
)
});
row_group_collects_for_nearest_positioned_ancestor ||
row.base
.style
.establishes_containing_block_for_absolute_descendants(
FragmentFlags::empty(),
)
});
row_slots
.par_iter()
.enumerate()
@ -1141,10 +1116,7 @@ impl<'a> TableLayout<'a> {
style: &cell.base.style,
};
let mut positioning_context = PositioningContext::new_for_subtree(
collect_for_nearest_positioned_ancestor,
);
let mut positioning_context = PositioningContext::default();
let layout = cell.contents.layout(
layout_context,
&mut positioning_context,
@ -1503,7 +1475,6 @@ impl<'a> TableLayout<'a> {
layout_context: &LayoutContext,
parent_positioning_context: &mut PositioningContext,
) -> BoxFragment {
let mut positioning_context = caption.context.new_positioning_context();
let containing_block = &ContainingBlock {
size: ContainingBlockSize {
inline: self.table_width + self.pbm.padding_border_sums.inline,
@ -1517,6 +1488,8 @@ impl<'a> TableLayout<'a> {
// stretch block size. https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing
let ignore_block_margins_for_stretch = LogicalSides1D::new(false, false);
let mut positioning_context =
PositioningContext::new_for_layout_box_base(&caption.context.base);
let mut box_fragment = caption.context.layout_in_flow_block_level(
layout_context,
positioning_context
@ -1769,11 +1742,7 @@ impl<'a> TableLayout<'a> {
) -> BoxFragment {
self.distributed_column_widths =
Self::distribute_width_to_columns(self.assignable_width, &self.columns);
self.layout_cells_in_row(
layout_context,
containing_block_for_children,
positioning_context,
);
self.layout_cells_in_row(layout_context, containing_block_for_children);
let table_writing_mode = containing_block_for_children.style.writing_mode;
let first_layout_row_heights = self.do_first_row_layout(table_writing_mode);
self.compute_table_height_and_final_row_heights(
@ -2325,7 +2294,7 @@ impl<'a> RowFragmentLayout<'a> {
Self {
row: table_row,
rect,
positioning_context: table_row.base.new_positioning_context(),
positioning_context: PositioningContext::new_for_layout_box_base(&table_row.base),
containing_block,
fragments: Vec::new(),
}
@ -2379,11 +2348,11 @@ impl<'a> RowFragmentLayout<'a> {
if let Some(mut row_positioning_context) = self.positioning_context.take() {
row_positioning_context.layout_collected_children(layout_context, &mut row_fragment);
let positioning_context = row_group_fragment_layout
let parent_positioning_context = row_group_fragment_layout
.as_mut()
.and_then(|layout| layout.positioning_context.as_mut())
.unwrap_or(table_positioning_context);
positioning_context.append(row_positioning_context);
parent_positioning_context.append(row_positioning_context);
}
let fragment = Fragment::Box(ArcRefCell::new(row_fragment));
@ -2410,7 +2379,7 @@ impl RowGroupFragmentLayout {
let row_group = row_group.borrow();
(
dimensions.get_row_group_rect(&row_group),
row_group.base.new_positioning_context(),
PositioningContext::new_for_layout_box_base(&row_group.base),
)
};
Self {

View file

@ -29,7 +29,7 @@ use crate::geom::{
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, LayoutStyle};
use crate::style_ext::LayoutStyle;
use crate::{ConstraintSpace, ContainingBlock, ContainingBlockSize};
const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
@ -250,29 +250,15 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
},
style,
};
let layout = {
let mut child_positioning_context = independent_context
.new_positioning_context()
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
self.positioning_context
.collects_for_nearest_positioned_ancestor(),
)
});
let layout = non_replaced.layout_without_caching(
self.layout_context,
&mut child_positioning_context,
&content_box_size_override,
containing_block,
false, /* depends_on_block_constraints */
);
// Store layout data on child for later access
child.positioning_context = child_positioning_context;
layout
};
child.positioning_context = PositioningContext::default();
let layout = non_replaced.layout_without_caching(
self.layout_context,
&mut child.positioning_context,
&content_box_size_override,
containing_block,
false, /* depends_on_block_constraints */
);
child.child_fragments = layout.fragments;
self.child_specific_layout_infos[usize::from(node_id)] =
@ -373,8 +359,7 @@ impl ComputeInlineContentSizes for TaffyContainer {
let mut grid_context = TaffyContainerContext {
layout_context,
positioning_context:
&mut PositioningContext::new_for_containing_block_for_all_descendants(),
positioning_context: &mut PositioningContext::default(),
content_box_size_override: containing_block,
style,
source_child_nodes: &self.children,
@ -540,17 +525,6 @@ impl TaffyContainer {
let child_specific_layout_info: Option<SpecificLayoutInfo> =
std::mem::take(&mut container_ctx.child_specific_layout_infos[child_id]);
let establishes_containing_block_for_absolute_descendants =
if let TaffyItemBoxInner::InFlowBox(independent_box) = &child.taffy_level_box {
child
.style
.establishes_containing_block_for_absolute_descendants(
independent_box.base_fragment_info().flags,
)
} else {
false
};
let fragment = match &mut child.taffy_level_box {
TaffyItemBoxInner::InFlowBox(independent_box) => {
let mut fragment_info = independent_box.base_fragment_info();
@ -573,29 +547,21 @@ impl TaffyContainer {
})
.with_specific_layout_info(child_specific_layout_info);
if establishes_containing_block_for_absolute_descendants {
child.positioning_context.layout_collected_children(
container_ctx.layout_context,
&mut box_fragment,
);
}
let fragment = Fragment::Box(ArcRefCell::new(box_fragment));
child.positioning_context.layout_collected_children(
container_ctx.layout_context,
&mut box_fragment,
);
child
.positioning_context
.adjust_static_position_of_hoisted_fragments(
&fragment,
.adjust_static_position_of_hoisted_fragments_with_offset(
&box_fragment.content_rect.origin.to_vector(),
PositioningContextLength::zero(),
);
let child_positioning_context = std::mem::replace(
&mut child.positioning_context,
PositioningContext::new_for_containing_block_for_all_descendants(),
);
container_ctx
.positioning_context
.append(child_positioning_context);
fragment
.append(std::mem::take(&mut child.positioning_context));
Fragment::Box(ArcRefCell::new(box_fragment))
},
TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => {
fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags {

View file

@ -110,7 +110,7 @@ impl TaffyItemBox {
Self {
taffy_layout: Default::default(),
child_fragments: Vec::new(),
positioning_context: PositioningContext::new_for_containing_block_for_all_descendants(),
positioning_context: PositioningContext::default(),
style,
taffy_level_box: inner,
}
@ -118,8 +118,7 @@ impl TaffyItemBox {
pub(crate) fn invalidate_cached_fragment(&mut self) {
self.taffy_layout = Default::default();
self.positioning_context =
PositioningContext::new_for_containing_block_for_all_descendants();
self.positioning_context = PositioningContext::default();
match self.taffy_level_box {
TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => {
independent_formatting_context

View file

@ -2794,6 +2794,13 @@
},
"css-grid": {
"abspos": {
"abspos-in-flexbox-in-grid-crash.html": [
"a2f71fb78e7f6fd82ea539e3af6c76d36f21e134",
[
null,
{}
]
],
"positioned-grid-items-crash.html": [
"228a6264df77fe82587a4116f231ff09537c8372",
[

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/servo/servo/issues/36696">
<div style="display: flex; position: relative">
<div style="display:grid">
<div>
<div style="display: flex">
<div style="position: absolute"></div>
</div>
</div>
</div>
</div>