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

@ -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